docs.js revision ac71b2b59bae525502ae0b7af93b5f3a9f569b47
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  !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
1862var API_LEVEL_COOKIE = "api_level";
1863var minLevel = 1;
1864var maxLevel = 1;
1865
1866/******* SIDENAV DIMENSIONS ************/
1867
1868  function initSidenavHeightResize() {
1869    // Change the drag bar size to nicely fit the scrollbar positions
1870    var $dragBar = $(".ui-resizable-s");
1871    $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1872
1873    $( "#resize-packages-nav" ).resizable({
1874      containment: "#nav-panels",
1875      handles: "s",
1876      alsoResize: "#packages-nav",
1877      resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1878      stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie  */
1879      });
1880
1881  }
1882
1883function updateSidenavFixedWidth() {
1884  if (!navBarIsFixed) return;
1885  $('#devdoc-nav').css({
1886    'width' : $('#side-nav').css('width'),
1887    'margin' : $('#side-nav').css('margin')
1888  });
1889  $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1890
1891  initSidenavHeightResize();
1892}
1893
1894function updateSidenavFullscreenWidth() {
1895  if (!navBarIsFixed) return;
1896  $('#devdoc-nav').css({
1897    'width' : $('#side-nav').css('width'),
1898    'margin' : $('#side-nav').css('margin')
1899  });
1900  $('#devdoc-nav .totop').css({'left': 'inherit'});
1901
1902  initSidenavHeightResize();
1903}
1904
1905function buildApiLevelSelector() {
1906  maxLevel = SINCE_DATA.length;
1907  var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1908  userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1909
1910  minLevel = parseInt($("#doc-api-level").attr("class"));
1911  // Handle provisional api levels; the provisional level will always be the highest possible level
1912  // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1913  // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1914  if (isNaN(minLevel) && minLevel.length) {
1915    minLevel = maxLevel;
1916  }
1917  var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1918  for (var i = maxLevel-1; i >= 0; i--) {
1919    var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1920  //  if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1921    select.append(option);
1922  }
1923
1924  // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1925  var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1926  selectedLevelItem.setAttribute('selected',true);
1927}
1928
1929function changeApiLevel() {
1930  maxLevel = SINCE_DATA.length;
1931  var selectedLevel = maxLevel;
1932
1933  selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1934  toggleVisisbleApis(selectedLevel, "body");
1935
1936  var date = new Date();
1937  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1938  var expiration = date.toGMTString();
1939  writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1940
1941  if (selectedLevel < minLevel) {
1942    var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
1943    $("#naMessage").show().html("<div><p><strong>This " + thing
1944              + " requires API level " + minLevel + " or higher.</strong></p>"
1945              + "<p>This document is hidden because your selected API level for the documentation is "
1946              + selectedLevel + ". You can change the documentation API level with the selector "
1947              + "above the left navigation.</p>"
1948              + "<p>For more information about specifying the API level your app requires, "
1949              + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
1950              + ">Supporting Different Platform Versions</a>.</p>"
1951              + "<input type='button' value='OK, make this page visible' "
1952              + "title='Change the API level to " + minLevel + "' "
1953              + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
1954              + "</div>");
1955  } else {
1956    $("#naMessage").hide();
1957  }
1958}
1959
1960function toggleVisisbleApis(selectedLevel, context) {
1961  var apis = $(".api",context);
1962  apis.each(function(i) {
1963    var obj = $(this);
1964    var className = obj.attr("class");
1965    var apiLevelIndex = className.lastIndexOf("-")+1;
1966    var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1967    apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1968    var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
1969    if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
1970      return;
1971    }
1972    apiLevel = parseInt(apiLevel);
1973
1974    // Handle provisional api levels; if this item's level is the provisional one, set it to the max
1975    var selectedLevelNum = parseInt(selectedLevel)
1976    var apiLevelNum = parseInt(apiLevel);
1977    if (isNaN(apiLevelNum)) {
1978        apiLevelNum = maxLevel;
1979    }
1980
1981    // Grey things out that aren't available and give a tooltip title
1982    if (apiLevelNum > selectedLevelNum) {
1983      obj.addClass("absent").attr("title","Requires API Level \""
1984            + apiLevel + "\" or higher");
1985    }
1986    else obj.removeClass("absent").removeAttr("title");
1987  });
1988}
1989
1990
1991
1992
1993/* #################  SIDENAV TREE VIEW ################### */
1994
1995function new_node(me, mom, text, link, children_data, api_level)
1996{
1997  var node = new Object();
1998  node.children = Array();
1999  node.children_data = children_data;
2000  node.depth = mom.depth + 1;
2001
2002  node.li = document.createElement("li");
2003  mom.get_children_ul().appendChild(node.li);
2004
2005  node.label_div = document.createElement("div");
2006  node.label_div.className = "label";
2007  if (api_level != null) {
2008    $(node.label_div).addClass("api");
2009    $(node.label_div).addClass("api-level-"+api_level);
2010  }
2011  node.li.appendChild(node.label_div);
2012
2013  if (children_data != null) {
2014    node.expand_toggle = document.createElement("a");
2015    node.expand_toggle.href = "javascript:void(0)";
2016    node.expand_toggle.onclick = function() {
2017          if (node.expanded) {
2018            $(node.get_children_ul()).slideUp("fast");
2019            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2020            node.expanded = false;
2021          } else {
2022            expand_node(me, node);
2023          }
2024       };
2025    node.label_div.appendChild(node.expand_toggle);
2026
2027    node.plus_img = document.createElement("img");
2028    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2029    node.plus_img.className = "plus";
2030    node.plus_img.width = "8";
2031    node.plus_img.border = "0";
2032    node.expand_toggle.appendChild(node.plus_img);
2033
2034    node.expanded = false;
2035  }
2036
2037  var a = document.createElement("a");
2038  node.label_div.appendChild(a);
2039  node.label = document.createTextNode(text);
2040  a.appendChild(node.label);
2041  if (link) {
2042    a.href = me.toroot + link;
2043  } else {
2044    if (children_data != null) {
2045      a.className = "nolink";
2046      a.href = "javascript:void(0)";
2047      a.onclick = node.expand_toggle.onclick;
2048      // This next line shouldn't be necessary.  I'll buy a beer for the first
2049      // person who figures out how to remove this line and have the link
2050      // toggle shut on the first try. --joeo@android.com
2051      node.expanded = false;
2052    }
2053  }
2054
2055
2056  node.children_ul = null;
2057  node.get_children_ul = function() {
2058      if (!node.children_ul) {
2059        node.children_ul = document.createElement("ul");
2060        node.children_ul.className = "children_ul";
2061        node.children_ul.style.display = "none";
2062        node.li.appendChild(node.children_ul);
2063      }
2064      return node.children_ul;
2065    };
2066
2067  return node;
2068}
2069
2070
2071
2072
2073function expand_node(me, node)
2074{
2075  if (node.children_data && !node.expanded) {
2076    if (node.children_visited) {
2077      $(node.get_children_ul()).slideDown("fast");
2078    } else {
2079      get_node(me, node);
2080      if ($(node.label_div).hasClass("absent")) {
2081        $(node.get_children_ul()).addClass("absent");
2082      }
2083      $(node.get_children_ul()).slideDown("fast");
2084    }
2085    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2086    node.expanded = true;
2087
2088    // perform api level toggling because new nodes are new to the DOM
2089    var selectedLevel = $("#apiLevelSelector option:selected").val();
2090    toggleVisisbleApis(selectedLevel, "#side-nav");
2091  }
2092}
2093
2094function get_node(me, mom)
2095{
2096  mom.children_visited = true;
2097  for (var i in mom.children_data) {
2098    var node_data = mom.children_data[i];
2099    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2100        node_data[2], node_data[3]);
2101  }
2102}
2103
2104function this_page_relative(toroot)
2105{
2106  var full = document.location.pathname;
2107  var file = "";
2108  if (toroot.substr(0, 1) == "/") {
2109    if (full.substr(0, toroot.length) == toroot) {
2110      return full.substr(toroot.length);
2111    } else {
2112      // the file isn't under toroot.  Fail.
2113      return null;
2114    }
2115  } else {
2116    if (toroot != "./") {
2117      toroot = "./" + toroot;
2118    }
2119    do {
2120      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2121        var pos = full.lastIndexOf("/");
2122        file = full.substr(pos) + file;
2123        full = full.substr(0, pos);
2124        toroot = toroot.substr(0, toroot.length-3);
2125      }
2126    } while (toroot != "" && toroot != "/");
2127    return file.substr(1);
2128  }
2129}
2130
2131function find_page(url, data)
2132{
2133  var nodes = data;
2134  var result = null;
2135  for (var i in nodes) {
2136    var d = nodes[i];
2137    if (d[1] == url) {
2138      return new Array(i);
2139    }
2140    else if (d[2] != null) {
2141      result = find_page(url, d[2]);
2142      if (result != null) {
2143        return (new Array(i).concat(result));
2144      }
2145    }
2146  }
2147  return null;
2148}
2149
2150function init_default_navtree(toroot) {
2151  init_navtree("tree-list", toroot, NAVTREE_DATA);
2152
2153  // perform api level toggling because because the whole tree is new to the DOM
2154  var selectedLevel = $("#apiLevelSelector option:selected").val();
2155  toggleVisisbleApis(selectedLevel, "#side-nav");
2156}
2157
2158function init_navtree(navtree_id, toroot, root_nodes)
2159{
2160  var me = new Object();
2161  me.toroot = toroot;
2162  me.node = new Object();
2163
2164  me.node.li = document.getElementById(navtree_id);
2165  me.node.children_data = root_nodes;
2166  me.node.children = new Array();
2167  me.node.children_ul = document.createElement("ul");
2168  me.node.get_children_ul = function() { return me.node.children_ul; };
2169  //me.node.children_ul.className = "children_ul";
2170  me.node.li.appendChild(me.node.children_ul);
2171  me.node.depth = 0;
2172
2173  get_node(me, me.node);
2174
2175  me.this_page = this_page_relative(toroot);
2176  me.breadcrumbs = find_page(me.this_page, root_nodes);
2177  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2178    var mom = me.node;
2179    for (var i in me.breadcrumbs) {
2180      var j = me.breadcrumbs[i];
2181      mom = mom.children[j];
2182      expand_node(me, mom);
2183    }
2184    mom.label_div.className = mom.label_div.className + " selected";
2185    addLoadEvent(function() {
2186      scrollIntoView("nav-tree");
2187      });
2188  }
2189}
2190
2191/* TODO: eliminate redundancy with non-google functions */
2192function init_google_navtree(navtree_id, toroot, root_nodes)
2193{
2194  var me = new Object();
2195  me.toroot = toroot;
2196  me.node = new Object();
2197
2198  me.node.li = document.getElementById(navtree_id);
2199  me.node.children_data = root_nodes;
2200  me.node.children = new Array();
2201  me.node.children_ul = document.createElement("ul");
2202  me.node.get_children_ul = function() { return me.node.children_ul; };
2203  //me.node.children_ul.className = "children_ul";
2204  me.node.li.appendChild(me.node.children_ul);
2205  me.node.depth = 0;
2206
2207  get_google_node(me, me.node);
2208
2209}
2210
2211function new_google_node(me, mom, text, link, children_data, api_level)
2212{
2213  var node = new Object();
2214  var child;
2215  node.children = Array();
2216  node.children_data = children_data;
2217  node.depth = mom.depth + 1;
2218  node.get_children_ul = function() {
2219      if (!node.children_ul) {
2220        node.children_ul = document.createElement("ul");
2221        node.children_ul.className = "tree-list-children";
2222        node.li.appendChild(node.children_ul);
2223      }
2224      return node.children_ul;
2225    };
2226  node.li = document.createElement("li");
2227
2228  mom.get_children_ul().appendChild(node.li);
2229
2230
2231  if(link) {
2232    child = document.createElement("a");
2233
2234  }
2235  else {
2236    child = document.createElement("span");
2237    child.className = "tree-list-subtitle";
2238
2239  }
2240  if (children_data != null) {
2241    node.li.className="nav-section";
2242    node.label_div = document.createElement("div");
2243    node.label_div.className = "nav-section-header-ref";
2244    node.li.appendChild(node.label_div);
2245    get_google_node(me, node);
2246    node.label_div.appendChild(child);
2247  }
2248  else {
2249    node.li.appendChild(child);
2250  }
2251  if(link) {
2252    child.href = me.toroot + link;
2253  }
2254  node.label = document.createTextNode(text);
2255  child.appendChild(node.label);
2256
2257  node.children_ul = null;
2258
2259  return node;
2260}
2261
2262function get_google_node(me, mom)
2263{
2264  mom.children_visited = true;
2265  var linkText;
2266  for (var i in mom.children_data) {
2267    var node_data = mom.children_data[i];
2268    linkText = node_data[0];
2269
2270    if(linkText.match("^"+"com.google.android")=="com.google.android"){
2271      linkText = linkText.substr(19, linkText.length);
2272    }
2273      mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
2274          node_data[2], node_data[3]);
2275  }
2276}
2277function showGoogleRefTree() {
2278  init_default_google_navtree(toRoot);
2279  init_default_gcm_navtree(toRoot);
2280  resizeNav();
2281}
2282
2283function init_default_google_navtree(toroot) {
2284  init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
2285}
2286
2287function init_default_gcm_navtree(toroot) {
2288  init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
2289}
2290
2291/* TOGGLE INHERITED MEMBERS */
2292
2293/* Toggle an inherited class (arrow toggle)
2294 * @param linkObj  The link that was clicked.
2295 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
2296 *                'null' to simply toggle.
2297 */
2298function toggleInherited(linkObj, expand) {
2299    var base = linkObj.getAttribute("id");
2300    var list = document.getElementById(base + "-list");
2301    var summary = document.getElementById(base + "-summary");
2302    var trigger = document.getElementById(base + "-trigger");
2303    var a = $(linkObj);
2304    if ( (expand == null && a.hasClass("closed")) || expand ) {
2305        list.style.display = "none";
2306        summary.style.display = "block";
2307        trigger.src = toRoot + "assets/images/triangle-opened.png";
2308        a.removeClass("closed");
2309        a.addClass("opened");
2310    } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2311        list.style.display = "block";
2312        summary.style.display = "none";
2313        trigger.src = toRoot + "assets/images/triangle-closed.png";
2314        a.removeClass("opened");
2315        a.addClass("closed");
2316    }
2317    return false;
2318}
2319
2320/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2321 * @param linkObj  The link that was clicked.
2322 * @param expand  'true' to ensure it's expanded. 'false' to ensure it's closed.
2323 *                'null' to simply toggle.
2324 */
2325function toggleAllInherited(linkObj, expand) {
2326  var a = $(linkObj);
2327  var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2328  var expandos = $(".jd-expando-trigger", table);
2329  if ( (expand == null && a.text() == "[Expand]") || expand ) {
2330    expandos.each(function(i) {
2331      toggleInherited(this, true);
2332    });
2333    a.text("[Collapse]");
2334  } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2335    expandos.each(function(i) {
2336      toggleInherited(this, false);
2337    });
2338    a.text("[Expand]");
2339  }
2340  return false;
2341}
2342
2343/* Toggle all inherited members in the class (link in the class title)
2344 */
2345function toggleAllClassInherited() {
2346  var a = $("#toggleAllClassInherited"); // get toggle link from class title
2347  var toggles = $(".toggle-all", $("#body-content"));
2348  if (a.text() == "[Expand All]") {
2349    toggles.each(function(i) {
2350      toggleAllInherited(this, true);
2351    });
2352    a.text("[Collapse All]");
2353  } else {
2354    toggles.each(function(i) {
2355      toggleAllInherited(this, false);
2356    });
2357    a.text("[Expand All]");
2358  }
2359  return false;
2360}
2361
2362/* Expand all inherited members in the class. Used when initiating page search */
2363function ensureAllInheritedExpanded() {
2364  var toggles = $(".toggle-all", $("#body-content"));
2365  toggles.each(function(i) {
2366    toggleAllInherited(this, true);
2367  });
2368  $("#toggleAllClassInherited").text("[Collapse All]");
2369}
2370
2371
2372/* HANDLE KEY EVENTS
2373 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2374 */
2375var agent = navigator['userAgent'].toLowerCase();
2376var mac = agent.indexOf("macintosh") != -1;
2377
2378$(document).keydown( function(e) {
2379var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2380  if (control && e.which == 70) {  // 70 is "F"
2381    ensureAllInheritedExpanded();
2382  }
2383});
2384