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