blob: 43bcb8f84bbe6fdbd93d1028c8b35789368d02b7 [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
Scott Maineb410352013-01-14 19:03:40 -08001147 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001148 * auto: whether or not to auto-proceed
1149 * speed: animation speed
1150 * autoTime: time between auto-rotation
1151 * easing: easing function for transition
1152 * start: item to select by default
1153 * scroll: direction to scroll in
1154 * pagination: whether or not to include dotted pagination
1155 *
1156 */
1157
1158 (function($) {
1159 $.fn.dacSlideshow = function(o) {
1160
1161 //Options - see above
1162 o = $.extend({
1163 btnPrev: null,
1164 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001165 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001166 auto: true,
1167 speed: 500,
1168 autoTime: 12000,
1169 easing: null,
1170 start: 0,
1171 scroll: 1,
1172 pagination: true
1173
1174 }, o || {});
1175
1176 //Set up a carousel for each
1177 return this.each(function() {
1178
1179 var running = false;
1180 var animCss = o.vertical ? "top" : "left";
1181 var sizeCss = o.vertical ? "height" : "width";
1182 var div = $(this);
1183 var ul = $("ul", div);
1184 var tLi = $("li", ul);
1185 var tl = tLi.size();
1186 var timer = null;
1187
1188 var li = $("li", ul);
1189 var itemLength = li.size();
1190 var curr = o.start;
1191
1192 li.css({float: o.vertical ? "none" : "left"});
1193 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1194 div.css({position: "relative", "z-index": "2", left: "0px"});
1195
1196 var liSize = o.vertical ? height(li) : width(li);
1197 var ulSize = liSize * itemLength;
1198 var divSize = liSize;
1199
1200 li.css({width: li.width(), height: li.height()});
1201 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1202
1203 div.css(sizeCss, divSize+"px");
1204
1205 //Pagination
1206 if (o.pagination) {
1207 var pagination = $("<div class='pagination'></div>");
1208 var pag_ul = $("<ul></ul>");
1209 if (tl > 1) {
1210 for (var i=0;i<tl;i++) {
1211 var li = $("<li>"+i+"</li>");
1212 pag_ul.append(li);
1213 if (i==o.start) li.addClass('active');
1214 li.click(function() {
1215 go(parseInt($(this).text()));
1216 })
1217 }
1218 pagination.append(pag_ul);
1219 div.append(pagination);
1220 }
1221 }
1222
1223 //Previous button
1224 if(o.btnPrev)
1225 $(o.btnPrev).click(function(e) {
1226 e.preventDefault();
1227 return go(curr-o.scroll);
1228 });
1229
1230 //Next button
1231 if(o.btnNext)
1232 $(o.btnNext).click(function(e) {
1233 e.preventDefault();
1234 return go(curr+o.scroll);
1235 });
Scott Maineb410352013-01-14 19:03:40 -08001236
1237 //Pause button
1238 if(o.btnPause)
1239 $(o.btnPause).click(function(e) {
1240 e.preventDefault();
1241 if ($(this).hasClass('paused')) {
1242 startRotateTimer();
1243 } else {
1244 pauseRotateTimer();
1245 }
1246 });
Scott Mainf5089842012-08-14 16:31:07 -07001247
1248 //Auto rotation
1249 if(o.auto) startRotateTimer();
1250
1251 function startRotateTimer() {
1252 clearInterval(timer);
1253 timer = setInterval(function() {
1254 if (curr == tl-1) {
1255 go(0);
1256 } else {
1257 go(curr+o.scroll);
1258 }
1259 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001260 $(o.btnPause).removeClass('paused');
1261 }
1262
1263 function pauseRotateTimer() {
1264 clearInterval(timer);
1265 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001266 }
1267
1268 //Go to an item
1269 function go(to) {
1270 if(!running) {
1271
1272 if(to<0) {
1273 to = itemLength-1;
1274 } else if (to>itemLength-1) {
1275 to = 0;
1276 }
1277 curr = to;
1278
1279 running = true;
1280
1281 ul.animate(
1282 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1283 function() {
1284 running = false;
1285 }
1286 );
1287
1288 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1289 $( (curr-o.scroll<0 && o.btnPrev)
1290 ||
1291 (curr+o.scroll > itemLength && o.btnNext)
1292 ||
1293 []
1294 ).addClass("disabled");
1295
1296
1297 var nav_items = $('li', pagination);
1298 nav_items.removeClass('active');
1299 nav_items.eq(to).addClass('active');
1300
1301
1302 }
1303 if(o.auto) startRotateTimer();
1304 return false;
1305 };
1306 });
1307 };
1308
1309 function css(el, prop) {
1310 return parseInt($.css(el[0], prop)) || 0;
1311 };
1312 function width(el) {
1313 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1314 };
1315 function height(el) {
1316 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1317 };
1318
1319 })(jQuery);
1320
1321
Robert Lyd2dd6e52012-11-29 21:28:48 -08001322/*
Scott Mainf5089842012-08-14 16:31:07 -07001323 * dacSlideshow 1.0
1324 * Used on develop/index.html for side-sliding tabs
1325 *
1326 * Sample usage:
1327 * HTML -
1328 * <div class="slideshow-container">
1329 * <a href="" class="slideshow-prev">Prev</a>
1330 * <a href="" class="slideshow-next">Next</a>
1331 * <ul>
1332 * <li class="item"><img src="images/marquee1.jpg"></li>
1333 * <li class="item"><img src="images/marquee2.jpg"></li>
1334 * <li class="item"><img src="images/marquee3.jpg"></li>
1335 * <li class="item"><img src="images/marquee4.jpg"></li>
1336 * </ul>
1337 * </div>
1338 *
1339 * <script type="text/javascript">
1340 * $('.slideshow-container').dacSlideshow({
1341 * auto: true,
1342 * btnPrev: '.slideshow-prev',
1343 * btnNext: '.slideshow-next'
1344 * });
1345 * </script>
1346 *
1347 * Options:
1348 * btnPrev: optional identifier for previous button
1349 * btnNext: optional identifier for next button
1350 * auto: whether or not to auto-proceed
1351 * speed: animation speed
1352 * autoTime: time between auto-rotation
1353 * easing: easing function for transition
1354 * start: item to select by default
1355 * scroll: direction to scroll in
1356 * pagination: whether or not to include dotted pagination
1357 *
1358 */
1359 (function($) {
1360 $.fn.dacTabbedList = function(o) {
1361
1362 //Options - see above
1363 o = $.extend({
1364 speed : 250,
1365 easing: null,
1366 nav_id: null,
1367 frame_id: null
1368 }, o || {});
1369
1370 //Set up a carousel for each
1371 return this.each(function() {
1372
1373 var curr = 0;
1374 var running = false;
1375 var animCss = "margin-left";
1376 var sizeCss = "width";
1377 var div = $(this);
1378
1379 var nav = $(o.nav_id, div);
1380 var nav_li = $("li", nav);
1381 var nav_size = nav_li.size();
1382 var frame = div.find(o.frame_id);
1383 var content_width = $(frame).find('ul').width();
1384 //Buttons
1385 $(nav_li).click(function(e) {
1386 go($(nav_li).index($(this)));
1387 })
1388
1389 //Go to an item
1390 function go(to) {
1391 if(!running) {
1392 curr = to;
1393 running = true;
1394
1395 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1396 function() {
1397 running = false;
1398 }
1399 );
1400
1401
1402 nav_li.removeClass('active');
1403 nav_li.eq(to).addClass('active');
1404
1405
1406 }
1407 return false;
1408 };
1409 });
1410 };
1411
1412 function css(el, prop) {
1413 return parseInt($.css(el[0], prop)) || 0;
1414 };
1415 function width(el) {
1416 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1417 };
1418 function height(el) {
1419 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1420 };
1421
1422 })(jQuery);
1423
1424
1425
1426
1427
1428/* ######################################################## */
1429/* ################ SEARCH SUGGESTIONS ################## */
1430/* ######################################################## */
1431
1432
1433var gSelectedIndex = -1;
1434var gSelectedID = -1;
1435var gMatches = new Array();
1436var gLastText = "";
1437var ROW_COUNT = 20;
1438var gInitialized = false;
1439
1440function set_item_selected($li, selected)
1441{
1442 if (selected) {
1443 $li.attr('class','jd-autocomplete jd-selected');
1444 } else {
1445 $li.attr('class','jd-autocomplete');
1446 }
1447}
1448
1449function set_item_values(toroot, $li, match)
1450{
1451 var $link = $('a',$li);
1452 $link.html(match.__hilabel || match.label);
1453 $link.attr('href',toroot + match.link);
1454}
1455
1456function sync_selection_table(toroot)
1457{
1458 var $list = $("#search_filtered");
1459 var $li; //list item jquery object
1460 var i; //list item iterator
1461 gSelectedID = -1;
1462
1463 //initialize the table; draw it for the first time (but not visible).
1464 if (!gInitialized) {
1465 for (i=0; i<ROW_COUNT; i++) {
1466 var $li = $("<li class='jd-autocomplete'></li>");
1467 $list.append($li);
1468
1469 $li.mousedown(function() {
1470 window.location = this.firstChild.getAttribute("href");
1471 });
1472 $li.mouseover(function() {
1473 $('#search_filtered li').removeClass('jd-selected');
1474 $(this).addClass('jd-selected');
1475 gSelectedIndex = $('#search_filtered li').index(this);
1476 });
1477 $li.append('<a></a>');
1478 }
1479 gInitialized = true;
1480 }
1481
1482 //if we have results, make the table visible and initialize result info
1483 if (gMatches.length > 0) {
1484 $('#search_filtered_div').removeClass('no-display');
1485 var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
1486 for (i=0; i<N; i++) {
1487 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1488 $li.attr('class','show-item');
1489 set_item_values(toroot, $li, gMatches[i]);
1490 set_item_selected($li, i == gSelectedIndex);
1491 if (i == gSelectedIndex) {
1492 gSelectedID = gMatches[i].id;
1493 }
1494 }
1495 //start hiding rows that are no longer matches
1496 for (; i<ROW_COUNT; i++) {
1497 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1498 $li.attr('class','no-display');
1499 }
1500 //if there are more results we're not showing, so say so.
1501/* if (gMatches.length > ROW_COUNT) {
1502 li = list.rows[ROW_COUNT];
1503 li.className = "show-item";
1504 c1 = li.cells[0];
1505 c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more";
1506 } else {
1507 list.rows[ROW_COUNT].className = "hide-item";
1508 }*/
1509 //if we have no results, hide the table
1510 } else {
1511 $('#search_filtered_div').addClass('no-display');
1512 }
1513}
1514
1515function search_changed(e, kd, toroot)
1516{
1517 var search = document.getElementById("search_autocomplete");
1518 var text = search.value.replace(/(^ +)|( +$)/g, '');
1519
1520 // show/hide the close button
1521 if (text != '') {
1522 $(".search .close").removeClass("hide");
1523 } else {
1524 $(".search .close").addClass("hide");
1525 }
1526
1527 // 13 = enter
1528 if (e.keyCode == 13) {
1529 $('#search_filtered_div').addClass('no-display');
1530 if (!$('#search_filtered_div').hasClass('no-display') || (gSelectedIndex < 0)) {
1531 if ($("#searchResults").is(":hidden")) {
1532 // if results aren't showing, return true to allow search to execute
1533 return true;
1534 } else {
1535 // otherwise, results are already showing, so allow ajax to auto refresh the results
1536 // and ignore this Enter press to avoid the reload.
1537 return false;
1538 }
1539 } else if (kd && gSelectedIndex >= 0) {
1540 window.location = toroot + gMatches[gSelectedIndex].link;
1541 return false;
1542 }
1543 }
1544 // 38 -- arrow up
1545 else if (kd && (e.keyCode == 38)) {
1546 if (gSelectedIndex >= 0) {
1547 $('#search_filtered li').removeClass('jd-selected');
1548 gSelectedIndex--;
1549 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1550 }
1551 return false;
1552 }
1553 // 40 -- arrow down
1554 else if (kd && (e.keyCode == 40)) {
1555 if (gSelectedIndex < gMatches.length-1
1556 && gSelectedIndex < ROW_COUNT-1) {
1557 $('#search_filtered li').removeClass('jd-selected');
1558 gSelectedIndex++;
1559 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1560 }
1561 return false;
1562 }
1563 else if (!kd && (e.keyCode != 40) && (e.keyCode != 38)) {
1564 gMatches = new Array();
1565 matchedCount = 0;
1566 gSelectedIndex = -1;
1567 for (var i=0; i<DATA.length; i++) {
1568 var s = DATA[i];
1569 if (text.length != 0 &&
1570 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1571 gMatches[matchedCount] = s;
1572 matchedCount++;
1573 }
1574 }
1575 rank_autocomplete_results(text);
1576 for (var i=0; i<gMatches.length; i++) {
1577 var s = gMatches[i];
1578 if (gSelectedID == s.id) {
1579 gSelectedIndex = i;
1580 }
1581 }
1582 highlight_autocomplete_result_labels(text);
1583 sync_selection_table(toroot);
1584 return true; // allow the event to bubble up to the search api
1585 }
1586}
1587
1588function rank_autocomplete_results(query) {
1589 query = query || '';
1590 if (!gMatches || !gMatches.length)
1591 return;
1592
1593 // helper function that gets the last occurence index of the given regex
1594 // in the given string, or -1 if not found
1595 var _lastSearch = function(s, re) {
1596 if (s == '')
1597 return -1;
1598 var l = -1;
1599 var tmp;
1600 while ((tmp = s.search(re)) >= 0) {
1601 if (l < 0) l = 0;
1602 l += tmp;
1603 s = s.substr(tmp + 1);
1604 }
1605 return l;
1606 };
1607
1608 // helper function that counts the occurrences of a given character in
1609 // a given string
1610 var _countChar = function(s, c) {
1611 var n = 0;
1612 for (var i=0; i<s.length; i++)
1613 if (s.charAt(i) == c) ++n;
1614 return n;
1615 };
1616
1617 var queryLower = query.toLowerCase();
1618 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1619 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1620 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1621
1622 var _resultScoreFn = function(result) {
1623 // scores are calculated based on exact and prefix matches,
1624 // and then number of path separators (dots) from the last
1625 // match (i.e. favoring classes and deep package names)
1626 var score = 1.0;
1627 var labelLower = result.label.toLowerCase();
1628 var t;
1629 t = _lastSearch(labelLower, partExactAlnumRE);
1630 if (t >= 0) {
1631 // exact part match
1632 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1633 score *= 200 / (partsAfter + 1);
1634 } else {
1635 t = _lastSearch(labelLower, partPrefixAlnumRE);
1636 if (t >= 0) {
1637 // part prefix match
1638 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1639 score *= 20 / (partsAfter + 1);
1640 }
1641 }
1642
1643 return score;
1644 };
1645
1646 for (var i=0; i<gMatches.length; i++) {
1647 gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);
1648 }
1649
1650 gMatches.sort(function(a,b){
1651 var n = b.__resultScore - a.__resultScore;
1652 if (n == 0) // lexicographical sort if scores are the same
1653 n = (a.label < b.label) ? -1 : 1;
1654 return n;
1655 });
1656}
1657
1658function highlight_autocomplete_result_labels(query) {
1659 query = query || '';
1660 if (!gMatches || !gMatches.length)
1661 return;
1662
1663 var queryLower = query.toLowerCase();
1664 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
1665 var queryRE = new RegExp(
1666 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
1667 for (var i=0; i<gMatches.length; i++) {
1668 gMatches[i].__hilabel = gMatches[i].label.replace(
1669 queryRE, '<b>$1</b>');
1670 }
1671}
1672
1673function search_focus_changed(obj, focused)
1674{
1675 if (!focused) {
1676 if(obj.value == ""){
1677 $(".search .close").addClass("hide");
1678 }
1679 document.getElementById("search_filtered_div").className = "no-display";
1680 }
1681}
1682
1683function submit_search() {
1684 var query = document.getElementById('search_autocomplete').value;
1685 location.hash = 'q=' + query;
1686 loadSearchResults();
1687 $("#searchResults").slideDown('slow');
1688 return false;
1689}
1690
1691
1692function hideResults() {
1693 $("#searchResults").slideUp();
1694 $(".search .close").addClass("hide");
1695 location.hash = '';
1696
1697 $("#search_autocomplete").val("").blur();
1698
1699 // reset the ajax search callback to nothing, so results don't appear unless ENTER
1700 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
1701 return false;
1702}
1703
1704
1705
1706/* ########################################################## */
1707/* ################ CUSTOM SEARCH ENGINE ################## */
1708/* ########################################################## */
1709
1710google.load('search', '1');
1711var searchControl;
1712
1713function loadSearchResults() {
1714 document.getElementById("search_autocomplete").style.color = "#000";
1715
1716 // create search control
1717 searchControl = new google.search.SearchControl();
1718
1719 // use our existing search form and use tabs when multiple searchers are used
1720 drawOptions = new google.search.DrawOptions();
1721 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
1722 drawOptions.setInput(document.getElementById("search_autocomplete"));
1723
1724 // configure search result options
1725 searchOptions = new google.search.SearcherOptions();
1726 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
1727
1728 // configure each of the searchers, for each tab
1729 devSiteSearcher = new google.search.WebSearch();
1730 devSiteSearcher.setUserDefinedLabel("All");
1731 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
1732
1733 designSearcher = new google.search.WebSearch();
1734 designSearcher.setUserDefinedLabel("Design");
1735 designSearcher.setSiteRestriction("http://developer.android.com/design/");
1736
1737 trainingSearcher = new google.search.WebSearch();
1738 trainingSearcher.setUserDefinedLabel("Training");
1739 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
1740
1741 guidesSearcher = new google.search.WebSearch();
1742 guidesSearcher.setUserDefinedLabel("Guides");
1743 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
1744
1745 referenceSearcher = new google.search.WebSearch();
1746 referenceSearcher.setUserDefinedLabel("Reference");
1747 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
1748
Scott Maindf08ada2012-12-03 08:54:37 -08001749 googleSearcher = new google.search.WebSearch();
1750 googleSearcher.setUserDefinedLabel("Google Services");
1751 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
1752
Scott Mainf5089842012-08-14 16:31:07 -07001753 blogSearcher = new google.search.WebSearch();
1754 blogSearcher.setUserDefinedLabel("Blog");
1755 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
1756
1757 // add each searcher to the search control
1758 searchControl.addSearcher(devSiteSearcher, searchOptions);
1759 searchControl.addSearcher(designSearcher, searchOptions);
1760 searchControl.addSearcher(trainingSearcher, searchOptions);
1761 searchControl.addSearcher(guidesSearcher, searchOptions);
1762 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08001763 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07001764 searchControl.addSearcher(blogSearcher, searchOptions);
1765
1766 // configure result options
1767 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
1768 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
1769 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
1770 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
1771
1772 // upon ajax search, refresh the url and search title
1773 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
1774 updateResultTitle(query);
1775 var query = document.getElementById('search_autocomplete').value;
1776 location.hash = 'q=' + query;
1777 });
1778
1779 // draw the search results box
1780 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
1781
1782 // get query and execute the search
1783 searchControl.execute(decodeURI(getQuery(location.hash)));
1784
1785 document.getElementById("search_autocomplete").focus();
1786 addTabListeners();
1787}
1788// End of loadSearchResults
1789
1790
1791google.setOnLoadCallback(function(){
1792 if (location.hash.indexOf("q=") == -1) {
1793 // if there's no query in the url, don't search and make sure results are hidden
1794 $('#searchResults').hide();
1795 return;
1796 } else {
1797 // first time loading search results for this page
1798 $('#searchResults').slideDown('slow');
1799 $(".search .close").removeClass("hide");
1800 loadSearchResults();
1801 }
1802}, true);
1803
1804// when an event on the browser history occurs (back, forward, load) requery hash and do search
1805$(window).hashchange( function(){
1806 // Exit if the hash isn't a search query or there's an error in the query
1807 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
1808 // If the results pane is open, close it.
1809 if (!$("#searchResults").is(":hidden")) {
1810 hideResults();
1811 }
1812 return;
1813 }
1814
1815 // Otherwise, we have a search to do
1816 var query = decodeURI(getQuery(location.hash));
1817 searchControl.execute(query);
1818 $('#searchResults').slideDown('slow');
1819 $("#search_autocomplete").focus();
1820 $(".search .close").removeClass("hide");
1821
1822 updateResultTitle(query);
1823});
1824
1825function updateResultTitle(query) {
1826 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
1827}
1828
1829// forcefully regain key-up event control (previously jacked by search api)
1830$("#search_autocomplete").keyup(function(event) {
1831 return search_changed(event, false, toRoot);
1832});
1833
1834// add event listeners to each tab so we can track the browser history
1835function addTabListeners() {
1836 var tabHeaders = $(".gsc-tabHeader");
1837 for (var i = 0; i < tabHeaders.length; i++) {
1838 $(tabHeaders[i]).attr("id",i).click(function() {
1839 /*
1840 // make a copy of the page numbers for the search left pane
1841 setTimeout(function() {
1842 // remove any residual page numbers
1843 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
1844 // move the page numbers to the left position; make a clone,
1845 // because the element is drawn to the DOM only once
1846 // and because we're going to remove it (previous line),
1847 // we need it to be available to move again as the user navigates
1848 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
1849 .clone().appendTo('#searchResults .gsc-tabsArea');
1850 }, 200);
1851 */
1852 });
1853 }
1854 setTimeout(function(){$(tabHeaders[0]).click()},200);
1855}
1856
1857
1858function getQuery(hash) {
1859 var queryParts = hash.split('=');
1860 return queryParts[1];
1861}
1862
1863/* returns the given string with all HTML brackets converted to entities
1864 TODO: move this to the site's JS library */
1865function escapeHTML(string) {
1866 return string.replace(/</g,"&lt;")
1867 .replace(/>/g,"&gt;");
1868}
1869
1870
1871
1872
1873
1874
1875
1876/* ######################################################## */
1877/* ################# JAVADOC REFERENCE ################### */
1878/* ######################################################## */
1879
Scott Main65511c02012-09-07 15:51:32 -07001880/* Initialize some droiddoc stuff, but only if we're in the reference */
Robert Ly67d75f12012-12-03 12:53:42 -08001881if (location.pathname.indexOf("/reference")) {
1882 if(!location.pathname.indexOf("/reference-gms/packages.html")
1883 && !location.pathname.indexOf("/reference-gcm/packages.html")
1884 && !location.pathname.indexOf("/reference/com/google") == 0) {
1885 $(document).ready(function() {
1886 // init available apis based on user pref
1887 changeApiLevel();
1888 initSidenavHeightResize()
1889 });
1890 }
Scott Main65511c02012-09-07 15:51:32 -07001891}
Scott Mainf5089842012-08-14 16:31:07 -07001892
1893var API_LEVEL_COOKIE = "api_level";
1894var minLevel = 1;
1895var maxLevel = 1;
1896
1897/******* SIDENAV DIMENSIONS ************/
1898
1899 function initSidenavHeightResize() {
1900 // Change the drag bar size to nicely fit the scrollbar positions
1901 var $dragBar = $(".ui-resizable-s");
1902 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1903
1904 $( "#resize-packages-nav" ).resizable({
1905 containment: "#nav-panels",
1906 handles: "s",
1907 alsoResize: "#packages-nav",
1908 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1909 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
1910 });
1911
1912 }
1913
1914function updateSidenavFixedWidth() {
1915 if (!navBarIsFixed) return;
1916 $('#devdoc-nav').css({
1917 'width' : $('#side-nav').css('width'),
1918 'margin' : $('#side-nav').css('margin')
1919 });
1920 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1921
1922 initSidenavHeightResize();
1923}
1924
1925function updateSidenavFullscreenWidth() {
1926 if (!navBarIsFixed) return;
1927 $('#devdoc-nav').css({
1928 'width' : $('#side-nav').css('width'),
1929 'margin' : $('#side-nav').css('margin')
1930 });
1931 $('#devdoc-nav .totop').css({'left': 'inherit'});
1932
1933 initSidenavHeightResize();
1934}
1935
1936function buildApiLevelSelector() {
1937 maxLevel = SINCE_DATA.length;
1938 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1939 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1940
1941 minLevel = parseInt($("#doc-api-level").attr("class"));
1942 // Handle provisional api levels; the provisional level will always be the highest possible level
1943 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1944 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1945 if (isNaN(minLevel) && minLevel.length) {
1946 minLevel = maxLevel;
1947 }
1948 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1949 for (var i = maxLevel-1; i >= 0; i--) {
1950 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1951 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1952 select.append(option);
1953 }
1954
1955 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1956 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1957 selectedLevelItem.setAttribute('selected',true);
1958}
1959
1960function changeApiLevel() {
1961 maxLevel = SINCE_DATA.length;
1962 var selectedLevel = maxLevel;
1963
1964 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1965 toggleVisisbleApis(selectedLevel, "body");
1966
1967 var date = new Date();
1968 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1969 var expiration = date.toGMTString();
1970 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1971
1972 if (selectedLevel < minLevel) {
1973 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08001974 $("#naMessage").show().html("<div><p><strong>This " + thing
1975 + " requires API level " + minLevel + " or higher.</strong></p>"
1976 + "<p>This document is hidden because your selected API level for the documentation is "
1977 + selectedLevel + ". You can change the documentation API level with the selector "
1978 + "above the left navigation.</p>"
1979 + "<p>For more information about specifying the API level your app requires, "
1980 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
1981 + ">Supporting Different Platform Versions</a>.</p>"
1982 + "<input type='button' value='OK, make this page visible' "
1983 + "title='Change the API level to " + minLevel + "' "
1984 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
1985 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07001986 } else {
1987 $("#naMessage").hide();
1988 }
1989}
1990
1991function toggleVisisbleApis(selectedLevel, context) {
1992 var apis = $(".api",context);
1993 apis.each(function(i) {
1994 var obj = $(this);
1995 var className = obj.attr("class");
1996 var apiLevelIndex = className.lastIndexOf("-")+1;
1997 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1998 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1999 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2000 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2001 return;
2002 }
2003 apiLevel = parseInt(apiLevel);
2004
2005 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2006 var selectedLevelNum = parseInt(selectedLevel)
2007 var apiLevelNum = parseInt(apiLevel);
2008 if (isNaN(apiLevelNum)) {
2009 apiLevelNum = maxLevel;
2010 }
2011
2012 // Grey things out that aren't available and give a tooltip title
2013 if (apiLevelNum > selectedLevelNum) {
2014 obj.addClass("absent").attr("title","Requires API Level \""
2015 + apiLevel + "\" or higher");
2016 }
2017 else obj.removeClass("absent").removeAttr("title");
2018 });
2019}
2020
2021
2022
2023
2024/* ################# SIDENAV TREE VIEW ################### */
2025
2026function new_node(me, mom, text, link, children_data, api_level)
2027{
2028 var node = new Object();
2029 node.children = Array();
2030 node.children_data = children_data;
2031 node.depth = mom.depth + 1;
2032
2033 node.li = document.createElement("li");
2034 mom.get_children_ul().appendChild(node.li);
2035
2036 node.label_div = document.createElement("div");
2037 node.label_div.className = "label";
2038 if (api_level != null) {
2039 $(node.label_div).addClass("api");
2040 $(node.label_div).addClass("api-level-"+api_level);
2041 }
2042 node.li.appendChild(node.label_div);
2043
2044 if (children_data != null) {
2045 node.expand_toggle = document.createElement("a");
2046 node.expand_toggle.href = "javascript:void(0)";
2047 node.expand_toggle.onclick = function() {
2048 if (node.expanded) {
2049 $(node.get_children_ul()).slideUp("fast");
2050 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2051 node.expanded = false;
2052 } else {
2053 expand_node(me, node);
2054 }
2055 };
2056 node.label_div.appendChild(node.expand_toggle);
2057
2058 node.plus_img = document.createElement("img");
2059 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2060 node.plus_img.className = "plus";
2061 node.plus_img.width = "8";
2062 node.plus_img.border = "0";
2063 node.expand_toggle.appendChild(node.plus_img);
2064
2065 node.expanded = false;
2066 }
2067
2068 var a = document.createElement("a");
2069 node.label_div.appendChild(a);
2070 node.label = document.createTextNode(text);
2071 a.appendChild(node.label);
2072 if (link) {
2073 a.href = me.toroot + link;
2074 } else {
2075 if (children_data != null) {
2076 a.className = "nolink";
2077 a.href = "javascript:void(0)";
2078 a.onclick = node.expand_toggle.onclick;
2079 // This next line shouldn't be necessary. I'll buy a beer for the first
2080 // person who figures out how to remove this line and have the link
2081 // toggle shut on the first try. --joeo@android.com
2082 node.expanded = false;
2083 }
2084 }
2085
2086
2087 node.children_ul = null;
2088 node.get_children_ul = function() {
2089 if (!node.children_ul) {
2090 node.children_ul = document.createElement("ul");
2091 node.children_ul.className = "children_ul";
2092 node.children_ul.style.display = "none";
2093 node.li.appendChild(node.children_ul);
2094 }
2095 return node.children_ul;
2096 };
2097
2098 return node;
2099}
2100
Robert Lyd2dd6e52012-11-29 21:28:48 -08002101
2102
2103
Scott Mainf5089842012-08-14 16:31:07 -07002104function expand_node(me, node)
2105{
2106 if (node.children_data && !node.expanded) {
2107 if (node.children_visited) {
2108 $(node.get_children_ul()).slideDown("fast");
2109 } else {
2110 get_node(me, node);
2111 if ($(node.label_div).hasClass("absent")) {
2112 $(node.get_children_ul()).addClass("absent");
2113 }
2114 $(node.get_children_ul()).slideDown("fast");
2115 }
2116 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2117 node.expanded = true;
2118
2119 // perform api level toggling because new nodes are new to the DOM
2120 var selectedLevel = $("#apiLevelSelector option:selected").val();
2121 toggleVisisbleApis(selectedLevel, "#side-nav");
2122 }
2123}
2124
2125function get_node(me, mom)
2126{
2127 mom.children_visited = true;
2128 for (var i in mom.children_data) {
2129 var node_data = mom.children_data[i];
2130 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2131 node_data[2], node_data[3]);
2132 }
2133}
2134
2135function this_page_relative(toroot)
2136{
2137 var full = document.location.pathname;
2138 var file = "";
2139 if (toroot.substr(0, 1) == "/") {
2140 if (full.substr(0, toroot.length) == toroot) {
2141 return full.substr(toroot.length);
2142 } else {
2143 // the file isn't under toroot. Fail.
2144 return null;
2145 }
2146 } else {
2147 if (toroot != "./") {
2148 toroot = "./" + toroot;
2149 }
2150 do {
2151 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2152 var pos = full.lastIndexOf("/");
2153 file = full.substr(pos) + file;
2154 full = full.substr(0, pos);
2155 toroot = toroot.substr(0, toroot.length-3);
2156 }
2157 } while (toroot != "" && toroot != "/");
2158 return file.substr(1);
2159 }
2160}
2161
2162function find_page(url, data)
2163{
2164 var nodes = data;
2165 var result = null;
2166 for (var i in nodes) {
2167 var d = nodes[i];
2168 if (d[1] == url) {
2169 return new Array(i);
2170 }
2171 else if (d[2] != null) {
2172 result = find_page(url, d[2]);
2173 if (result != null) {
2174 return (new Array(i).concat(result));
2175 }
2176 }
2177 }
2178 return null;
2179}
2180
Scott Mainf5089842012-08-14 16:31:07 -07002181function init_default_navtree(toroot) {
2182 init_navtree("tree-list", toroot, NAVTREE_DATA);
2183
2184 // perform api level toggling because because the whole tree is new to the DOM
2185 var selectedLevel = $("#apiLevelSelector option:selected").val();
2186 toggleVisisbleApis(selectedLevel, "#side-nav");
2187}
2188
2189function init_navtree(navtree_id, toroot, root_nodes)
2190{
2191 var me = new Object();
2192 me.toroot = toroot;
2193 me.node = new Object();
2194
2195 me.node.li = document.getElementById(navtree_id);
2196 me.node.children_data = root_nodes;
2197 me.node.children = new Array();
2198 me.node.children_ul = document.createElement("ul");
2199 me.node.get_children_ul = function() { return me.node.children_ul; };
2200 //me.node.children_ul.className = "children_ul";
2201 me.node.li.appendChild(me.node.children_ul);
2202 me.node.depth = 0;
2203
2204 get_node(me, me.node);
2205
2206 me.this_page = this_page_relative(toroot);
2207 me.breadcrumbs = find_page(me.this_page, root_nodes);
2208 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2209 var mom = me.node;
2210 for (var i in me.breadcrumbs) {
2211 var j = me.breadcrumbs[i];
2212 mom = mom.children[j];
2213 expand_node(me, mom);
2214 }
2215 mom.label_div.className = mom.label_div.className + " selected";
2216 addLoadEvent(function() {
2217 scrollIntoView("nav-tree");
2218 });
2219 }
2220}
2221
Robert Lyd2dd6e52012-11-29 21:28:48 -08002222/* TODO: eliminate redundancy with non-google functions */
2223function init_google_navtree(navtree_id, toroot, root_nodes)
2224{
2225 var me = new Object();
2226 me.toroot = toroot;
2227 me.node = new Object();
2228
2229 me.node.li = document.getElementById(navtree_id);
2230 me.node.children_data = root_nodes;
2231 me.node.children = new Array();
2232 me.node.children_ul = document.createElement("ul");
2233 me.node.get_children_ul = function() { return me.node.children_ul; };
2234 //me.node.children_ul.className = "children_ul";
2235 me.node.li.appendChild(me.node.children_ul);
2236 me.node.depth = 0;
2237
2238 get_google_node(me, me.node);
2239
2240}
2241
2242function new_google_node(me, mom, text, link, children_data, api_level)
2243{
2244 var node = new Object();
2245 var child;
2246 node.children = Array();
2247 node.children_data = children_data;
2248 node.depth = mom.depth + 1;
2249 node.get_children_ul = function() {
2250 if (!node.children_ul) {
Scott Mainac71b2b2012-11-30 14:40:58 -08002251 node.children_ul = document.createElement("ul");
2252 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002253 node.li.appendChild(node.children_ul);
2254 }
2255 return node.children_ul;
2256 };
2257 node.li = document.createElement("li");
2258
2259 mom.get_children_ul().appendChild(node.li);
2260
2261
2262 if(link) {
2263 child = document.createElement("a");
2264
2265 }
2266 else {
2267 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08002268 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002269
2270 }
2271 if (children_data != null) {
2272 node.li.className="nav-section";
2273 node.label_div = document.createElement("div");
2274 node.label_div.className = "nav-section-header-ref";
2275 node.li.appendChild(node.label_div);
2276 get_google_node(me, node);
2277 node.label_div.appendChild(child);
2278 }
2279 else {
2280 node.li.appendChild(child);
2281 }
2282 if(link) {
2283 child.href = me.toroot + link;
2284 }
2285 node.label = document.createTextNode(text);
2286 child.appendChild(node.label);
2287
2288 node.children_ul = null;
2289
2290 return node;
2291}
2292
2293function get_google_node(me, mom)
2294{
2295 mom.children_visited = true;
2296 var linkText;
2297 for (var i in mom.children_data) {
2298 var node_data = mom.children_data[i];
2299 linkText = node_data[0];
2300
2301 if(linkText.match("^"+"com.google.android")=="com.google.android"){
2302 linkText = linkText.substr(19, linkText.length);
2303 }
2304 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
2305 node_data[2], node_data[3]);
2306 }
2307}
2308function showGoogleRefTree() {
2309 init_default_google_navtree(toRoot);
2310 init_default_gcm_navtree(toRoot);
2311 resizeNav();
2312}
2313
2314function init_default_google_navtree(toroot) {
2315 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
2316}
2317
2318function init_default_gcm_navtree(toroot) {
2319 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
2320}
2321
Scott Mainf5089842012-08-14 16:31:07 -07002322/* TOGGLE INHERITED MEMBERS */
2323
2324/* Toggle an inherited class (arrow toggle)
2325 * @param linkObj The link that was clicked.
2326 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2327 * 'null' to simply toggle.
2328 */
2329function toggleInherited(linkObj, expand) {
2330 var base = linkObj.getAttribute("id");
2331 var list = document.getElementById(base + "-list");
2332 var summary = document.getElementById(base + "-summary");
2333 var trigger = document.getElementById(base + "-trigger");
2334 var a = $(linkObj);
2335 if ( (expand == null && a.hasClass("closed")) || expand ) {
2336 list.style.display = "none";
2337 summary.style.display = "block";
2338 trigger.src = toRoot + "assets/images/triangle-opened.png";
2339 a.removeClass("closed");
2340 a.addClass("opened");
2341 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2342 list.style.display = "block";
2343 summary.style.display = "none";
2344 trigger.src = toRoot + "assets/images/triangle-closed.png";
2345 a.removeClass("opened");
2346 a.addClass("closed");
2347 }
2348 return false;
2349}
2350
2351/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2352 * @param linkObj The link that was clicked.
2353 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2354 * 'null' to simply toggle.
2355 */
2356function toggleAllInherited(linkObj, expand) {
2357 var a = $(linkObj);
2358 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2359 var expandos = $(".jd-expando-trigger", table);
2360 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2361 expandos.each(function(i) {
2362 toggleInherited(this, true);
2363 });
2364 a.text("[Collapse]");
2365 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2366 expandos.each(function(i) {
2367 toggleInherited(this, false);
2368 });
2369 a.text("[Expand]");
2370 }
2371 return false;
2372}
2373
2374/* Toggle all inherited members in the class (link in the class title)
2375 */
2376function toggleAllClassInherited() {
2377 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2378 var toggles = $(".toggle-all", $("#body-content"));
2379 if (a.text() == "[Expand All]") {
2380 toggles.each(function(i) {
2381 toggleAllInherited(this, true);
2382 });
2383 a.text("[Collapse All]");
2384 } else {
2385 toggles.each(function(i) {
2386 toggleAllInherited(this, false);
2387 });
2388 a.text("[Expand All]");
2389 }
2390 return false;
2391}
2392
2393/* Expand all inherited members in the class. Used when initiating page search */
2394function ensureAllInheritedExpanded() {
2395 var toggles = $(".toggle-all", $("#body-content"));
2396 toggles.each(function(i) {
2397 toggleAllInherited(this, true);
2398 });
2399 $("#toggleAllClassInherited").text("[Collapse All]");
2400}
2401
2402
2403/* HANDLE KEY EVENTS
2404 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2405 */
2406var agent = navigator['userAgent'].toLowerCase();
2407var mac = agent.indexOf("macintosh") != -1;
2408
2409$(document).keydown( function(e) {
2410var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2411 if (control && e.which == 70) { // 70 is "F"
2412 ensureAllInheritedExpanded();
2413 }
2414});