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