Home | History | Annotate | Download | only in 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').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   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 :
    156 false; // 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
    198       $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
    199 
    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');
    203       } 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());
    207       }
    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 
    453 function 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 
    479 function setNavBarLeftPos() {
    480   navBarLeftPos = $('#body-content').offset().left;
    481 }
    482 
    483 
    484 function 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
    498 function 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 
    510 var agent = navigator['userAgent'].toLowerCase();
    511 // If a mobile phone, set flag and do mobile setup
    512 if ((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.
    521 Loading this in the head was slowing page load time */
    522 addLoadEvent( 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 
    530 addLoadEvent( function() {
    531   $("pre:not(.no-pretty-print)").addClass("prettyprint");
    532   prettyPrint();
    533 } );
    534 
    535 function 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 
    562 function 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 
    628 var updateScrollbars = false;
    629 var 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  */
    636 function 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. */
    657 function 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 */
    669 function 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 
    677 function 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. */
    695 function 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 */
    719 function 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 
    732 function 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 
    749 function 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 
    790 REMEMBER THE PREVIOUS PAGE FOR EACH TAB
    791 
    792 function 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 
    833 function 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 
    857 function 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 
    899 function getNavPref() {
    900   var v = readCookie('reference_nav');
    901   if (v != NAV_PREF_TREE) {
    902     v = NAV_PREF_PANELS;
    903   }
    904   return v;
    905 }
    906 
    907 function 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 
    917 function 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 
    951 /* ############################################ */
    952 /* ##########     LOCALIZATION     ############ */
    953 /* ############################################ */
    954 
    955 function 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 
    968 function 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 
    982 function changeNavLang(lang) {
    983   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"))
    989     }
    990   });
    991 }
    992 
    993 function changeDocLang(lang) {
    994   changeNavLang(lang);
    995 }
    996 
    997 function 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 
   1010 function loadLangPref() {
   1011   var lang = readCookie("pref_lang");
   1012   if (lang != 0) {
   1013     $("#language").find("option[value='"+lang+"']").attr("selected",true);
   1014   }
   1015 }
   1016 
   1017 function 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 */
   1034 function 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 }
   1053 
   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 
   1354 var gSelectedIndex = -1;
   1355 var gSelectedID = -1;
   1356 var gMatches = new Array();
   1357 var gLastText = "";
   1358 var ROW_COUNT = 20;
   1359 var gInitialized = false;
   1360 
   1361 function 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 
   1370 function 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 
   1377 function 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 
   1436 function 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 
   1509 function 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 
   1579 function 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 
   1594 function 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 
   1604 function 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 
   1613 function 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 
   1631 google.load('search', '1');
   1632 var searchControl;
   1633 
   1634 function 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 
   1707 google.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 
   1741 function 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
   1751 function 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 
   1774 function 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 */
   1781 function 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 
   1796 /* Initialize some droiddoc stuff, but only if we're in the reference */
   1797 if (location.pathname.indexOf("/reference") == 0) {
   1798   $(document).ready(function() {
   1799     // init available apis based on user pref
   1800     changeApiLevel();
   1801     initSidenavHeightResize()
   1802   });
   1803 }
   1804 
   1805 var API_LEVEL_COOKIE = "api_level";
   1806 var minLevel = 1;
   1807 var 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   
   1826 function 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 
   1837 function 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 
   1848 function 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 
   1872 function 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 
   1896 function 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 
   1931 function 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 (a] 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 
   2006 function 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 
   2027 function 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 
   2037 function 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 
   2064 function 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 
   2083 function 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 
   2090 function 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 
   2098 function 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  */
   2138 function 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  */
   2165 function 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  */
   2185 function 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 */
   2203 function 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  */
   2215 var agent = navigator['userAgent'].toLowerCase();
   2216 var mac = agent.indexOf("macintosh") != -1;
   2217 
   2218 $(document).keydown( function(e) {
   2219 var 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 
   2227