docs.js revision df08adae5b003bfb056341254c35a16ec9295afe
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  googleSearcher = new google.search.WebSearch();
1725  googleSearcher.setUserDefinedLabel("Google Services");
1726  googleSearcher.setSiteRestriction("http://developer.android.com/google/");
1727
1728  blogSearcher = new google.search.WebSearch();
1729  blogSearcher.setUserDefinedLabel("Blog");
1730  blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
1731
1732  // add each searcher to the search control
1733  searchControl.addSearcher(devSiteSearcher, searchOptions);
1734  searchControl.addSearcher(designSearcher, searchOptions);
1735  searchControl.addSearcher(trainingSearcher, searchOptions);
1736  searchControl.addSearcher(guidesSearcher, searchOptions);
1737  searchControl.addSearcher(referenceSearcher, searchOptions);
1738  searchControl.addSearcher(googleSearcher, searchOptions);
1739  searchControl.addSearcher(blogSearcher, searchOptions);
1740
1741  // configure result options
1742  searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
1743  searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
1744  searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
1745  searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
1746
1747  // upon ajax search, refresh the url and search title
1748  searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
1749    updateResultTitle(query);
1750    var query = document.getElementById('search_autocomplete').value;
1751    location.hash = 'q=' + query;
1752  });
1753
1754  // draw the search results box
1755  searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
1756
1757  // get query and execute the search
1758  searchControl.execute(decodeURI(getQuery(location.hash)));
1759
1760  document.getElementById("search_autocomplete").focus();
1761  addTabListeners();
1762}
1763// End of loadSearchResults
1764
1765
1766google.setOnLoadCallback(function(){
1767  if (location.hash.indexOf("q=") == -1) {
1768    // if there's no query in the url, don't search and make sure results are hidden
1769    $('#searchResults').hide();
1770    return;
1771  } else {
1772    // first time loading search results for this page
1773    $('#searchResults').slideDown('slow');
1774    $(".search .close").removeClass("hide");
1775    loadSearchResults();
1776  }
1777}, true);
1778
1779// when an event on the browser history occurs (back, forward, load) requery hash and do search
1780$(window).hashchange( function(){
1781  // Exit if the hash isn't a search query or there's an error in the query
1782  if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
1783    // If the results pane is open, close it.
1784    if (!$("#searchResults").is(":hidden")) {
1785      hideResults();
1786    }
1787    return;
1788  }
1789
1790  // Otherwise, we have a search to do
1791  var query = decodeURI(getQuery(location.hash));
1792  searchControl.execute(query);
1793  $('#searchResults').slideDown('slow');
1794  $("#search_autocomplete").focus();
1795  $(".search .close").removeClass("hide");
1796
1797  updateResultTitle(query);
1798});
1799
1800function updateResultTitle(query) {
1801  $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
1802}
1803
1804// forcefully regain key-up event control (previously jacked by search api)
1805$("#search_autocomplete").keyup(function(event) {
1806  return search_changed(event, false, toRoot);
1807});
1808
1809// add event listeners to each tab so we can track the browser history
1810function addTabListeners() {
1811  var tabHeaders = $(".gsc-tabHeader");
1812  for (var i = 0; i < tabHeaders.length; i++) {
1813    $(tabHeaders[i]).attr("id",i).click(function() {
1814    /*
1815      // make a copy of the page numbers for the search left pane
1816      setTimeout(function() {
1817        // remove any residual page numbers
1818        $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
1819        // move the page numbers to the left position; make a clone,
1820        // because the element is drawn to the DOM only once
1821        // and because we're going to remove it (previous line),
1822        // we need it to be available to move again as the user navigates
1823        $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
1824                        .clone().appendTo('#searchResults .gsc-tabsArea');
1825        }, 200);
1826      */
1827    });
1828  }
1829  setTimeout(function(){$(tabHeaders[0]).click()},200);
1830}
1831
1832
1833function getQuery(hash) {
1834  var queryParts = hash.split('=');
1835  return queryParts[1];
1836}
1837
1838/* returns the given string with all HTML brackets converted to entities
1839    TODO: move this to the site's JS library */
1840function escapeHTML(string) {
1841  return string.replace(/</g,"&lt;")
1842                .replace(/>/g,"&gt;");
1843}
1844
1845
1846
1847
1848
1849
1850
1851/* ######################################################## */
1852/* #################  JAVADOC REFERENCE ################### */
1853/* ######################################################## */
1854
1855/* Initialize some droiddoc stuff, but only if we're in the reference */
1856if ((location.pathname.indexOf("/reference") &&
1857  !location.pathname.indexOf("/reference-gms/packages.html") &&
1858  !location.pathname.indexOf("/reference-gcm/packages.html") &&
1859  !location.pathname.indexOf("/reference/com/google")) == 0) {
1860  $(document).ready(function() {
1861    // init available apis based on user pref
1862    changeApiLevel();
1863    initSidenavHeightResize()
1864  });
1865}
1866
1867var API_LEVEL_COOKIE = "api_level";
1868var minLevel = 1;
1869var maxLevel = 1;
1870
1871/******* SIDENAV DIMENSIONS ************/
1872
1873  function initSidenavHeightResize() {
1874    // Change the drag bar size to nicely fit the scrollbar positions
1875    var $dragBar = $(".ui-resizable-s");
1876    $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1877
1878    $( "#resize-packages-nav" ).resizable({
1879      containment: "#nav-panels",
1880      handles: "s",
1881      alsoResize: "#packages-nav",
1882      resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1883      stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie  */
1884      });
1885
1886  }
1887
1888function updateSidenavFixedWidth() {
1889  if (!navBarIsFixed) return;
1890  $('#devdoc-nav').css({
1891    'width' : $('#side-nav').css('width'),
1892    'margin' : $('#side-nav').css('margin')
1893  });
1894  $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1895
1896  initSidenavHeightResize();
1897}
1898
1899function updateSidenavFullscreenWidth() {
1900  if (!navBarIsFixed) return;
1901  $('#devdoc-nav').css({
1902    'width' : $('#side-nav').css('width'),
1903    'margin' : $('#side-nav').css('margin')
1904  });
1905  $('#devdoc-nav .totop').css({'left': 'inherit'});
1906
1907  initSidenavHeightResize();
1908}
1909
1910function buildApiLevelSelector() {
1911  maxLevel = SINCE_DATA.length;
1912  var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1913  userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1914
1915  minLevel = parseInt($("#doc-api-level").attr("class"));
1916  // Handle provisional api levels; the provisional level will always be the highest possible level
1917  // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1918  // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1919  if (isNaN(minLevel) && minLevel.length) {
1920    minLevel = maxLevel;
1921  }
1922  var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1923  for (var i = maxLevel-1; i >= 0; i--) {
1924    var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1925  //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1926    select.append(option);
1927  }
1928
1929  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1930  var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1931  selectedLevelItem.setAttribute('selected',true);
1932}
1933
1934function changeApiLevel() {
1935  maxLevel = SINCE_DATA.length;
1936  var selectedLevel = maxLevel;
1937
1938  selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1939  toggleVisisbleApis(selectedLevel, "body");
1940
1941  var date = new Date();
1942  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1943  var expiration = date.toGMTString();
1944  writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1945
1946  if (selectedLevel < minLevel) {
1947    var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
1948    $("#naMessage").show().html("<div><p><strong>This " + thing
1949              + " requires API level " + minLevel + " or higher.</strong></p>"
1950              + "<p>This document is hidden because your selected API level for the documentation is "
1951              + selectedLevel + ". You can change the documentation API level with the selector "
1952              + "above the left navigation.</p>"
1953              + "<p>For more information about specifying the API level your app requires, "
1954              + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
1955              + ">Supporting Different Platform Versions</a>.</p>"
1956              + "<input type='button' value='OK, make this page visible' "
1957              + "title='Change the API level to " + minLevel + "' "
1958              + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
1959              + "</div>");
1960  } else {
1961    $("#naMessage").hide();
1962  }
1963}
1964
1965function toggleVisisbleApis(selectedLevel, context) {
1966  var apis = $(".api",context);
1967  apis.each(function(i) {
1968    var obj = $(this);
1969    var className = obj.attr("class");
1970    var apiLevelIndex = className.lastIndexOf("-")+1;
1971    var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1972    apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1973    var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
1974    if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
1975      return;
1976    }
1977    apiLevel = parseInt(apiLevel);
1978
1979    // Handle provisional api levels; if this item's level is the provisional one, set it to the max
1980    var selectedLevelNum = parseInt(selectedLevel)
1981    var apiLevelNum = parseInt(apiLevel);
1982    if (isNaN(apiLevelNum)) {
1983        apiLevelNum = maxLevel;
1984    }
1985
1986    // Grey things out that aren't available and give a tooltip title
1987    if (apiLevelNum > selectedLevelNum) {
1988      obj.addClass("absent").attr("title","Requires API Level \""
1989            + apiLevel + "\" or higher");
1990    }
1991    else obj.removeClass("absent").removeAttr("title");
1992  });
1993}
1994
1995
1996
1997
1998/* #################  SIDENAV TREE VIEW ################### */
1999
2000function new_node(me, mom, text, link, children_data, api_level)
2001{
2002  var node = new Object();
2003  node.children = Array();
2004  node.children_data = children_data;
2005  node.depth = mom.depth + 1;
2006
2007  node.li = document.createElement("li");
2008  mom.get_children_ul().appendChild(node.li);
2009
2010  node.label_div = document.createElement("div");
2011  node.label_div.className = "label";
2012  if (api_level != null) {
2013    $(node.label_div).addClass("api");
2014    $(node.label_div).addClass("api-level-"+api_level);
2015  }
2016  node.li.appendChild(node.label_div);
2017
2018  if (children_data != null) {
2019    node.expand_toggle = document.createElement("a");
2020    node.expand_toggle.href = "javascript:void(0)";
2021    node.expand_toggle.onclick = function() {
2022          if (node.expanded) {
2023            $(node.get_children_ul()).slideUp("fast");
2024            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2025            node.expanded = false;
2026          } else {
2027            expand_node(me, node);
2028          }
2029       };
2030    node.label_div.appendChild(node.expand_toggle);
2031
2032    node.plus_img = document.createElement("img");
2033    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2034    node.plus_img.className = "plus";
2035    node.plus_img.width = "8";
2036    node.plus_img.border = "0";
2037    node.expand_toggle.appendChild(node.plus_img);
2038
2039    node.expanded = false;
2040  }
2041
2042  var a = document.createElement("a");
2043  node.label_div.appendChild(a);
2044  node.label = document.createTextNode(text);
2045  a.appendChild(node.label);
2046  if (link) {
2047    a.href = me.toroot + link;
2048  } else {
2049    if (children_data != null) {
2050      a.className = "nolink";
2051      a.href = "javascript:void(0)";
2052      a.onclick = node.expand_toggle.onclick;
2053      // This next line shouldn't be necessary.  I'll buy a beer for the first
2054      // person who figures out how to remove this line and have the link
2055      // toggle shut on the first try. --joeo@android.com
2056      node.expanded = false;
2057    }
2058  }
2059
2060
2061  node.children_ul = null;
2062  node.get_children_ul = function() {
2063      if (!node.children_ul) {
2064        node.children_ul = document.createElement("ul");
2065        node.children_ul.className = "children_ul";
2066        node.children_ul.style.display = "none";
2067        node.li.appendChild(node.children_ul);
2068      }
2069      return node.children_ul;
2070    };
2071
2072  return node;
2073}
2074
2075
2076
2077
2078function expand_node(me, node)
2079{
2080  if (node.children_data && !node.expanded) {
2081    if (node.children_visited) {
2082      $(node.get_children_ul()).slideDown("fast");
2083    } else {
2084      get_node(me, node);
2085      if ($(node.label_div).hasClass("absent")) {
2086        $(node.get_children_ul()).addClass("absent");
2087      }
2088      $(node.get_children_ul()).slideDown("fast");
2089    }
2090    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2091    node.expanded = true;
2092
2093    // perform api level toggling because new nodes are new to the DOM
2094    var selectedLevel = $("#apiLevelSelector option:selected").val();
2095    toggleVisisbleApis(selectedLevel, "#side-nav");
2096  }
2097}
2098
2099function get_node(me, mom)
2100{
2101  mom.children_visited = true;
2102  for (var i in mom.children_data) {
2103    var node_data = mom.children_data[i];
2104    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2105        node_data[2], node_data[3]);
2106  }
2107}
2108
2109function this_page_relative(toroot)
2110{
2111  var full = document.location.pathname;
2112  var file = "";
2113  if (toroot.substr(0, 1) == "/") {
2114    if (full.substr(0, toroot.length) == toroot) {
2115      return full.substr(toroot.length);
2116    } else {
2117      // the file isn't under toroot.  Fail.
2118      return null;
2119    }
2120  } else {
2121    if (toroot != "./") {
2122      toroot = "./" + toroot;
2123    }
2124    do {
2125      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2126        var pos = full.lastIndexOf("/");
2127        file = full.substr(pos) + file;
2128        full = full.substr(0, pos);
2129        toroot = toroot.substr(0, toroot.length-3);
2130      }
2131    } while (toroot != "" && toroot != "/");
2132    return file.substr(1);
2133  }
2134}
2135
2136function find_page(url, data)
2137{
2138  var nodes = data;
2139  var result = null;
2140  for (var i in nodes) {
2141    var d = nodes[i];
2142    if (d[1] == url) {
2143      return new Array(i);
2144    }
2145    else if (d[2] != null) {
2146      result = find_page(url, d[2]);
2147      if (result != null) {
2148        return (new Array(i).concat(result));
2149      }
2150    }
2151  }
2152  return null;
2153}
2154
2155function init_default_navtree(toroot) {
2156  init_navtree("tree-list", toroot, NAVTREE_DATA);
2157
2158  // perform api level toggling because because the whole tree is new to the DOM
2159  var selectedLevel = $("#apiLevelSelector option:selected").val();
2160  toggleVisisbleApis(selectedLevel, "#side-nav");
2161}
2162
2163function init_navtree(navtree_id, toroot, root_nodes)
2164{
2165  var me = new Object();
2166  me.toroot = toroot;
2167  me.node = new Object();
2168
2169  me.node.li = document.getElementById(navtree_id);
2170  me.node.children_data = root_nodes;
2171  me.node.children = new Array();
2172  me.node.children_ul = document.createElement("ul");
2173  me.node.get_children_ul = function() { return me.node.children_ul; };
2174  //me.node.children_ul.className = "children_ul";
2175  me.node.li.appendChild(me.node.children_ul);
2176  me.node.depth = 0;
2177
2178  get_node(me, me.node);
2179
2180  me.this_page = this_page_relative(toroot);
2181  me.breadcrumbs = find_page(me.this_page, root_nodes);
2182  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2183    var mom = me.node;
2184    for (var i in me.breadcrumbs) {
2185      var j = me.breadcrumbs[i];
2186      mom = mom.children[j];
2187      expand_node(me, mom);
2188    }
2189    mom.label_div.className = mom.label_div.className + " selected";
2190    addLoadEvent(function() {
2191      scrollIntoView("nav-tree");
2192      });
2193  }
2194}
2195
2196/* TODO: eliminate redundancy with non-google functions */
2197function init_google_navtree(navtree_id, toroot, root_nodes)
2198{
2199  var me = new Object();
2200  me.toroot = toroot;
2201  me.node = new Object();
2202
2203  me.node.li = document.getElementById(navtree_id);
2204  me.node.children_data = root_nodes;
2205  me.node.children = new Array();
2206  me.node.children_ul = document.createElement("ul");
2207  me.node.get_children_ul = function() { return me.node.children_ul; };
2208  //me.node.children_ul.className = "children_ul";
2209  me.node.li.appendChild(me.node.children_ul);
2210  me.node.depth = 0;
2211
2212  get_google_node(me, me.node);
2213
2214}
2215
2216function new_google_node(me, mom, text, link, children_data, api_level)
2217{
2218  var node = new Object();
2219  var child;
2220  node.children = Array();
2221  node.children_data = children_data;
2222  node.depth = mom.depth + 1;
2223  node.get_children_ul = function() {
2224      if (!node.children_ul) {
2225        node.children_ul = document.createElement("ul");
2226        node.children_ul.className = "tree-list-children";
2227        node.li.appendChild(node.children_ul);
2228      }
2229      return node.children_ul;
2230    };
2231  node.li = document.createElement("li");
2232
2233  mom.get_children_ul().appendChild(node.li);
2234
2235
2236  if(link) {
2237    child = document.createElement("a");
2238
2239  }
2240  else {
2241    child = document.createElement("span");
2242    child.className = "tree-list-subtitle";
2243
2244  }
2245  if (children_data != null) {
2246    node.li.className="nav-section";
2247    node.label_div = document.createElement("div");
2248    node.label_div.className = "nav-section-header-ref";
2249    node.li.appendChild(node.label_div);
2250    get_google_node(me, node);
2251    node.label_div.appendChild(child);
2252  }
2253  else {
2254    node.li.appendChild(child);
2255  }
2256  if(link) {
2257    child.href = me.toroot + link;
2258  }
2259  node.label = document.createTextNode(text);
2260  child.appendChild(node.label);
2261
2262  node.children_ul = null;
2263
2264  return node;
2265}
2266
2267function get_google_node(me, mom)
2268{
2269  mom.children_visited = true;
2270  var linkText;
2271  for (var i in mom.children_data) {
2272    var node_data = mom.children_data[i];
2273    linkText = node_data[0];
2274
2275    if(linkText.match("^"+"com.google.android")=="com.google.android"){
2276      linkText = linkText.substr(19, linkText.length);
2277    }
2278      mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
2279          node_data[2], node_data[3]);
2280  }
2281}
2282function showGoogleRefTree() {
2283  init_default_google_navtree(toRoot);
2284  init_default_gcm_navtree(toRoot);
2285  resizeNav();
2286}
2287
2288function init_default_google_navtree(toroot) {
2289  init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
2290}
2291
2292function init_default_gcm_navtree(toroot) {
2293  init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
2294}
2295
2296/* TOGGLE INHERITED MEMBERS */
2297
2298/* Toggle an inherited class (arrow toggle)
2299 * @param linkObj  The link that was clicked.
2300 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
2301 *                'null' to simply toggle.
2302 */
2303function toggleInherited(linkObj, expand) {
2304    var base = linkObj.getAttribute("id");
2305    var list = document.getElementById(base + "-list");
2306    var summary = document.getElementById(base + "-summary");
2307    var trigger = document.getElementById(base + "-trigger");
2308    var a = $(linkObj);
2309    if ( (expand == null && a.hasClass("closed")) || expand ) {
2310        list.style.display = "none";
2311        summary.style.display = "block";
2312        trigger.src = toRoot + "assets/images/triangle-opened.png";
2313        a.removeClass("closed");
2314        a.addClass("opened");
2315    } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2316        list.style.display = "block";
2317        summary.style.display = "none";
2318        trigger.src = toRoot + "assets/images/triangle-closed.png";
2319        a.removeClass("opened");
2320        a.addClass("closed");
2321    }
2322    return false;
2323}
2324
2325/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2326 * @param linkObj  The link that was clicked.
2327 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
2328 *                'null' to simply toggle.
2329 */
2330function toggleAllInherited(linkObj, expand) {
2331  var a = $(linkObj);
2332  var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2333  var expandos = $(".jd-expando-trigger", table);
2334  if ( (expand == null && a.text() == "[Expand]") || expand ) {
2335    expandos.each(function(i) {
2336      toggleInherited(this, true);
2337    });
2338    a.text("[Collapse]");
2339  } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2340    expandos.each(function(i) {
2341      toggleInherited(this, false);
2342    });
2343    a.text("[Expand]");
2344  }
2345  return false;
2346}
2347
2348/* Toggle all inherited members in the class (link in the class title)
2349 */
2350function toggleAllClassInherited() {
2351  var a = $("#toggleAllClassInherited"); // get toggle link from class title
2352  var toggles = $(".toggle-all", $("#body-content"));
2353  if (a.text() == "[Expand All]") {
2354    toggles.each(function(i) {
2355      toggleAllInherited(this, true);
2356    });
2357    a.text("[Collapse All]");
2358  } else {
2359    toggles.each(function(i) {
2360      toggleAllInherited(this, false);
2361    });
2362    a.text("[Expand All]");
2363  }
2364  return false;
2365}
2366
2367/* Expand all inherited members in the class. Used when initiating page search */
2368function ensureAllInheritedExpanded() {
2369  var toggles = $(".toggle-all", $("#body-content"));
2370  toggles.each(function(i) {
2371    toggleAllInherited(this, true);
2372  });
2373  $("#toggleAllClassInherited").text("[Collapse All]");
2374}
2375
2376
2377/* HANDLE KEY EVENTS
2378 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2379 */
2380var agent = navigator['userAgent'].toLowerCase();
2381var mac = agent.indexOf("macintosh") != -1;
2382
2383$(document).keydown( function(e) {
2384var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2385  if (control && e.which == 70) {  // 70 is "F"
2386    ensureAllInheritedExpanded();
2387  }
2388});
2389