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