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