blob: d18c4906e8082bf14189d05936b0aef9a9ac4f18 [file] [log] [blame]
Scott Maine4d8f1b2012-06-21 18:03:05 -07001var classesNav;
2var devdocNav;
3var sidenav;
4var cookie_namespace = 'android_developer';
5var NAV_PREF_TREE = "tree";
6var NAV_PREF_PANELS = "panels";
7var nav_pref;
Scott Maine4d8f1b2012-06-21 18:03:05 -07008var isMobile = false; // true if mobile, so we can adjust some layout
9
Scott Main1b3db112012-07-03 14:06:22 -070010var basePath = getBaseUri(location.pathname);
11var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
12
Scott Maine4d8f1b2012-06-21 18:03:05 -070013
14/****** ON LOAD SET UP STUFF *********/
15
16var navBarIsFixed = false;
17$(document).ready(function() {
18 // init the fullscreen toggle click event
19 $('#nav-swap .fullscreen').click(function(){
20 if ($(this).hasClass('disabled')) {
21 toggleFullscreen(true);
22 } else {
23 toggleFullscreen(false);
24 }
25 });
26
27 // initialize the divs with custom scrollbars
28 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
29
30 // add HRs below all H2s (except for a few other h2 variants)
Scott Main24bbcd52012-09-21 14:33:43 -070031 $('h2').not('#qv h2').not('#tb h2').not('.sidebox h2').not('#devdoc-nav h2').css({marginBottom:0}).after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070032
33 // set search's onkeyup handler here so we can show suggestions
34 // even while search results are visible
Scott Main1b3db112012-07-03 14:06:22 -070035 $("#search_autocomplete").keyup(function() {return search_changed(event, false, toRoot)});
Scott Maine4d8f1b2012-06-21 18:03:05 -070036
37 // set up the search close button
38 $('.search .close').click(function() {
39 $searchInput = $('#search_autocomplete');
40 $searchInput.attr('value', '');
41 $(this).addClass("hide");
42 $("#search-container").removeClass('active');
43 $("#search_autocomplete").blur();
44 search_focus_changed($searchInput.get(), false); // see search_autocomplete.js
45 hideResults(); // see search_autocomplete.js
46 });
47 $('.search').click(function() {
48 if (!$('#search_autocomplete').is(":focused")) {
49 $('#search_autocomplete').focus();
50 }
51 });
52
53 // Set up quicknav
54 var quicknav_open = false;
55 $("#btn-quicknav").click(function() {
56 if (quicknav_open) {
57 $(this).removeClass('active');
58 quicknav_open = false;
59 collapse();
60 } else {
61 $(this).addClass('active');
62 quicknav_open = true;
63 expand();
64 }
65 })
66
67 var expand = function() {
68 $('#header-wrap').addClass('quicknav');
69 $('#quicknav').stop().show().animate({opacity:'1'});
70 }
71
72 var collapse = function() {
73 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
74 $(this).hide();
75 $('#header-wrap').removeClass('quicknav');
76 });
77 }
78
79
80 //Set up search
81 $("#search_autocomplete").focus(function() {
82 $("#search-container").addClass('active');
83 })
84 $("#search-container").mouseover(function() {
85 $("#search-container").addClass('active');
86 $("#search_autocomplete").focus();
87 })
88 $("#search-container").mouseout(function() {
89 if ($("#search_autocomplete").is(":focus")) return;
90 if ($("#search_autocomplete").val() == '') {
91 setTimeout(function(){
92 $("#search-container").removeClass('active');
93 $("#search_autocomplete").blur();
94 },250);
95 }
96 })
97 $("#search_autocomplete").blur(function() {
98 if ($("#search_autocomplete").val() == '') {
99 $("#search-container").removeClass('active');
100 }
101 })
102
103
104 // prep nav expandos
105 var pagePath = document.location.pathname;
106 // account for intl docs by removing the intl/*/ path
107 if (pagePath.indexOf("/intl/") == 0) {
108 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
109 }
110
111 if (pagePath.indexOf(SITE_ROOT) == 0) {
112 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
113 pagePath += 'index.html';
114 }
115 }
116
117 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
118 // If running locally, SITE_ROOT will be a relative path, so account for that by
119 // finding the relative URL to this page. This will allow us to find links on the page
120 // leading back to this page.
121 var pathParts = pagePath.split('/');
122 var relativePagePathParts = [];
123 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
124 for (var i = 0; i < upDirs; i++) {
125 relativePagePathParts.push('..');
126 }
127 for (var i = 0; i < upDirs; i++) {
128 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
129 }
130 relativePagePathParts.push(pathParts[pathParts.length - 1]);
131 pagePath = relativePagePathParts.join('/');
132 } else {
133 // Otherwise the page path is already an absolute URL
134 }
135
136 // select current page in sidenav and set up prev/next links if they exist
137 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
138 if ($selNavLink.length) {
139 $selListItem = $selNavLink.closest('li');
140
141 $selListItem.addClass('selected');
142 $selListItem.closest('li.nav-section').addClass('expanded');
143 $selListItem.closest('li.nav-section').children('ul').show();
144 $selListItem.closest('li.nav-section').parent().closest('li.nav-section').addClass('expanded');
145 $selListItem.closest('li.nav-section').parent().closest('ul').show();
146
147
148 // $selListItem.closest('li.nav-section').closest('li.nav-section').addClass('expanded');
149 // $selListItem.closest('li.nav-section').closest('li.nav-section').children('ul').show();
150
151 // set up prev links
152 var $prevLink = [];
153 var $prevListItem = $selListItem.prev('li');
154
155 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
156false; // navigate across topic boundaries only in design docs
157 if ($prevListItem.length) {
158 if ($prevListItem.hasClass('nav-section')) {
159 if (crossBoundaries) {
160 // jump to last topic of previous section
161 $prevLink = $prevListItem.find('a:last');
162 }
163 } else {
164 // jump to previous topic in this section
165 $prevLink = $prevListItem.find('a:eq(0)');
166 }
167 } else {
168 // jump to this section's index page (if it exists)
169 var $parentListItem = $selListItem.parents('li');
170 $prevLink = $selListItem.parents('li').find('a');
171
172 // except if cross boundaries aren't allowed, and we're at the top of a section already
173 // (and there's another parent)
174 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
175 && $selListItem.hasClass('nav-section')) {
176 $prevLink = [];
177 }
178 }
179
180 if ($prevLink.length) {
181 var prevHref = $prevLink.attr('href');
182 if (prevHref == SITE_ROOT + 'index.html') {
183 // Don't show Previous when it leads to the homepage
184 } else {
185 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
186 }
187 }
188
189 // set up next links
190 var $nextLink = [];
191 var startCourse = false;
192 var startClass = false;
193 var training = $(".next-class-link").length; // decides whether to provide "next class" link
194 var isCrossingBoundary = false;
195
196 if ($selListItem.hasClass('nav-section')) {
197 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700198 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700199
200 // if there aren't any children, go to the next section (required for About pages)
201 if($nextLink.length == 0) {
202 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700203 } else if ($('.topic-start-link').length) {
204 // as long as there's a child link and there is a "topic start link" (we're on a landing)
205 // then set the landing page "start link" text to be the first doc title
206 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700207 }
208
209 // Handle some Training specialties
210 if ($selListItem.parent().is("#nav") && $(".start-course-link").length) {
211 // this means we're at the very top of the TOC hierarchy
212 startCourse = true;
213 } else if ($(".start-class-link").length) {
214 // this means this page has children but is not at the top (it's a class, not a course)
215 startClass = true;
216 }
217 } else {
218 // jump to the next topic in this section (if it exists)
219 $nextLink = $selListItem.next('li').find('a:eq(0)');
220 if (!$nextLink.length) {
221 if (crossBoundaries || training) {
222 // no more topics in this section, jump to the first topic in the next section
223 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
224 isCrossingBoundary = true;
225 }
226 }
227 }
228 if ($nextLink.length) {
229 if (startCourse || startClass) {
230 if (startCourse) {
231 $('.start-course-link').attr('href', $nextLink.attr('href')).removeClass("hide");
232 } else {
233 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
234 }
235 // if there's no training bar (below the start button),
236 // then we need to add a bottom border to button
237 if (!$("#tb").length) {
238 $('.start-course-link').css({'border-bottom':'1px solid #DADADA'});
239 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
240 }
241 } else if (training && isCrossingBoundary) {
242 $('.content-footer.next-class').show();
243 $('.next-page-link').attr('href','')
244 .removeClass("hide").addClass("disabled")
245 .click(function() { return false; });
246
247 $('.next-class-link').attr('href',$nextLink.attr('href'))
248 .removeClass("hide").append($nextLink.html());
249 $('.next-class-link').find('.new').empty();
250 } else {
251 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
252 }
253 }
254
255 }
256
257
258
259 // Set up expand/collapse behavior
260 $('#nav li.nav-section .nav-section-header').click(function() {
261 var section = $(this).closest('li.nav-section');
262 if (section.hasClass('expanded')) {
263 /* hide me */
264 // if (section.hasClass('selected') || section.find('li').hasClass('selected')) {
265 // /* but not if myself or my descendents are selected */
266 // return;
267 // }
268 section.children('ul').slideUp(250, function() {
269 section.closest('li').removeClass('expanded');
270 resizeNav();
271 });
272 } else {
273 /* show me */
274 // first hide all other siblings
275 var $others = $('li.nav-section.expanded', $(this).closest('ul'));
276 $others.removeClass('expanded').children('ul').slideUp(250);
277
278 // now expand me
279 section.closest('li').addClass('expanded');
280 section.children('ul').slideDown(250, function() {
281 resizeNav();
282 });
283 }
284 });
285
286 $(".scroll-pane").scroll(function(event) {
287 event.preventDefault();
288 return false;
289 });
290
291 /* Resize nav height when window height changes */
292 $(window).resize(function() {
293 if ($('#side-nav').length == 0) return;
294 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
295 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
296 // make sidenav behave when resizing the window and side-scolling is a concern
297 if (navBarIsFixed) {
298 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
299 updateSideNavPosition();
300 } else {
301 updateSidenavFullscreenWidth();
302 }
303 }
304 resizeNav();
305 });
306
307
308 // Set up fixed navbar
309 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
310 $(window).scroll(function(event) {
311 if ($('#side-nav').length == 0) return;
312 if (event.target.nodeName == "DIV") {
313 // Dump scroll event if the target is a DIV, because that means the event is coming
314 // from a scrollable div and so there's no need to make adjustments to our layout
315 return;
316 }
317 var scrollTop = $(window).scrollTop();
318 var headerHeight = $('#header').outerHeight();
319 var subheaderHeight = $('#nav-x').outerHeight();
320 var searchResultHeight = $('#searchResults').is(":visible") ?
321 $('#searchResults').outerHeight() : 0;
322 var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight;
323 var navBarShouldBeFixed = scrollTop > totalHeaderHeight;
324
325 var scrollLeft = $(window).scrollLeft();
326 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
327 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
328 updateSideNavPosition();
329 prevScrollLeft = scrollLeft;
330 }
331
332 // Don't continue if the header is sufficently far away
333 // (to avoid intensive resizing that slows scrolling)
334 if (navBarIsFixed && navBarShouldBeFixed) {
335 return;
336 }
337
338 if (navBarIsFixed != navBarShouldBeFixed) {
339 if (navBarShouldBeFixed) {
340 // make it fixed
341 var width = $('#devdoc-nav').width();
342 $('#devdoc-nav')
343 .addClass('fixed')
344 .css({'width':width+'px'})
345 .prependTo('#body-content');
346 // add neato "back to top" button
347 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
348
349 // update the sidenaav position for side scrolling
350 updateSideNavPosition();
351 } else {
352 // make it static again
353 $('#devdoc-nav')
354 .removeClass('fixed')
355 .css({'width':'auto','margin':''})
356 .prependTo('#side-nav');
357 $('#devdoc-nav a.totop').hide();
358 }
359 navBarIsFixed = navBarShouldBeFixed;
360 }
361
362 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
363 });
364
365
366 var navBarLeftPos;
367 if ($('#devdoc-nav').length) {
368 setNavBarLeftPos();
369 }
370
371
372 // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away
373 // from the page)
374 $('.nav-section-header').find('a:eq(0)').click(function(evt) {
375 window.location.href = $(this).attr('href');
376 return false;
377 });
378
379 // Set up play-on-hover <video> tags.
380 $('video.play-on-hover').bind('click', function(){
381 $(this).get(0).load(); // in case the video isn't seekable
382 $(this).get(0).play();
383 });
384
385 // Set up tooltips
386 var TOOLTIP_MARGIN = 10;
387 $('acronym').each(function() {
388 var $target = $(this);
389 var $tooltip = $('<div>')
390 .addClass('tooltip-box')
391 .text($target.attr('title'))
392 .hide()
393 .appendTo('body');
394 $target.removeAttr('title');
395
396 $target.hover(function() {
397 // in
398 var targetRect = $target.offset();
399 targetRect.width = $target.width();
400 targetRect.height = $target.height();
401
402 $tooltip.css({
403 left: targetRect.left,
404 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
405 });
406 $tooltip.addClass('below');
407 $tooltip.show();
408 }, function() {
409 // out
410 $tooltip.hide();
411 });
412 });
413
414 // Set up <h2> deeplinks
415 $('h2').click(function() {
416 var id = $(this).attr('id');
417 if (id) {
418 document.location.hash = id;
419 }
420 });
421
422 //Loads the +1 button
423 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
424 po.src = 'https://apis.google.com/js/plusone.js';
425 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
426
427
428 // Revise the sidenav widths to make room for the scrollbar
429 // which avoids the visible width from changing each time the bar appears
430 var $sidenav = $("#side-nav");
431 var sidenav_width = parseInt($sidenav.innerWidth());
432
433 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
434
435
436 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
437
438 if ($(".scroll-pane").length > 1) {
439 // Check if there's a user preference for the panel heights
440 var cookieHeight = readCookie("reference_height");
441 if (cookieHeight) {
442 restoreHeight(cookieHeight);
443 }
444 }
445
446 resizeNav();
447
448
449});
450
451
452
453function toggleFullscreen(enable) {
454 var delay = 20;
455 var enabled = true;
456 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
457 if (enable) {
458 // Currently NOT USING fullscreen; enable fullscreen
459 stylesheet.removeAttr('disabled');
460 $('#nav-swap .fullscreen').removeClass('disabled');
461 $('#devdoc-nav').css({left:''});
462 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
463 enabled = true;
464 } else {
465 // Currently USING fullscreen; disable fullscreen
466 stylesheet.attr('disabled', 'disabled');
467 $('#nav-swap .fullscreen').addClass('disabled');
468 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
469 enabled = false;
470 }
471 writeCookie("fullscreen", enabled, null, null);
472 setNavBarLeftPos();
473 resizeNav(delay);
474 updateSideNavPosition();
475 setTimeout(initSidenavHeightResize,delay);
476}
477
478
479function setNavBarLeftPos() {
480 navBarLeftPos = $('#body-content').offset().left;
481}
482
483
484function updateSideNavPosition() {
485 var newLeft = $(window).scrollLeft() - navBarLeftPos;
486 $('#devdoc-nav').css({left: -newLeft});
487 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
488}
489
490
491
492
493
494
495
496
497// TODO: use $(document).ready instead
498function addLoadEvent(newfun) {
499 var current = window.onload;
500 if (typeof window.onload != 'function') {
501 window.onload = newfun;
502 } else {
503 window.onload = function() {
504 current();
505 newfun();
506 }
507 }
508}
509
510var agent = navigator['userAgent'].toLowerCase();
511// If a mobile phone, set flag and do mobile setup
512if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
513 (agent.indexOf("blackberry") != -1) ||
514 (agent.indexOf("webos") != -1) ||
515 (agent.indexOf("mini") != -1)) { // opera mini browsers
516 isMobile = true;
517}
518
519
520/* loads the lists.js file to the page.
521Loading this in the head was slowing page load time */
522addLoadEvent( function() {
523 var lists = document.createElement("script");
524 lists.setAttribute("type","text/javascript");
525 lists.setAttribute("src", toRoot+"reference/lists.js");
526 document.getElementsByTagName("head")[0].appendChild(lists);
527} );
528
529
530addLoadEvent( function() {
531 $("pre:not(.no-pretty-print)").addClass("prettyprint");
532 prettyPrint();
533} );
534
Scott Maine4d8f1b2012-06-21 18:03:05 -0700535function init() {
536 //resizeNav();
537
538 resizePackagesNav = $("#resize-packages-nav");
539 classesNav = $("#classes-nav");
540 devdocNav = $("#devdoc-nav");
541
542 var cookiePath = "";
543 if (location.href.indexOf("/reference/") != -1) {
544 cookiePath = "reference_";
545 } else if (location.href.indexOf("/guide/") != -1) {
546 cookiePath = "guide_";
547 } else if (location.href.indexOf("/tools/") != -1) {
548 cookiePath = "tools_";
549 } else if (location.href.indexOf("/training/") != -1) {
550 cookiePath = "training_";
551 } else if (location.href.indexOf("/design/") != -1) {
552 cookiePath = "design_";
553 } else if (location.href.indexOf("/distribute/") != -1) {
554 cookiePath = "distribute_";
555 }
556}
557
558
559
560/* ######### RESIZE THE SIDENAV HEIGHT ########## */
561
562function resizeNav(delay) {
563 var $nav = $("#devdoc-nav");
564 var $window = $(window);
565 var navHeight;
566
567 // Get the height of entire window and the total header height.
568 // Then figure out based on scroll position whether the header is visible
569 var windowHeight = $window.height();
570 var scrollTop = $window.scrollTop();
571 var headerHeight = $('#header').outerHeight();
572 var subheaderHeight = $('#nav-x').outerHeight();
573 var headerVisible = (scrollTop < (headerHeight + subheaderHeight));
574
575 // get the height of space between nav and top of window.
576 // Could be either margin or top position, depending on whether the nav is fixed.
577 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
578 // add 1 for the #side-nav bottom margin
579
580 // Depending on whether the header is visible, set the side nav's height.
581 if (headerVisible) {
582 // The sidenav height grows as the header goes off screen
583 navHeight = windowHeight - (headerHeight + subheaderHeight - scrollTop) - topMargin;
584 } else {
585 // Once header is off screen, the nav height is almost full window height
586 navHeight = windowHeight - topMargin;
587 }
588
589
590
591 $scrollPanes = $(".scroll-pane");
592 if ($scrollPanes.length > 1) {
593 // subtract the height of the api level widget and nav swapper from the available nav height
594 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
595
596 $("#swapper").css({height:navHeight + "px"});
597 if ($("#nav-tree").is(":visible")) {
598 $("#nav-tree").css({height:navHeight});
599 }
600
601 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
602 //subtract 10px to account for drag bar
603
604 // if the window becomes small enough to make the class panel height 0,
605 // then the package panel should begin to shrink
606 if (parseInt(classesHeight) <= 0) {
607 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
608 $("#packages-nav").css({height:navHeight - 10});
609 }
610
611 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
612 $("#classes-nav .jspContainer").css({height:classesHeight});
613
614
615 } else {
616 $nav.height(navHeight);
617 }
618
619 if (delay) {
620 updateFromResize = true;
621 delayedReInitScrollbars(delay);
622 } else {
623 reInitScrollbars();
624 }
625
626}
627
628var updateScrollbars = false;
629var updateFromResize = false;
630
631/* Re-initialize the scrollbars to account for changed nav size.
632 * This method postpones the actual update by a 1/4 second in order to optimize the
633 * scroll performance while the header is still visible, because re-initializing the
634 * scroll panes is an intensive process.
635 */
636function delayedReInitScrollbars(delay) {
637 // If we're scheduled for an update, but have received another resize request
638 // before the scheduled resize has occured, just ignore the new request
639 // (and wait for the scheduled one).
640 if (updateScrollbars && updateFromResize) {
641 updateFromResize = false;
642 return;
643 }
644
645 // We're scheduled for an update and the update request came from this method's setTimeout
646 if (updateScrollbars && !updateFromResize) {
647 reInitScrollbars();
648 updateScrollbars = false;
649 } else {
650 updateScrollbars = true;
651 updateFromResize = false;
652 setTimeout('delayedReInitScrollbars()',delay);
653 }
654}
655
656/* Re-initialize the scrollbars to account for changed nav size. */
657function reInitScrollbars() {
658 var pane = $(".scroll-pane").each(function(){
659 var api = $(this).data('jsp');
660 if (!api) { setTimeout(reInitScrollbars,300); return;}
661 api.reinitialise( {verticalGutter:0} );
662 });
663 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
664}
665
666
667/* Resize the height of the nav panels in the reference,
668 * and save the new size to a cookie */
669function saveNavPanels() {
670 var basePath = getBaseUri(location.pathname);
671 var section = basePath.substring(1,basePath.indexOf("/",1));
672 writeCookie("height", resizePackagesNav.css("height"), section, null);
673}
674
675
676
677function restoreHeight(packageHeight) {
678 $("#resize-packages-nav").height(packageHeight);
679 $("#packages-nav").height(packageHeight);
680 // var classesHeight = navHeight - packageHeight;
681 // $("#classes-nav").css({height:classesHeight});
682 // $("#classes-nav .jspContainer").css({height:classesHeight});
683}
684
685
686
687/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
688
689
690
691
692
693/** Scroll the jScrollPane to make the currently selected item visible
694 This is called when the page finished loading. */
695function scrollIntoView(nav) {
696 var $nav = $("#"+nav);
697 var element = $nav.jScrollPane({/* ...settings... */});
698 var api = element.data('jsp');
699
700 if ($nav.is(':visible')) {
701 var $selected = $(".selected", $nav);
702 if ($selected.length == 0) return;
703
704 var selectedOffset = $selected.position().top;
705 if (selectedOffset + 90 > $nav.height()) { // add 90 so that we scroll up even
706 // if the current item is close to the bottom
707 api.scrollTo(0, selectedOffset - ($nav.height() / 4), false); // scroll the item into view
708 // to be 1/4 of the way from the top
709 }
710 }
711}
712
713
714
715
716
717
718/* Show popup dialogs */
719function showDialog(id) {
720 $dialog = $("#"+id);
721 $dialog.prepend('<div class="box-border"><div class="top"> <div class="left"></div> <div class="right"></div></div><div class="bottom"> <div class="left"></div> <div class="right"></div> </div> </div>');
722 $dialog.wrapInner('<div/>');
723 $dialog.removeClass("hide");
724}
725
726
727
728
729
730/* ######### COOKIES! ########## */
731
732function readCookie(cookie) {
733 var myCookie = cookie_namespace+"_"+cookie+"=";
734 if (document.cookie) {
735 var index = document.cookie.indexOf(myCookie);
736 if (index != -1) {
737 var valStart = index + myCookie.length;
738 var valEnd = document.cookie.indexOf(";", valStart);
739 if (valEnd == -1) {
740 valEnd = document.cookie.length;
741 }
742 var val = document.cookie.substring(valStart, valEnd);
743 return val;
744 }
745 }
746 return 0;
747}
748
749function writeCookie(cookie, val, section, expiration) {
750 if (val==undefined) return;
751 section = section == null ? "_" : "_"+section+"_";
752 if (expiration == null) {
753 var date = new Date();
754 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
755 expiration = date.toGMTString();
756 }
757 var cookieValue = cookie_namespace + section + cookie + "=" + val
758 + "; expires=" + expiration+"; path=/";
759 document.cookie = cookieValue;
760}
761
762/* ######### END COOKIES! ########## */
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788/*
789
790REMEMBER THE PREVIOUS PAGE FOR EACH TAB
791
792function loadLast(cookiePath) {
793 var location = window.location.href;
794 if (location.indexOf("/"+cookiePath+"/") != -1) {
795 return true;
796 }
797 var lastPage = readCookie(cookiePath + "_lastpage");
798 if (lastPage) {
799 window.location = lastPage;
800 return false;
801 }
802 return true;
803}
804
805
806
807$(window).unload(function(){
808 var path = getBaseUri(location.pathname);
809 if (path.indexOf("/reference/") != -1) {
810 writeCookie("lastpage", path, "reference", null);
811 } else if (path.indexOf("/guide/") != -1) {
812 writeCookie("lastpage", path, "guide", null);
813 } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) {
814 writeCookie("lastpage", path, "resources", null);
815 }
816});
817
818*/
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833function toggle(obj, slide) {
834 var ul = $("ul:first", obj);
835 var li = ul.parent();
836 if (li.hasClass("closed")) {
837 if (slide) {
838 ul.slideDown("fast");
839 } else {
840 ul.show();
841 }
842 li.removeClass("closed");
843 li.addClass("open");
844 $(".toggle-img", li).attr("title", "hide pages");
845 } else {
846 ul.slideUp("fast");
847 li.removeClass("open");
848 li.addClass("closed");
849 $(".toggle-img", li).attr("title", "show pages");
850 }
851}
852
853
854
855
856
857function buildToggleLists() {
858 $(".toggle-list").each(
859 function(i) {
860 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
861 $(this).addClass("closed");
862 });
863}
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896/* REFERENCE NAV SWAP */
897
898
899function getNavPref() {
900 var v = readCookie('reference_nav');
901 if (v != NAV_PREF_TREE) {
902 v = NAV_PREF_PANELS;
903 }
904 return v;
905}
906
907function chooseDefaultNav() {
908 nav_pref = getNavPref();
909 if (nav_pref == NAV_PREF_TREE) {
910 $("#nav-panels").toggle();
911 $("#panel-link").toggle();
912 $("#nav-tree").toggle();
913 $("#tree-link").toggle();
914 }
915}
916
917function swapNav() {
918 if (nav_pref == NAV_PREF_TREE) {
919 nav_pref = NAV_PREF_PANELS;
920 } else {
921 nav_pref = NAV_PREF_TREE;
922 init_default_navtree(toRoot);
923 }
924 var date = new Date();
925 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
926 writeCookie("nav", nav_pref, "reference", date.toGMTString());
927
928 $("#nav-panels").toggle();
929 $("#panel-link").toggle();
930 $("#nav-tree").toggle();
931 $("#tree-link").toggle();
932
933 resizeNav();
934
935 // Gross nasty hack to make tree view show up upon first swap by setting height manually
936 $("#nav-tree .jspContainer:visible")
937 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
938 // Another nasty hack to make the scrollbar appear now that we have height
939 resizeNav();
940
941 if ($("#nav-tree").is(':visible')) {
942 scrollIntoView("nav-tree");
943 } else {
944 scrollIntoView("packages-nav");
945 scrollIntoView("classes-nav");
946 }
947}
948
949
950
Scott Mainf5089842012-08-14 16:31:07 -0700951/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700952/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -0700953/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700954
955function getBaseUri(uri) {
956 var intlUrl = (uri.substring(0,6) == "/intl/");
957 if (intlUrl) {
958 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
959 base = base.substring(base.indexOf('/')+1, base.length);
960 //alert("intl, returning base url: /" + base);
961 return ("/" + base);
962 } else {
963 //alert("not intl, returning uri as found.");
964 return uri;
965 }
966}
967
968function requestAppendHL(uri) {
969//append "?hl=<lang> to an outgoing request (such as to blog)
970 var lang = getLangPref();
971 if (lang) {
972 var q = 'hl=' + lang;
973 uri += '?' + q;
974 window.location = uri;
975 return false;
976 } else {
977 return true;
978 }
979}
980
981
Scott Maine4d8f1b2012-06-21 18:03:05 -0700982function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -0700983 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
984 $links.each(function(i){ // for each link with a translation
985 var $link = $(this);
986 if (lang != "en") { // No need to worry about English, because a language change invokes new request
987 // put the desired language from the attribute as the text
988 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700989 }
Scott Main6eb95f12012-10-02 17:12:23 -0700990 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700991}
992
993function changeDocLang(lang) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700994 changeNavLang(lang);
995}
996
997function changeLangPref(lang, refresh) {
998 var date = new Date();
999 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
1000 // keep this for 50 years
1001 //alert("expires: " + expires)
1002 writeCookie("pref_lang", lang, null, expires);
1003 changeDocLang(lang);
1004 if (refresh) {
1005 l = getBaseUri(location.pathname);
1006 window.location = l;
1007 }
1008}
1009
1010function loadLangPref() {
1011 var lang = readCookie("pref_lang");
1012 if (lang != 0) {
1013 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1014 }
1015}
1016
1017function getLangPref() {
1018 var lang = $("#language").find(":selected").attr("value");
1019 if (!lang) {
1020 lang = readCookie("pref_lang");
1021 }
1022 return (lang != 0) ? lang : 'en';
1023}
1024
1025/* ########## END LOCALIZATION ############ */
1026
1027
1028
1029
1030
1031
1032/* Used to hide and reveal supplemental content, such as long code samples.
1033 See the companion CSS in android-developer-docs.css */
1034function toggleContent(obj) {
1035 var div = $(obj.parentNode.parentNode);
1036 var toggleMe = $(".toggle-content-toggleme",div);
1037 if (div.hasClass("closed")) { // if it's closed, open it
1038 toggleMe.slideDown();
1039 $(".toggle-content-text", obj).toggle();
1040 div.removeClass("closed").addClass("open");
1041 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot
1042 + "assets/images/triangle-opened.png");
1043 } else { // if it's open, close it
1044 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
1045 $(".toggle-content-text", obj).toggle();
1046 div.removeClass("open").addClass("closed");
1047 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
1048 + "assets/images/triangle-closed.png");
1049 });
1050 }
1051 return false;
1052}
Scott Mainf5089842012-08-14 16:31:07 -07001053
1054
1055
1056
1057
1058
1059/*
1060 * Slideshow 1.0
1061 * Used on /index.html and /develop/index.html for carousel
1062 *
1063 * Sample usage:
1064 * HTML -
1065 * <div class="slideshow-container">
1066 * <a href="" class="slideshow-prev">Prev</a>
1067 * <a href="" class="slideshow-next">Next</a>
1068 * <ul>
1069 * <li class="item"><img src="images/marquee1.jpg"></li>
1070 * <li class="item"><img src="images/marquee2.jpg"></li>
1071 * <li class="item"><img src="images/marquee3.jpg"></li>
1072 * <li class="item"><img src="images/marquee4.jpg"></li>
1073 * </ul>
1074 * </div>
1075 *
1076 * <script type="text/javascript">
1077 * $('.slideshow-container').dacSlideshow({
1078 * auto: true,
1079 * btnPrev: '.slideshow-prev',
1080 * btnNext: '.slideshow-next'
1081 * });
1082 * </script>
1083 *
1084 * Options:
1085 * btnPrev: optional identifier for previous button
1086 * btnNext: optional identifier for next button
1087 * auto: whether or not to auto-proceed
1088 * speed: animation speed
1089 * autoTime: time between auto-rotation
1090 * easing: easing function for transition
1091 * start: item to select by default
1092 * scroll: direction to scroll in
1093 * pagination: whether or not to include dotted pagination
1094 *
1095 */
1096
1097 (function($) {
1098 $.fn.dacSlideshow = function(o) {
1099
1100 //Options - see above
1101 o = $.extend({
1102 btnPrev: null,
1103 btnNext: null,
1104 auto: true,
1105 speed: 500,
1106 autoTime: 12000,
1107 easing: null,
1108 start: 0,
1109 scroll: 1,
1110 pagination: true
1111
1112 }, o || {});
1113
1114 //Set up a carousel for each
1115 return this.each(function() {
1116
1117 var running = false;
1118 var animCss = o.vertical ? "top" : "left";
1119 var sizeCss = o.vertical ? "height" : "width";
1120 var div = $(this);
1121 var ul = $("ul", div);
1122 var tLi = $("li", ul);
1123 var tl = tLi.size();
1124 var timer = null;
1125
1126 var li = $("li", ul);
1127 var itemLength = li.size();
1128 var curr = o.start;
1129
1130 li.css({float: o.vertical ? "none" : "left"});
1131 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1132 div.css({position: "relative", "z-index": "2", left: "0px"});
1133
1134 var liSize = o.vertical ? height(li) : width(li);
1135 var ulSize = liSize * itemLength;
1136 var divSize = liSize;
1137
1138 li.css({width: li.width(), height: li.height()});
1139 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1140
1141 div.css(sizeCss, divSize+"px");
1142
1143 //Pagination
1144 if (o.pagination) {
1145 var pagination = $("<div class='pagination'></div>");
1146 var pag_ul = $("<ul></ul>");
1147 if (tl > 1) {
1148 for (var i=0;i<tl;i++) {
1149 var li = $("<li>"+i+"</li>");
1150 pag_ul.append(li);
1151 if (i==o.start) li.addClass('active');
1152 li.click(function() {
1153 go(parseInt($(this).text()));
1154 })
1155 }
1156 pagination.append(pag_ul);
1157 div.append(pagination);
1158 }
1159 }
1160
1161 //Previous button
1162 if(o.btnPrev)
1163 $(o.btnPrev).click(function(e) {
1164 e.preventDefault();
1165 return go(curr-o.scroll);
1166 });
1167
1168 //Next button
1169 if(o.btnNext)
1170 $(o.btnNext).click(function(e) {
1171 e.preventDefault();
1172 return go(curr+o.scroll);
1173 });
1174
1175 //Auto rotation
1176 if(o.auto) startRotateTimer();
1177
1178 function startRotateTimer() {
1179 clearInterval(timer);
1180 timer = setInterval(function() {
1181 if (curr == tl-1) {
1182 go(0);
1183 } else {
1184 go(curr+o.scroll);
1185 }
1186 }, o.autoTime);
1187 }
1188
1189 //Go to an item
1190 function go(to) {
1191 if(!running) {
1192
1193 if(to<0) {
1194 to = itemLength-1;
1195 } else if (to>itemLength-1) {
1196 to = 0;
1197 }
1198 curr = to;
1199
1200 running = true;
1201
1202 ul.animate(
1203 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1204 function() {
1205 running = false;
1206 }
1207 );
1208
1209 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1210 $( (curr-o.scroll<0 && o.btnPrev)
1211 ||
1212 (curr+o.scroll > itemLength && o.btnNext)
1213 ||
1214 []
1215 ).addClass("disabled");
1216
1217
1218 var nav_items = $('li', pagination);
1219 nav_items.removeClass('active');
1220 nav_items.eq(to).addClass('active');
1221
1222
1223 }
1224 if(o.auto) startRotateTimer();
1225 return false;
1226 };
1227 });
1228 };
1229
1230 function css(el, prop) {
1231 return parseInt($.css(el[0], prop)) || 0;
1232 };
1233 function width(el) {
1234 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1235 };
1236 function height(el) {
1237 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1238 };
1239
1240 })(jQuery);
1241
1242
1243/*
1244 * dacSlideshow 1.0
1245 * Used on develop/index.html for side-sliding tabs
1246 *
1247 * Sample usage:
1248 * HTML -
1249 * <div class="slideshow-container">
1250 * <a href="" class="slideshow-prev">Prev</a>
1251 * <a href="" class="slideshow-next">Next</a>
1252 * <ul>
1253 * <li class="item"><img src="images/marquee1.jpg"></li>
1254 * <li class="item"><img src="images/marquee2.jpg"></li>
1255 * <li class="item"><img src="images/marquee3.jpg"></li>
1256 * <li class="item"><img src="images/marquee4.jpg"></li>
1257 * </ul>
1258 * </div>
1259 *
1260 * <script type="text/javascript">
1261 * $('.slideshow-container').dacSlideshow({
1262 * auto: true,
1263 * btnPrev: '.slideshow-prev',
1264 * btnNext: '.slideshow-next'
1265 * });
1266 * </script>
1267 *
1268 * Options:
1269 * btnPrev: optional identifier for previous button
1270 * btnNext: optional identifier for next button
1271 * auto: whether or not to auto-proceed
1272 * speed: animation speed
1273 * autoTime: time between auto-rotation
1274 * easing: easing function for transition
1275 * start: item to select by default
1276 * scroll: direction to scroll in
1277 * pagination: whether or not to include dotted pagination
1278 *
1279 */
1280 (function($) {
1281 $.fn.dacTabbedList = function(o) {
1282
1283 //Options - see above
1284 o = $.extend({
1285 speed : 250,
1286 easing: null,
1287 nav_id: null,
1288 frame_id: null
1289 }, o || {});
1290
1291 //Set up a carousel for each
1292 return this.each(function() {
1293
1294 var curr = 0;
1295 var running = false;
1296 var animCss = "margin-left";
1297 var sizeCss = "width";
1298 var div = $(this);
1299
1300 var nav = $(o.nav_id, div);
1301 var nav_li = $("li", nav);
1302 var nav_size = nav_li.size();
1303 var frame = div.find(o.frame_id);
1304 var content_width = $(frame).find('ul').width();
1305 //Buttons
1306 $(nav_li).click(function(e) {
1307 go($(nav_li).index($(this)));
1308 })
1309
1310 //Go to an item
1311 function go(to) {
1312 if(!running) {
1313 curr = to;
1314 running = true;
1315
1316 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1317 function() {
1318 running = false;
1319 }
1320 );
1321
1322
1323 nav_li.removeClass('active');
1324 nav_li.eq(to).addClass('active');
1325
1326
1327 }
1328 return false;
1329 };
1330 });
1331 };
1332
1333 function css(el, prop) {
1334 return parseInt($.css(el[0], prop)) || 0;
1335 };
1336 function width(el) {
1337 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1338 };
1339 function height(el) {
1340 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1341 };
1342
1343 })(jQuery);
1344
1345
1346
1347
1348
1349/* ######################################################## */
1350/* ################ SEARCH SUGGESTIONS ################## */
1351/* ######################################################## */
1352
1353
1354var gSelectedIndex = -1;
1355var gSelectedID = -1;
1356var gMatches = new Array();
1357var gLastText = "";
1358var ROW_COUNT = 20;
1359var gInitialized = false;
1360
1361function set_item_selected($li, selected)
1362{
1363 if (selected) {
1364 $li.attr('class','jd-autocomplete jd-selected');
1365 } else {
1366 $li.attr('class','jd-autocomplete');
1367 }
1368}
1369
1370function set_item_values(toroot, $li, match)
1371{
1372 var $link = $('a',$li);
1373 $link.html(match.__hilabel || match.label);
1374 $link.attr('href',toroot + match.link);
1375}
1376
1377function sync_selection_table(toroot)
1378{
1379 var $list = $("#search_filtered");
1380 var $li; //list item jquery object
1381 var i; //list item iterator
1382 gSelectedID = -1;
1383
1384 //initialize the table; draw it for the first time (but not visible).
1385 if (!gInitialized) {
1386 for (i=0; i<ROW_COUNT; i++) {
1387 var $li = $("<li class='jd-autocomplete'></li>");
1388 $list.append($li);
1389
1390 $li.mousedown(function() {
1391 window.location = this.firstChild.getAttribute("href");
1392 });
1393 $li.mouseover(function() {
1394 $('#search_filtered li').removeClass('jd-selected');
1395 $(this).addClass('jd-selected');
1396 gSelectedIndex = $('#search_filtered li').index(this);
1397 });
1398 $li.append('<a></a>');
1399 }
1400 gInitialized = true;
1401 }
1402
1403 //if we have results, make the table visible and initialize result info
1404 if (gMatches.length > 0) {
1405 $('#search_filtered_div').removeClass('no-display');
1406 var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
1407 for (i=0; i<N; i++) {
1408 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1409 $li.attr('class','show-item');
1410 set_item_values(toroot, $li, gMatches[i]);
1411 set_item_selected($li, i == gSelectedIndex);
1412 if (i == gSelectedIndex) {
1413 gSelectedID = gMatches[i].id;
1414 }
1415 }
1416 //start hiding rows that are no longer matches
1417 for (; i<ROW_COUNT; i++) {
1418 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1419 $li.attr('class','no-display');
1420 }
1421 //if there are more results we're not showing, so say so.
1422/* if (gMatches.length > ROW_COUNT) {
1423 li = list.rows[ROW_COUNT];
1424 li.className = "show-item";
1425 c1 = li.cells[0];
1426 c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more";
1427 } else {
1428 list.rows[ROW_COUNT].className = "hide-item";
1429 }*/
1430 //if we have no results, hide the table
1431 } else {
1432 $('#search_filtered_div').addClass('no-display');
1433 }
1434}
1435
1436function search_changed(e, kd, toroot)
1437{
1438 var search = document.getElementById("search_autocomplete");
1439 var text = search.value.replace(/(^ +)|( +$)/g, '');
1440
1441 // show/hide the close button
1442 if (text != '') {
1443 $(".search .close").removeClass("hide");
1444 } else {
1445 $(".search .close").addClass("hide");
1446 }
1447
1448 // 13 = enter
1449 if (e.keyCode == 13) {
1450 $('#search_filtered_div').addClass('no-display');
1451 if (!$('#search_filtered_div').hasClass('no-display') || (gSelectedIndex < 0)) {
1452 if ($("#searchResults").is(":hidden")) {
1453 // if results aren't showing, return true to allow search to execute
1454 return true;
1455 } else {
1456 // otherwise, results are already showing, so allow ajax to auto refresh the results
1457 // and ignore this Enter press to avoid the reload.
1458 return false;
1459 }
1460 } else if (kd && gSelectedIndex >= 0) {
1461 window.location = toroot + gMatches[gSelectedIndex].link;
1462 return false;
1463 }
1464 }
1465 // 38 -- arrow up
1466 else if (kd && (e.keyCode == 38)) {
1467 if (gSelectedIndex >= 0) {
1468 $('#search_filtered li').removeClass('jd-selected');
1469 gSelectedIndex--;
1470 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1471 }
1472 return false;
1473 }
1474 // 40 -- arrow down
1475 else if (kd && (e.keyCode == 40)) {
1476 if (gSelectedIndex < gMatches.length-1
1477 && gSelectedIndex < ROW_COUNT-1) {
1478 $('#search_filtered li').removeClass('jd-selected');
1479 gSelectedIndex++;
1480 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1481 }
1482 return false;
1483 }
1484 else if (!kd && (e.keyCode != 40) && (e.keyCode != 38)) {
1485 gMatches = new Array();
1486 matchedCount = 0;
1487 gSelectedIndex = -1;
1488 for (var i=0; i<DATA.length; i++) {
1489 var s = DATA[i];
1490 if (text.length != 0 &&
1491 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1492 gMatches[matchedCount] = s;
1493 matchedCount++;
1494 }
1495 }
1496 rank_autocomplete_results(text);
1497 for (var i=0; i<gMatches.length; i++) {
1498 var s = gMatches[i];
1499 if (gSelectedID == s.id) {
1500 gSelectedIndex = i;
1501 }
1502 }
1503 highlight_autocomplete_result_labels(text);
1504 sync_selection_table(toroot);
1505 return true; // allow the event to bubble up to the search api
1506 }
1507}
1508
1509function rank_autocomplete_results(query) {
1510 query = query || '';
1511 if (!gMatches || !gMatches.length)
1512 return;
1513
1514 // helper function that gets the last occurence index of the given regex
1515 // in the given string, or -1 if not found
1516 var _lastSearch = function(s, re) {
1517 if (s == '')
1518 return -1;
1519 var l = -1;
1520 var tmp;
1521 while ((tmp = s.search(re)) >= 0) {
1522 if (l < 0) l = 0;
1523 l += tmp;
1524 s = s.substr(tmp + 1);
1525 }
1526 return l;
1527 };
1528
1529 // helper function that counts the occurrences of a given character in
1530 // a given string
1531 var _countChar = function(s, c) {
1532 var n = 0;
1533 for (var i=0; i<s.length; i++)
1534 if (s.charAt(i) == c) ++n;
1535 return n;
1536 };
1537
1538 var queryLower = query.toLowerCase();
1539 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1540 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1541 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1542
1543 var _resultScoreFn = function(result) {
1544 // scores are calculated based on exact and prefix matches,
1545 // and then number of path separators (dots) from the last
1546 // match (i.e. favoring classes and deep package names)
1547 var score = 1.0;
1548 var labelLower = result.label.toLowerCase();
1549 var t;
1550 t = _lastSearch(labelLower, partExactAlnumRE);
1551 if (t >= 0) {
1552 // exact part match
1553 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1554 score *= 200 / (partsAfter + 1);
1555 } else {
1556 t = _lastSearch(labelLower, partPrefixAlnumRE);
1557 if (t >= 0) {
1558 // part prefix match
1559 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1560 score *= 20 / (partsAfter + 1);
1561 }
1562 }
1563
1564 return score;
1565 };
1566
1567 for (var i=0; i<gMatches.length; i++) {
1568 gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);
1569 }
1570
1571 gMatches.sort(function(a,b){
1572 var n = b.__resultScore - a.__resultScore;
1573 if (n == 0) // lexicographical sort if scores are the same
1574 n = (a.label < b.label) ? -1 : 1;
1575 return n;
1576 });
1577}
1578
1579function highlight_autocomplete_result_labels(query) {
1580 query = query || '';
1581 if (!gMatches || !gMatches.length)
1582 return;
1583
1584 var queryLower = query.toLowerCase();
1585 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
1586 var queryRE = new RegExp(
1587 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
1588 for (var i=0; i<gMatches.length; i++) {
1589 gMatches[i].__hilabel = gMatches[i].label.replace(
1590 queryRE, '<b>$1</b>');
1591 }
1592}
1593
1594function search_focus_changed(obj, focused)
1595{
1596 if (!focused) {
1597 if(obj.value == ""){
1598 $(".search .close").addClass("hide");
1599 }
1600 document.getElementById("search_filtered_div").className = "no-display";
1601 }
1602}
1603
1604function submit_search() {
1605 var query = document.getElementById('search_autocomplete').value;
1606 location.hash = 'q=' + query;
1607 loadSearchResults();
1608 $("#searchResults").slideDown('slow');
1609 return false;
1610}
1611
1612
1613function hideResults() {
1614 $("#searchResults").slideUp();
1615 $(".search .close").addClass("hide");
1616 location.hash = '';
1617
1618 $("#search_autocomplete").val("").blur();
1619
1620 // reset the ajax search callback to nothing, so results don't appear unless ENTER
1621 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
1622 return false;
1623}
1624
1625
1626
1627/* ########################################################## */
1628/* ################ CUSTOM SEARCH ENGINE ################## */
1629/* ########################################################## */
1630
1631google.load('search', '1');
1632var searchControl;
1633
1634function loadSearchResults() {
1635 document.getElementById("search_autocomplete").style.color = "#000";
1636
1637 // create search control
1638 searchControl = new google.search.SearchControl();
1639
1640 // use our existing search form and use tabs when multiple searchers are used
1641 drawOptions = new google.search.DrawOptions();
1642 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
1643 drawOptions.setInput(document.getElementById("search_autocomplete"));
1644
1645 // configure search result options
1646 searchOptions = new google.search.SearcherOptions();
1647 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
1648
1649 // configure each of the searchers, for each tab
1650 devSiteSearcher = new google.search.WebSearch();
1651 devSiteSearcher.setUserDefinedLabel("All");
1652 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
1653
1654 designSearcher = new google.search.WebSearch();
1655 designSearcher.setUserDefinedLabel("Design");
1656 designSearcher.setSiteRestriction("http://developer.android.com/design/");
1657
1658 trainingSearcher = new google.search.WebSearch();
1659 trainingSearcher.setUserDefinedLabel("Training");
1660 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
1661
1662 guidesSearcher = new google.search.WebSearch();
1663 guidesSearcher.setUserDefinedLabel("Guides");
1664 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
1665
1666 referenceSearcher = new google.search.WebSearch();
1667 referenceSearcher.setUserDefinedLabel("Reference");
1668 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
1669
1670 blogSearcher = new google.search.WebSearch();
1671 blogSearcher.setUserDefinedLabel("Blog");
1672 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
1673
1674 // add each searcher to the search control
1675 searchControl.addSearcher(devSiteSearcher, searchOptions);
1676 searchControl.addSearcher(designSearcher, searchOptions);
1677 searchControl.addSearcher(trainingSearcher, searchOptions);
1678 searchControl.addSearcher(guidesSearcher, searchOptions);
1679 searchControl.addSearcher(referenceSearcher, searchOptions);
1680 searchControl.addSearcher(blogSearcher, searchOptions);
1681
1682 // configure result options
1683 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
1684 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
1685 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
1686 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
1687
1688 // upon ajax search, refresh the url and search title
1689 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
1690 updateResultTitle(query);
1691 var query = document.getElementById('search_autocomplete').value;
1692 location.hash = 'q=' + query;
1693 });
1694
1695 // draw the search results box
1696 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
1697
1698 // get query and execute the search
1699 searchControl.execute(decodeURI(getQuery(location.hash)));
1700
1701 document.getElementById("search_autocomplete").focus();
1702 addTabListeners();
1703}
1704// End of loadSearchResults
1705
1706
1707google.setOnLoadCallback(function(){
1708 if (location.hash.indexOf("q=") == -1) {
1709 // if there's no query in the url, don't search and make sure results are hidden
1710 $('#searchResults').hide();
1711 return;
1712 } else {
1713 // first time loading search results for this page
1714 $('#searchResults').slideDown('slow');
1715 $(".search .close").removeClass("hide");
1716 loadSearchResults();
1717 }
1718}, true);
1719
1720// when an event on the browser history occurs (back, forward, load) requery hash and do search
1721$(window).hashchange( function(){
1722 // Exit if the hash isn't a search query or there's an error in the query
1723 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
1724 // If the results pane is open, close it.
1725 if (!$("#searchResults").is(":hidden")) {
1726 hideResults();
1727 }
1728 return;
1729 }
1730
1731 // Otherwise, we have a search to do
1732 var query = decodeURI(getQuery(location.hash));
1733 searchControl.execute(query);
1734 $('#searchResults').slideDown('slow');
1735 $("#search_autocomplete").focus();
1736 $(".search .close").removeClass("hide");
1737
1738 updateResultTitle(query);
1739});
1740
1741function updateResultTitle(query) {
1742 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
1743}
1744
1745// forcefully regain key-up event control (previously jacked by search api)
1746$("#search_autocomplete").keyup(function(event) {
1747 return search_changed(event, false, toRoot);
1748});
1749
1750// add event listeners to each tab so we can track the browser history
1751function addTabListeners() {
1752 var tabHeaders = $(".gsc-tabHeader");
1753 for (var i = 0; i < tabHeaders.length; i++) {
1754 $(tabHeaders[i]).attr("id",i).click(function() {
1755 /*
1756 // make a copy of the page numbers for the search left pane
1757 setTimeout(function() {
1758 // remove any residual page numbers
1759 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
1760 // move the page numbers to the left position; make a clone,
1761 // because the element is drawn to the DOM only once
1762 // and because we're going to remove it (previous line),
1763 // we need it to be available to move again as the user navigates
1764 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
1765 .clone().appendTo('#searchResults .gsc-tabsArea');
1766 }, 200);
1767 */
1768 });
1769 }
1770 setTimeout(function(){$(tabHeaders[0]).click()},200);
1771}
1772
1773
1774function getQuery(hash) {
1775 var queryParts = hash.split('=');
1776 return queryParts[1];
1777}
1778
1779/* returns the given string with all HTML brackets converted to entities
1780 TODO: move this to the site's JS library */
1781function escapeHTML(string) {
1782 return string.replace(/</g,"&lt;")
1783 .replace(/>/g,"&gt;");
1784}
1785
1786
1787
1788
1789
1790
1791
1792/* ######################################################## */
1793/* ################# JAVADOC REFERENCE ################### */
1794/* ######################################################## */
1795
Scott Main65511c02012-09-07 15:51:32 -07001796/* Initialize some droiddoc stuff, but only if we're in the reference */
1797if (location.pathname.indexOf("/reference") == 0) {
1798 $(document).ready(function() {
1799 // init available apis based on user pref
1800 changeApiLevel();
1801 initSidenavHeightResize()
1802 });
1803}
Scott Mainf5089842012-08-14 16:31:07 -07001804
1805var API_LEVEL_COOKIE = "api_level";
1806var minLevel = 1;
1807var maxLevel = 1;
1808
1809/******* SIDENAV DIMENSIONS ************/
1810
1811 function initSidenavHeightResize() {
1812 // Change the drag bar size to nicely fit the scrollbar positions
1813 var $dragBar = $(".ui-resizable-s");
1814 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1815
1816 $( "#resize-packages-nav" ).resizable({
1817 containment: "#nav-panels",
1818 handles: "s",
1819 alsoResize: "#packages-nav",
1820 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1821 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
1822 });
1823
1824 }
1825
1826function updateSidenavFixedWidth() {
1827 if (!navBarIsFixed) return;
1828 $('#devdoc-nav').css({
1829 'width' : $('#side-nav').css('width'),
1830 'margin' : $('#side-nav').css('margin')
1831 });
1832 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1833
1834 initSidenavHeightResize();
1835}
1836
1837function updateSidenavFullscreenWidth() {
1838 if (!navBarIsFixed) return;
1839 $('#devdoc-nav').css({
1840 'width' : $('#side-nav').css('width'),
1841 'margin' : $('#side-nav').css('margin')
1842 });
1843 $('#devdoc-nav .totop').css({'left': 'inherit'});
1844
1845 initSidenavHeightResize();
1846}
1847
1848function buildApiLevelSelector() {
1849 maxLevel = SINCE_DATA.length;
1850 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1851 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1852
1853 minLevel = parseInt($("#doc-api-level").attr("class"));
1854 // Handle provisional api levels; the provisional level will always be the highest possible level
1855 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1856 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1857 if (isNaN(minLevel) && minLevel.length) {
1858 minLevel = maxLevel;
1859 }
1860 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1861 for (var i = maxLevel-1; i >= 0; i--) {
1862 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1863 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1864 select.append(option);
1865 }
1866
1867 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1868 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1869 selectedLevelItem.setAttribute('selected',true);
1870}
1871
1872function changeApiLevel() {
1873 maxLevel = SINCE_DATA.length;
1874 var selectedLevel = maxLevel;
1875
1876 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1877 toggleVisisbleApis(selectedLevel, "body");
1878
1879 var date = new Date();
1880 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1881 var expiration = date.toGMTString();
1882 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1883
1884 if (selectedLevel < minLevel) {
1885 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
1886 $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API level " + selectedLevel + ".</strong></p>"
1887 + "<p>To use this " + thing + ", you must develop your app using a build target "
1888 + "that supports API level " + $("#doc-api-level").attr("class") + " or higher. To read these "
1889 + "APIs, change the value of the API level filter above.</p>"
1890 + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API level?</a></p></div>");
1891 } else {
1892 $("#naMessage").hide();
1893 }
1894}
1895
1896function toggleVisisbleApis(selectedLevel, context) {
1897 var apis = $(".api",context);
1898 apis.each(function(i) {
1899 var obj = $(this);
1900 var className = obj.attr("class");
1901 var apiLevelIndex = className.lastIndexOf("-")+1;
1902 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1903 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1904 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
1905 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
1906 return;
1907 }
1908 apiLevel = parseInt(apiLevel);
1909
1910 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
1911 var selectedLevelNum = parseInt(selectedLevel)
1912 var apiLevelNum = parseInt(apiLevel);
1913 if (isNaN(apiLevelNum)) {
1914 apiLevelNum = maxLevel;
1915 }
1916
1917 // Grey things out that aren't available and give a tooltip title
1918 if (apiLevelNum > selectedLevelNum) {
1919 obj.addClass("absent").attr("title","Requires API Level \""
1920 + apiLevel + "\" or higher");
1921 }
1922 else obj.removeClass("absent").removeAttr("title");
1923 });
1924}
1925
1926
1927
1928
1929/* ################# SIDENAV TREE VIEW ################### */
1930
1931function new_node(me, mom, text, link, children_data, api_level)
1932{
1933 var node = new Object();
1934 node.children = Array();
1935 node.children_data = children_data;
1936 node.depth = mom.depth + 1;
1937
1938 node.li = document.createElement("li");
1939 mom.get_children_ul().appendChild(node.li);
1940
1941 node.label_div = document.createElement("div");
1942 node.label_div.className = "label";
1943 if (api_level != null) {
1944 $(node.label_div).addClass("api");
1945 $(node.label_div).addClass("api-level-"+api_level);
1946 }
1947 node.li.appendChild(node.label_div);
1948
1949 if (children_data != null) {
1950 node.expand_toggle = document.createElement("a");
1951 node.expand_toggle.href = "javascript:void(0)";
1952 node.expand_toggle.onclick = function() {
1953 if (node.expanded) {
1954 $(node.get_children_ul()).slideUp("fast");
1955 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
1956 node.expanded = false;
1957 } else {
1958 expand_node(me, node);
1959 }
1960 };
1961 node.label_div.appendChild(node.expand_toggle);
1962
1963 node.plus_img = document.createElement("img");
1964 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
1965 node.plus_img.className = "plus";
1966 node.plus_img.width = "8";
1967 node.plus_img.border = "0";
1968 node.expand_toggle.appendChild(node.plus_img);
1969
1970 node.expanded = false;
1971 }
1972
1973 var a = document.createElement("a");
1974 node.label_div.appendChild(a);
1975 node.label = document.createTextNode(text);
1976 a.appendChild(node.label);
1977 if (link) {
1978 a.href = me.toroot + link;
1979 } else {
1980 if (children_data != null) {
1981 a.className = "nolink";
1982 a.href = "javascript:void(0)";
1983 a.onclick = node.expand_toggle.onclick;
1984 // This next line shouldn't be necessary. I'll buy a beer for the first
1985 // person who figures out how to remove this line and have the link
1986 // toggle shut on the first try. --joeo@android.com
1987 node.expanded = false;
1988 }
1989 }
1990
1991
1992 node.children_ul = null;
1993 node.get_children_ul = function() {
1994 if (!node.children_ul) {
1995 node.children_ul = document.createElement("ul");
1996 node.children_ul.className = "children_ul";
1997 node.children_ul.style.display = "none";
1998 node.li.appendChild(node.children_ul);
1999 }
2000 return node.children_ul;
2001 };
2002
2003 return node;
2004}
2005
2006function expand_node(me, node)
2007{
2008 if (node.children_data && !node.expanded) {
2009 if (node.children_visited) {
2010 $(node.get_children_ul()).slideDown("fast");
2011 } else {
2012 get_node(me, node);
2013 if ($(node.label_div).hasClass("absent")) {
2014 $(node.get_children_ul()).addClass("absent");
2015 }
2016 $(node.get_children_ul()).slideDown("fast");
2017 }
2018 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2019 node.expanded = true;
2020
2021 // perform api level toggling because new nodes are new to the DOM
2022 var selectedLevel = $("#apiLevelSelector option:selected").val();
2023 toggleVisisbleApis(selectedLevel, "#side-nav");
2024 }
2025}
2026
2027function get_node(me, mom)
2028{
2029 mom.children_visited = true;
2030 for (var i in mom.children_data) {
2031 var node_data = mom.children_data[i];
2032 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2033 node_data[2], node_data[3]);
2034 }
2035}
2036
2037function this_page_relative(toroot)
2038{
2039 var full = document.location.pathname;
2040 var file = "";
2041 if (toroot.substr(0, 1) == "/") {
2042 if (full.substr(0, toroot.length) == toroot) {
2043 return full.substr(toroot.length);
2044 } else {
2045 // the file isn't under toroot. Fail.
2046 return null;
2047 }
2048 } else {
2049 if (toroot != "./") {
2050 toroot = "./" + toroot;
2051 }
2052 do {
2053 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2054 var pos = full.lastIndexOf("/");
2055 file = full.substr(pos) + file;
2056 full = full.substr(0, pos);
2057 toroot = toroot.substr(0, toroot.length-3);
2058 }
2059 } while (toroot != "" && toroot != "/");
2060 return file.substr(1);
2061 }
2062}
2063
2064function find_page(url, data)
2065{
2066 var nodes = data;
2067 var result = null;
2068 for (var i in nodes) {
2069 var d = nodes[i];
2070 if (d[1] == url) {
2071 return new Array(i);
2072 }
2073 else if (d[2] != null) {
2074 result = find_page(url, d[2]);
2075 if (result != null) {
2076 return (new Array(i).concat(result));
2077 }
2078 }
2079 }
2080 return null;
2081}
2082
2083function load_navtree_data(toroot) {
2084 var navtreeData = document.createElement("script");
2085 navtreeData.setAttribute("type","text/javascript");
2086 navtreeData.setAttribute("src", toroot+"navtree_data.js");
2087 $("head").append($(navtreeData));
2088}
2089
2090function init_default_navtree(toroot) {
2091 init_navtree("tree-list", toroot, NAVTREE_DATA);
2092
2093 // perform api level toggling because because the whole tree is new to the DOM
2094 var selectedLevel = $("#apiLevelSelector option:selected").val();
2095 toggleVisisbleApis(selectedLevel, "#side-nav");
2096}
2097
2098function init_navtree(navtree_id, toroot, root_nodes)
2099{
2100 var me = new Object();
2101 me.toroot = toroot;
2102 me.node = new Object();
2103
2104 me.node.li = document.getElementById(navtree_id);
2105 me.node.children_data = root_nodes;
2106 me.node.children = new Array();
2107 me.node.children_ul = document.createElement("ul");
2108 me.node.get_children_ul = function() { return me.node.children_ul; };
2109 //me.node.children_ul.className = "children_ul";
2110 me.node.li.appendChild(me.node.children_ul);
2111 me.node.depth = 0;
2112
2113 get_node(me, me.node);
2114
2115 me.this_page = this_page_relative(toroot);
2116 me.breadcrumbs = find_page(me.this_page, root_nodes);
2117 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2118 var mom = me.node;
2119 for (var i in me.breadcrumbs) {
2120 var j = me.breadcrumbs[i];
2121 mom = mom.children[j];
2122 expand_node(me, mom);
2123 }
2124 mom.label_div.className = mom.label_div.className + " selected";
2125 addLoadEvent(function() {
2126 scrollIntoView("nav-tree");
2127 });
2128 }
2129}
2130
2131/* TOGGLE INHERITED MEMBERS */
2132
2133/* Toggle an inherited class (arrow toggle)
2134 * @param linkObj The link that was clicked.
2135 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2136 * 'null' to simply toggle.
2137 */
2138function toggleInherited(linkObj, expand) {
2139 var base = linkObj.getAttribute("id");
2140 var list = document.getElementById(base + "-list");
2141 var summary = document.getElementById(base + "-summary");
2142 var trigger = document.getElementById(base + "-trigger");
2143 var a = $(linkObj);
2144 if ( (expand == null && a.hasClass("closed")) || expand ) {
2145 list.style.display = "none";
2146 summary.style.display = "block";
2147 trigger.src = toRoot + "assets/images/triangle-opened.png";
2148 a.removeClass("closed");
2149 a.addClass("opened");
2150 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2151 list.style.display = "block";
2152 summary.style.display = "none";
2153 trigger.src = toRoot + "assets/images/triangle-closed.png";
2154 a.removeClass("opened");
2155 a.addClass("closed");
2156 }
2157 return false;
2158}
2159
2160/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2161 * @param linkObj The link that was clicked.
2162 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2163 * 'null' to simply toggle.
2164 */
2165function toggleAllInherited(linkObj, expand) {
2166 var a = $(linkObj);
2167 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2168 var expandos = $(".jd-expando-trigger", table);
2169 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2170 expandos.each(function(i) {
2171 toggleInherited(this, true);
2172 });
2173 a.text("[Collapse]");
2174 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2175 expandos.each(function(i) {
2176 toggleInherited(this, false);
2177 });
2178 a.text("[Expand]");
2179 }
2180 return false;
2181}
2182
2183/* Toggle all inherited members in the class (link in the class title)
2184 */
2185function toggleAllClassInherited() {
2186 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2187 var toggles = $(".toggle-all", $("#body-content"));
2188 if (a.text() == "[Expand All]") {
2189 toggles.each(function(i) {
2190 toggleAllInherited(this, true);
2191 });
2192 a.text("[Collapse All]");
2193 } else {
2194 toggles.each(function(i) {
2195 toggleAllInherited(this, false);
2196 });
2197 a.text("[Expand All]");
2198 }
2199 return false;
2200}
2201
2202/* Expand all inherited members in the class. Used when initiating page search */
2203function ensureAllInheritedExpanded() {
2204 var toggles = $(".toggle-all", $("#body-content"));
2205 toggles.each(function(i) {
2206 toggleAllInherited(this, true);
2207 });
2208 $("#toggleAllClassInherited").text("[Collapse All]");
2209}
2210
2211
2212/* HANDLE KEY EVENTS
2213 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2214 */
2215var agent = navigator['userAgent'].toLowerCase();
2216var mac = agent.indexOf("macintosh") != -1;
2217
2218$(document).keydown( function(e) {
2219var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2220 if (control && e.which == 70) { // 70 is "F"
2221 ensureAllInheritedExpanded();
2222 }
2223});
2224
2225
2226