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