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