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