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