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