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