OSDN Git Service

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