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