blob: f61cf1ed3dc17cfd8f967c0276eccb07571ffee1 [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
Scott Mainf6145542013-04-01 16:38:11 -07009var mPagePath; // initialized in ready() function
Scott Maine4d8f1b2012-06-21 18:03:05 -070010
Scott Main1b3db112012-07-03 14:06:22 -070011var basePath = getBaseUri(location.pathname);
12var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
Scott Main7e447ed2013-02-19 17:22:37 -080013var GOOGLE_DATA; // combined data for google service apis, used for search suggest
Scott Main1b3db112012-07-03 14:06:22 -070014
Scott Main25e73002013-03-27 15:24:06 -070015// Ensure that all ajax getScript() requests allow caching
16$.ajaxSetup({
17 cache: true
18});
Scott Maine4d8f1b2012-06-21 18:03:05 -070019
20/****** ON LOAD SET UP STUFF *********/
21
22var navBarIsFixed = false;
23$(document).ready(function() {
Scott Main7e447ed2013-02-19 17:22:37 -080024
Scott Main0e76e7e2013-03-12 10:24:07 -070025 // load json file for JD doc search suggestions
26 $.getScript(toRoot + 'reference/jd_lists.js');
Scott Main7e447ed2013-02-19 17:22:37 -080027 // load json file for Android API search suggestions
28 $.getScript(toRoot + 'reference/lists.js');
29 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080030 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080031 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
32 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080033 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080034 if(jqxhr.status === 200) {
35 // combine GCM and GMS data
36 GOOGLE_DATA = GMS_DATA;
37 var start = GOOGLE_DATA.length;
38 for (var i=0; i<GCM_DATA.length; i++) {
39 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
40 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
41 }
42 }
43 });
44 }
45 });
46
Scott Main0e76e7e2013-03-12 10:24:07 -070047 // setup keyboard listener for search shortcut
48 $('body').keyup(function(event) {
49 if (event.which == 191) {
50 $('#search_autocomplete').focus();
51 }
52 });
Scott Main015d6162013-01-29 09:01:52 -080053
Scott Maine4d8f1b2012-06-21 18:03:05 -070054 // init the fullscreen toggle click event
55 $('#nav-swap .fullscreen').click(function(){
56 if ($(this).hasClass('disabled')) {
57 toggleFullscreen(true);
58 } else {
59 toggleFullscreen(false);
60 }
61 });
62
63 // initialize the divs with custom scrollbars
64 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
65
66 // add HRs below all H2s (except for a few other h2 variants)
Scott Maindb3678b2012-10-23 14:13:41 -070067 $('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 -070068
69 // set up the search close button
70 $('.search .close').click(function() {
71 $searchInput = $('#search_autocomplete');
72 $searchInput.attr('value', '');
73 $(this).addClass("hide");
74 $("#search-container").removeClass('active');
75 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070076 search_focus_changed($searchInput.get(), false);
77 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070078 });
79
80 // Set up quicknav
81 var quicknav_open = false;
82 $("#btn-quicknav").click(function() {
83 if (quicknav_open) {
84 $(this).removeClass('active');
85 quicknav_open = false;
86 collapse();
87 } else {
88 $(this).addClass('active');
89 quicknav_open = true;
90 expand();
91 }
92 })
93
94 var expand = function() {
95 $('#header-wrap').addClass('quicknav');
96 $('#quicknav').stop().show().animate({opacity:'1'});
97 }
98
99 var collapse = function() {
100 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
101 $(this).hide();
102 $('#header-wrap').removeClass('quicknav');
103 });
104 }
105
106
107 //Set up search
108 $("#search_autocomplete").focus(function() {
109 $("#search-container").addClass('active');
110 })
111 $("#search-container").mouseover(function() {
112 $("#search-container").addClass('active');
113 $("#search_autocomplete").focus();
114 })
115 $("#search-container").mouseout(function() {
116 if ($("#search_autocomplete").is(":focus")) return;
117 if ($("#search_autocomplete").val() == '') {
118 setTimeout(function(){
119 $("#search-container").removeClass('active');
120 $("#search_autocomplete").blur();
121 },250);
122 }
123 })
124 $("#search_autocomplete").blur(function() {
125 if ($("#search_autocomplete").val() == '') {
126 $("#search-container").removeClass('active');
127 }
128 })
129
130
131 // prep nav expandos
132 var pagePath = document.location.pathname;
133 // account for intl docs by removing the intl/*/ path
134 if (pagePath.indexOf("/intl/") == 0) {
135 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
136 }
Scott Mainac2aef52013-02-12 14:15:23 -0800137
Scott Maine4d8f1b2012-06-21 18:03:05 -0700138 if (pagePath.indexOf(SITE_ROOT) == 0) {
139 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
140 pagePath += 'index.html';
141 }
142 }
143
Scott Main01a25452013-02-12 17:32:27 -0800144 // Need a copy of the pagePath before it gets changed in the next block;
145 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
146 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700147 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
148 // If running locally, SITE_ROOT will be a relative path, so account for that by
149 // finding the relative URL to this page. This will allow us to find links on the page
150 // leading back to this page.
151 var pathParts = pagePath.split('/');
152 var relativePagePathParts = [];
153 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
154 for (var i = 0; i < upDirs; i++) {
155 relativePagePathParts.push('..');
156 }
157 for (var i = 0; i < upDirs; i++) {
158 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
159 }
160 relativePagePathParts.push(pathParts[pathParts.length - 1]);
161 pagePath = relativePagePathParts.join('/');
162 } else {
163 // Otherwise the page path is already an absolute URL
164 }
165
Scott Mainac2aef52013-02-12 14:15:23 -0800166 // Highlight the header tabs...
167 // highlight Design tab
168 if ($("body").hasClass("design")) {
169 $("#header li.design a").addClass("selected");
170
171 // highlight Develop tab
172 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
173 $("#header li.develop a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800174 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800175 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800176 if (rootDir == "training") {
177 $("#nav-x li.training a").addClass("selected");
178 } else if (rootDir == "guide") {
179 $("#nav-x li.guide a").addClass("selected");
180 } else if (rootDir == "reference") {
181 // If the root is reference, but page is also part of Google Services, select Google
182 if ($("body").hasClass("google")) {
183 $("#nav-x li.google a").addClass("selected");
184 } else {
185 $("#nav-x li.reference a").addClass("selected");
186 }
187 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
188 $("#nav-x li.tools a").addClass("selected");
189 } else if ($("body").hasClass("google")) {
190 $("#nav-x li.google a").addClass("selected");
191 }
192
193 // highlight Distribute tab
194 } else if ($("body").hasClass("distribute")) {
195 $("#header li.distribute a").addClass("selected");
196 }
197
Scott Mainf6145542013-04-01 16:38:11 -0700198 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
199 // and highlight the sidenav
200 mPagePath = pagePath;
201 highlightSidenav();
Scott Mainac2aef52013-02-12 14:15:23 -0800202
Scott Mainf6145542013-04-01 16:38:11 -0700203 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700204 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700205 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700206 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800207 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700208
209 // set up prev links
210 var $prevLink = [];
211 var $prevListItem = $selListItem.prev('li');
212
213 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
214false; // navigate across topic boundaries only in design docs
215 if ($prevListItem.length) {
216 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700217 // jump to last topic of previous section
218 $prevLink = $prevListItem.find('a:last');
219 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700220 // jump to previous topic in this section
221 $prevLink = $prevListItem.find('a:eq(0)');
222 }
223 } else {
224 // jump to this section's index page (if it exists)
225 var $parentListItem = $selListItem.parents('li');
226 $prevLink = $selListItem.parents('li').find('a');
227
228 // except if cross boundaries aren't allowed, and we're at the top of a section already
229 // (and there's another parent)
230 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
231 && $selListItem.hasClass('nav-section')) {
232 $prevLink = [];
233 }
234 }
235
Scott Maine4d8f1b2012-06-21 18:03:05 -0700236 // set up next links
237 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700238 var startClass = false;
239 var training = $(".next-class-link").length; // decides whether to provide "next class" link
240 var isCrossingBoundary = false;
241
242 if ($selListItem.hasClass('nav-section')) {
243 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700244 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700245
246 // if there aren't any children, go to the next section (required for About pages)
247 if($nextLink.length == 0) {
248 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700249 } else if ($('.topic-start-link').length) {
250 // as long as there's a child link and there is a "topic start link" (we're on a landing)
251 // then set the landing page "start link" text to be the first doc title
252 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700253 }
254
Scott Main5a1123e2012-09-26 12:51:28 -0700255 // If the selected page has a description, then it's a class or article homepage
256 if ($selListItem.find('a[description]').length) {
257 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700258 startClass = true;
259 }
260 } else {
261 // jump to the next topic in this section (if it exists)
262 $nextLink = $selListItem.next('li').find('a:eq(0)');
263 if (!$nextLink.length) {
Scott Main5a1123e2012-09-26 12:51:28 -0700264 isCrossingBoundary = true;
265 // no more topics in this section, jump to the first topic in the next section
266 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
267 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
268 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700269 }
270 }
271 }
Scott Main5a1123e2012-09-26 12:51:28 -0700272
273 if (startClass) {
274 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
275
276 // if there's no training bar (below the start button),
277 // then we need to add a bottom border to button
278 if (!$("#tb").length) {
279 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700280 }
Scott Main5a1123e2012-09-26 12:51:28 -0700281 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
282 $('.content-footer.next-class').show();
283 $('.next-page-link').attr('href','')
284 .removeClass("hide").addClass("disabled")
285 .click(function() { return false; });
286
287 $('.next-class-link').attr('href',$nextLink.attr('href'))
288 .removeClass("hide").append($nextLink.html());
289 $('.next-class-link').find('.new').empty();
290 } else {
291 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
292 }
293
294 if (!startClass && $prevLink.length) {
295 var prevHref = $prevLink.attr('href');
296 if (prevHref == SITE_ROOT + 'index.html') {
297 // Don't show Previous when it leads to the homepage
298 } else {
299 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
300 }
301 }
302
303 // If this is a training 'article', there should be no prev/next nav
304 // ... if the grandparent is the "nav" ... and it has no child list items...
305 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
306 !$selListItem.find('li').length) {
307 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
308 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700309 }
310
311 }
Scott Main5a1123e2012-09-26 12:51:28 -0700312
313
314
315 // Set up the course landing pages for Training with class names and descriptions
316 if ($('body.trainingcourse').length) {
317 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
318 var $classDescriptions = $classLinks.attr('description');
319
320 var $olClasses = $('<ol class="class-list"></ol>');
321 var $liClass;
322 var $imgIcon;
323 var $h2Title;
324 var $pSummary;
325 var $olLessons;
326 var $liLesson;
327 $classLinks.each(function(index) {
328 $liClass = $('<li></li>');
329 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
330 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>');
331
332 $olLessons = $('<ol class="lesson-list"></ol>');
333
334 $lessons = $(this).closest('li').find('ul li a');
335
336 if ($lessons.length) {
337 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" alt=""/>');
338 $lessons.each(function(index) {
339 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
340 });
341 } else {
342 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" alt=""/>');
343 $pSummary.addClass('article');
344 }
345
346 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
347 $olClasses.append($liClass);
348 });
349 $('.jd-descr').append($olClasses);
350 }
351
Scott Maine4d8f1b2012-06-21 18:03:05 -0700352
353
354
355 // Set up expand/collapse behavior
356 $('#nav li.nav-section .nav-section-header').click(function() {
357 var section = $(this).closest('li.nav-section');
358 if (section.hasClass('expanded')) {
359 /* hide me */
360 // if (section.hasClass('selected') || section.find('li').hasClass('selected')) {
361 // /* but not if myself or my descendents are selected */
362 // return;
363 // }
364 section.children('ul').slideUp(250, function() {
365 section.closest('li').removeClass('expanded');
366 resizeNav();
367 });
368 } else {
369 /* show me */
370 // first hide all other siblings
371 var $others = $('li.nav-section.expanded', $(this).closest('ul'));
372 $others.removeClass('expanded').children('ul').slideUp(250);
373
374 // now expand me
375 section.closest('li').addClass('expanded');
376 section.children('ul').slideDown(250, function() {
377 resizeNav();
378 });
379 }
380 });
381
382 $(".scroll-pane").scroll(function(event) {
383 event.preventDefault();
384 return false;
385 });
386
387 /* Resize nav height when window height changes */
388 $(window).resize(function() {
389 if ($('#side-nav').length == 0) return;
390 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
391 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
392 // make sidenav behave when resizing the window and side-scolling is a concern
393 if (navBarIsFixed) {
394 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
395 updateSideNavPosition();
396 } else {
397 updateSidenavFullscreenWidth();
398 }
399 }
400 resizeNav();
401 });
402
403
404 // Set up fixed navbar
405 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
406 $(window).scroll(function(event) {
407 if ($('#side-nav').length == 0) return;
408 if (event.target.nodeName == "DIV") {
409 // Dump scroll event if the target is a DIV, because that means the event is coming
410 // from a scrollable div and so there's no need to make adjustments to our layout
411 return;
412 }
413 var scrollTop = $(window).scrollTop();
414 var headerHeight = $('#header').outerHeight();
415 var subheaderHeight = $('#nav-x').outerHeight();
416 var searchResultHeight = $('#searchResults').is(":visible") ?
417 $('#searchResults').outerHeight() : 0;
418 var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight;
Scott Mainb8d06a52012-12-19 18:38:24 -0800419 // we set the navbar fixed when the scroll position is beyond the height of the site header...
Scott Maine4d8f1b2012-06-21 18:03:05 -0700420 var navBarShouldBeFixed = scrollTop > totalHeaderHeight;
Scott Mainb8d06a52012-12-19 18:38:24 -0800421 // ... except if the document content is shorter than the sidenav height.
422 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
423 if ($("#doc-col").height() < $("#side-nav").height()) {
424 navBarShouldBeFixed = false;
425 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700426
427 var scrollLeft = $(window).scrollLeft();
428 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
429 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
430 updateSideNavPosition();
431 prevScrollLeft = scrollLeft;
432 }
433
434 // Don't continue if the header is sufficently far away
435 // (to avoid intensive resizing that slows scrolling)
436 if (navBarIsFixed && navBarShouldBeFixed) {
437 return;
438 }
439
440 if (navBarIsFixed != navBarShouldBeFixed) {
441 if (navBarShouldBeFixed) {
442 // make it fixed
443 var width = $('#devdoc-nav').width();
444 $('#devdoc-nav')
445 .addClass('fixed')
446 .css({'width':width+'px'})
447 .prependTo('#body-content');
448 // add neato "back to top" button
449 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
450
451 // update the sidenaav position for side scrolling
452 updateSideNavPosition();
453 } else {
454 // make it static again
455 $('#devdoc-nav')
456 .removeClass('fixed')
457 .css({'width':'auto','margin':''})
458 .prependTo('#side-nav');
459 $('#devdoc-nav a.totop').hide();
460 }
461 navBarIsFixed = navBarShouldBeFixed;
462 }
463
464 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
465 });
466
467
468 var navBarLeftPos;
469 if ($('#devdoc-nav').length) {
470 setNavBarLeftPos();
471 }
472
473
474 // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away
475 // from the page)
476 $('.nav-section-header').find('a:eq(0)').click(function(evt) {
477 window.location.href = $(this).attr('href');
478 return false;
479 });
480
481 // Set up play-on-hover <video> tags.
482 $('video.play-on-hover').bind('click', function(){
483 $(this).get(0).load(); // in case the video isn't seekable
484 $(this).get(0).play();
485 });
486
487 // Set up tooltips
488 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700489 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700490 var $target = $(this);
491 var $tooltip = $('<div>')
492 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700493 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700494 .hide()
495 .appendTo('body');
496 $target.removeAttr('title');
497
498 $target.hover(function() {
499 // in
500 var targetRect = $target.offset();
501 targetRect.width = $target.width();
502 targetRect.height = $target.height();
503
504 $tooltip.css({
505 left: targetRect.left,
506 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
507 });
508 $tooltip.addClass('below');
509 $tooltip.show();
510 }, function() {
511 // out
512 $tooltip.hide();
513 });
514 });
515
516 // Set up <h2> deeplinks
517 $('h2').click(function() {
518 var id = $(this).attr('id');
519 if (id) {
520 document.location.hash = id;
521 }
522 });
523
524 //Loads the +1 button
525 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
526 po.src = 'https://apis.google.com/js/plusone.js';
527 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
528
529
530 // Revise the sidenav widths to make room for the scrollbar
531 // which avoids the visible width from changing each time the bar appears
532 var $sidenav = $("#side-nav");
533 var sidenav_width = parseInt($sidenav.innerWidth());
534
535 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
536
537
538 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
539
540 if ($(".scroll-pane").length > 1) {
541 // Check if there's a user preference for the panel heights
542 var cookieHeight = readCookie("reference_height");
543 if (cookieHeight) {
544 restoreHeight(cookieHeight);
545 }
546 }
547
548 resizeNav();
549
Scott Main015d6162013-01-29 09:01:52 -0800550 /* init the language selector based on user cookie for lang */
551 loadLangPref();
552 changeNavLang(getLangPref());
553
554 /* setup event handlers to ensure the overflow menu is visible while picking lang */
555 $("#language select")
556 .mousedown(function() {
557 $("div.morehover").addClass("hover"); })
558 .blur(function() {
559 $("div.morehover").removeClass("hover"); });
560
561 /* some global variable setup */
562 resizePackagesNav = $("#resize-packages-nav");
563 classesNav = $("#classes-nav");
564 devdocNav = $("#devdoc-nav");
565
566 var cookiePath = "";
567 if (location.href.indexOf("/reference/") != -1) {
568 cookiePath = "reference_";
569 } else if (location.href.indexOf("/guide/") != -1) {
570 cookiePath = "guide_";
571 } else if (location.href.indexOf("/tools/") != -1) {
572 cookiePath = "tools_";
573 } else if (location.href.indexOf("/training/") != -1) {
574 cookiePath = "training_";
575 } else if (location.href.indexOf("/design/") != -1) {
576 cookiePath = "design_";
577 } else if (location.href.indexOf("/distribute/") != -1) {
578 cookiePath = "distribute_";
579 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700580
581});
Scott Main7e447ed2013-02-19 17:22:37 -0800582// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700583
584
Scott Mainf6145542013-04-01 16:38:11 -0700585function highlightSidenav() {
586 // select current page in sidenav and header, and set up prev/next links if they exist
587 var $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
588 var $selListItem;
589 if ($selNavLink.length) {
590
591 // Find this page's <li> in sidenav and set selected
592 $selListItem = $selNavLink.closest('li');
593 $selListItem.addClass('selected');
594
595 // Traverse up the tree and expand all parent nav-sections
596 $selNavLink.parents('li.nav-section').each(function() {
597 $(this).addClass('expanded');
598 $(this).children('ul').show();
599 });
600 }
601}
602
Scott Maine4d8f1b2012-06-21 18:03:05 -0700603
604function toggleFullscreen(enable) {
605 var delay = 20;
606 var enabled = true;
607 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
608 if (enable) {
609 // Currently NOT USING fullscreen; enable fullscreen
610 stylesheet.removeAttr('disabled');
611 $('#nav-swap .fullscreen').removeClass('disabled');
612 $('#devdoc-nav').css({left:''});
613 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
614 enabled = true;
615 } else {
616 // Currently USING fullscreen; disable fullscreen
617 stylesheet.attr('disabled', 'disabled');
618 $('#nav-swap .fullscreen').addClass('disabled');
619 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
620 enabled = false;
621 }
622 writeCookie("fullscreen", enabled, null, null);
623 setNavBarLeftPos();
624 resizeNav(delay);
625 updateSideNavPosition();
626 setTimeout(initSidenavHeightResize,delay);
627}
628
629
630function setNavBarLeftPos() {
631 navBarLeftPos = $('#body-content').offset().left;
632}
633
634
635function updateSideNavPosition() {
636 var newLeft = $(window).scrollLeft() - navBarLeftPos;
637 $('#devdoc-nav').css({left: -newLeft});
638 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
639}
640
641
642
643
644
645
646
647
648// TODO: use $(document).ready instead
649function addLoadEvent(newfun) {
650 var current = window.onload;
651 if (typeof window.onload != 'function') {
652 window.onload = newfun;
653 } else {
654 window.onload = function() {
655 current();
656 newfun();
657 }
658 }
659}
660
661var agent = navigator['userAgent'].toLowerCase();
662// If a mobile phone, set flag and do mobile setup
663if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
664 (agent.indexOf("blackberry") != -1) ||
665 (agent.indexOf("webos") != -1) ||
666 (agent.indexOf("mini") != -1)) { // opera mini browsers
667 isMobile = true;
668}
669
670
Scott Maine4d8f1b2012-06-21 18:03:05 -0700671addLoadEvent( function() {
672 $("pre:not(.no-pretty-print)").addClass("prettyprint");
673 prettyPrint();
674} );
675
Scott Maine4d8f1b2012-06-21 18:03:05 -0700676
677
678
679/* ######### RESIZE THE SIDENAV HEIGHT ########## */
680
681function resizeNav(delay) {
682 var $nav = $("#devdoc-nav");
683 var $window = $(window);
684 var navHeight;
685
686 // Get the height of entire window and the total header height.
687 // Then figure out based on scroll position whether the header is visible
688 var windowHeight = $window.height();
689 var scrollTop = $window.scrollTop();
690 var headerHeight = $('#header').outerHeight();
691 var subheaderHeight = $('#nav-x').outerHeight();
692 var headerVisible = (scrollTop < (headerHeight + subheaderHeight));
693
694 // get the height of space between nav and top of window.
695 // Could be either margin or top position, depending on whether the nav is fixed.
696 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
697 // add 1 for the #side-nav bottom margin
698
699 // Depending on whether the header is visible, set the side nav's height.
700 if (headerVisible) {
701 // The sidenav height grows as the header goes off screen
702 navHeight = windowHeight - (headerHeight + subheaderHeight - scrollTop) - topMargin;
703 } else {
704 // Once header is off screen, the nav height is almost full window height
705 navHeight = windowHeight - topMargin;
706 }
707
708
709
710 $scrollPanes = $(".scroll-pane");
711 if ($scrollPanes.length > 1) {
712 // subtract the height of the api level widget and nav swapper from the available nav height
713 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
714
715 $("#swapper").css({height:navHeight + "px"});
716 if ($("#nav-tree").is(":visible")) {
717 $("#nav-tree").css({height:navHeight});
718 }
719
720 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
721 //subtract 10px to account for drag bar
722
723 // if the window becomes small enough to make the class panel height 0,
724 // then the package panel should begin to shrink
725 if (parseInt(classesHeight) <= 0) {
726 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
727 $("#packages-nav").css({height:navHeight - 10});
728 }
729
730 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
731 $("#classes-nav .jspContainer").css({height:classesHeight});
732
733
734 } else {
735 $nav.height(navHeight);
736 }
737
738 if (delay) {
739 updateFromResize = true;
740 delayedReInitScrollbars(delay);
741 } else {
742 reInitScrollbars();
743 }
744
745}
746
747var updateScrollbars = false;
748var updateFromResize = false;
749
750/* Re-initialize the scrollbars to account for changed nav size.
751 * This method postpones the actual update by a 1/4 second in order to optimize the
752 * scroll performance while the header is still visible, because re-initializing the
753 * scroll panes is an intensive process.
754 */
755function delayedReInitScrollbars(delay) {
756 // If we're scheduled for an update, but have received another resize request
757 // before the scheduled resize has occured, just ignore the new request
758 // (and wait for the scheduled one).
759 if (updateScrollbars && updateFromResize) {
760 updateFromResize = false;
761 return;
762 }
763
764 // We're scheduled for an update and the update request came from this method's setTimeout
765 if (updateScrollbars && !updateFromResize) {
766 reInitScrollbars();
767 updateScrollbars = false;
768 } else {
769 updateScrollbars = true;
770 updateFromResize = false;
771 setTimeout('delayedReInitScrollbars()',delay);
772 }
773}
774
775/* Re-initialize the scrollbars to account for changed nav size. */
776function reInitScrollbars() {
777 var pane = $(".scroll-pane").each(function(){
778 var api = $(this).data('jsp');
779 if (!api) { setTimeout(reInitScrollbars,300); return;}
780 api.reinitialise( {verticalGutter:0} );
781 });
782 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
783}
784
785
786/* Resize the height of the nav panels in the reference,
787 * and save the new size to a cookie */
788function saveNavPanels() {
789 var basePath = getBaseUri(location.pathname);
790 var section = basePath.substring(1,basePath.indexOf("/",1));
791 writeCookie("height", resizePackagesNav.css("height"), section, null);
792}
793
794
795
796function restoreHeight(packageHeight) {
797 $("#resize-packages-nav").height(packageHeight);
798 $("#packages-nav").height(packageHeight);
799 // var classesHeight = navHeight - packageHeight;
800 // $("#classes-nav").css({height:classesHeight});
801 // $("#classes-nav .jspContainer").css({height:classesHeight});
802}
803
804
805
806/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
807
808
809
810
811
812/** Scroll the jScrollPane to make the currently selected item visible
813 This is called when the page finished loading. */
814function scrollIntoView(nav) {
815 var $nav = $("#"+nav);
816 var element = $nav.jScrollPane({/* ...settings... */});
817 var api = element.data('jsp');
818
819 if ($nav.is(':visible')) {
820 var $selected = $(".selected", $nav);
821 if ($selected.length == 0) return;
822
823 var selectedOffset = $selected.position().top;
824 if (selectedOffset + 90 > $nav.height()) { // add 90 so that we scroll up even
825 // if the current item is close to the bottom
826 api.scrollTo(0, selectedOffset - ($nav.height() / 4), false); // scroll the item into view
827 // to be 1/4 of the way from the top
828 }
829 }
830}
831
832
833
834
835
836
837/* Show popup dialogs */
838function showDialog(id) {
839 $dialog = $("#"+id);
840 $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>');
841 $dialog.wrapInner('<div/>');
842 $dialog.removeClass("hide");
843}
844
845
846
847
848
849/* ######### COOKIES! ########## */
850
851function readCookie(cookie) {
852 var myCookie = cookie_namespace+"_"+cookie+"=";
853 if (document.cookie) {
854 var index = document.cookie.indexOf(myCookie);
855 if (index != -1) {
856 var valStart = index + myCookie.length;
857 var valEnd = document.cookie.indexOf(";", valStart);
858 if (valEnd == -1) {
859 valEnd = document.cookie.length;
860 }
861 var val = document.cookie.substring(valStart, valEnd);
862 return val;
863 }
864 }
865 return 0;
866}
867
868function writeCookie(cookie, val, section, expiration) {
869 if (val==undefined) return;
870 section = section == null ? "_" : "_"+section+"_";
871 if (expiration == null) {
872 var date = new Date();
873 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
874 expiration = date.toGMTString();
875 }
876 var cookieValue = cookie_namespace + section + cookie + "=" + val
877 + "; expires=" + expiration+"; path=/";
878 document.cookie = cookieValue;
879}
880
881/* ######### END COOKIES! ########## */
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907/*
908
909REMEMBER THE PREVIOUS PAGE FOR EACH TAB
910
911function loadLast(cookiePath) {
912 var location = window.location.href;
913 if (location.indexOf("/"+cookiePath+"/") != -1) {
914 return true;
915 }
916 var lastPage = readCookie(cookiePath + "_lastpage");
917 if (lastPage) {
918 window.location = lastPage;
919 return false;
920 }
921 return true;
922}
923
924
925
926$(window).unload(function(){
927 var path = getBaseUri(location.pathname);
928 if (path.indexOf("/reference/") != -1) {
929 writeCookie("lastpage", path, "reference", null);
930 } else if (path.indexOf("/guide/") != -1) {
931 writeCookie("lastpage", path, "guide", null);
932 } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) {
933 writeCookie("lastpage", path, "resources", null);
934 }
935});
936
937*/
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952function toggle(obj, slide) {
953 var ul = $("ul:first", obj);
954 var li = ul.parent();
955 if (li.hasClass("closed")) {
956 if (slide) {
957 ul.slideDown("fast");
958 } else {
959 ul.show();
960 }
961 li.removeClass("closed");
962 li.addClass("open");
963 $(".toggle-img", li).attr("title", "hide pages");
964 } else {
965 ul.slideUp("fast");
966 li.removeClass("open");
967 li.addClass("closed");
968 $(".toggle-img", li).attr("title", "show pages");
969 }
970}
971
972
973
974
975
976function buildToggleLists() {
977 $(".toggle-list").each(
978 function(i) {
979 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
980 $(this).addClass("closed");
981 });
982}
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015/* REFERENCE NAV SWAP */
1016
1017
1018function getNavPref() {
1019 var v = readCookie('reference_nav');
1020 if (v != NAV_PREF_TREE) {
1021 v = NAV_PREF_PANELS;
1022 }
1023 return v;
1024}
1025
1026function chooseDefaultNav() {
1027 nav_pref = getNavPref();
1028 if (nav_pref == NAV_PREF_TREE) {
1029 $("#nav-panels").toggle();
1030 $("#panel-link").toggle();
1031 $("#nav-tree").toggle();
1032 $("#tree-link").toggle();
1033 }
1034}
1035
1036function swapNav() {
1037 if (nav_pref == NAV_PREF_TREE) {
1038 nav_pref = NAV_PREF_PANELS;
1039 } else {
1040 nav_pref = NAV_PREF_TREE;
1041 init_default_navtree(toRoot);
1042 }
1043 var date = new Date();
1044 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1045 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1046
1047 $("#nav-panels").toggle();
1048 $("#panel-link").toggle();
1049 $("#nav-tree").toggle();
1050 $("#tree-link").toggle();
1051
1052 resizeNav();
1053
1054 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1055 $("#nav-tree .jspContainer:visible")
1056 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1057 // Another nasty hack to make the scrollbar appear now that we have height
1058 resizeNav();
1059
1060 if ($("#nav-tree").is(':visible')) {
1061 scrollIntoView("nav-tree");
1062 } else {
1063 scrollIntoView("packages-nav");
1064 scrollIntoView("classes-nav");
1065 }
1066}
1067
1068
1069
Scott Mainf5089842012-08-14 16:31:07 -07001070/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001071/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001072/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001073
1074function getBaseUri(uri) {
1075 var intlUrl = (uri.substring(0,6) == "/intl/");
1076 if (intlUrl) {
1077 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1078 base = base.substring(base.indexOf('/')+1, base.length);
1079 //alert("intl, returning base url: /" + base);
1080 return ("/" + base);
1081 } else {
1082 //alert("not intl, returning uri as found.");
1083 return uri;
1084 }
1085}
1086
1087function requestAppendHL(uri) {
1088//append "?hl=<lang> to an outgoing request (such as to blog)
1089 var lang = getLangPref();
1090 if (lang) {
1091 var q = 'hl=' + lang;
1092 uri += '?' + q;
1093 window.location = uri;
1094 return false;
1095 } else {
1096 return true;
1097 }
1098}
1099
1100
Scott Maine4d8f1b2012-06-21 18:03:05 -07001101function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001102 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1103 $links.each(function(i){ // for each link with a translation
1104 var $link = $(this);
1105 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1106 // put the desired language from the attribute as the text
1107 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001108 }
Scott Main6eb95f12012-10-02 17:12:23 -07001109 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001110}
1111
Scott Main015d6162013-01-29 09:01:52 -08001112function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001113 var date = new Date();
1114 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
1115 // keep this for 50 years
1116 //alert("expires: " + expires)
1117 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001118
1119 // ####### TODO: Remove this condition once we're stable on devsite #######
1120 // This condition is only needed if we still need to support legacy GAE server
1121 if (devsite) {
1122 // Switch language when on Devsite server
1123 if (submit) {
1124 $("#setlang").submit();
1125 }
1126 } else {
1127 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001128 if (submit) {
1129 window.location = getBaseUri(location.pathname);
1130 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001131 }
1132}
1133
1134function loadLangPref() {
1135 var lang = readCookie("pref_lang");
1136 if (lang != 0) {
1137 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1138 }
1139}
1140
1141function getLangPref() {
1142 var lang = $("#language").find(":selected").attr("value");
1143 if (!lang) {
1144 lang = readCookie("pref_lang");
1145 }
1146 return (lang != 0) ? lang : 'en';
1147}
1148
1149/* ########## END LOCALIZATION ############ */
1150
1151
1152
1153
1154
1155
1156/* Used to hide and reveal supplemental content, such as long code samples.
1157 See the companion CSS in android-developer-docs.css */
1158function toggleContent(obj) {
1159 var div = $(obj.parentNode.parentNode);
1160 var toggleMe = $(".toggle-content-toggleme",div);
1161 if (div.hasClass("closed")) { // if it's closed, open it
1162 toggleMe.slideDown();
1163 $(".toggle-content-text", obj).toggle();
1164 div.removeClass("closed").addClass("open");
1165 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot
1166 + "assets/images/triangle-opened.png");
1167 } else { // if it's open, close it
1168 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
1169 $(".toggle-content-text", obj).toggle();
1170 div.removeClass("open").addClass("closed");
1171 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
1172 + "assets/images/triangle-closed.png");
1173 });
1174 }
1175 return false;
1176}
Scott Mainf5089842012-08-14 16:31:07 -07001177
1178
Scott Maindb3678b2012-10-23 14:13:41 -07001179/* New version of expandable content */
1180function toggleExpandable(link,id) {
1181 if($(id).is(':visible')) {
1182 $(id).slideUp();
1183 $(link).removeClass('expanded');
1184 } else {
1185 $(id).slideDown();
1186 $(link).addClass('expanded');
1187 }
1188}
1189
1190function hideExpandable(ids) {
1191 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001192 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001193}
1194
Scott Mainf5089842012-08-14 16:31:07 -07001195
1196
1197
1198
Robert Lyd2dd6e52012-11-29 21:28:48 -08001199/*
Scott Mainf5089842012-08-14 16:31:07 -07001200 * Slideshow 1.0
1201 * Used on /index.html and /develop/index.html for carousel
1202 *
1203 * Sample usage:
1204 * HTML -
1205 * <div class="slideshow-container">
1206 * <a href="" class="slideshow-prev">Prev</a>
1207 * <a href="" class="slideshow-next">Next</a>
1208 * <ul>
1209 * <li class="item"><img src="images/marquee1.jpg"></li>
1210 * <li class="item"><img src="images/marquee2.jpg"></li>
1211 * <li class="item"><img src="images/marquee3.jpg"></li>
1212 * <li class="item"><img src="images/marquee4.jpg"></li>
1213 * </ul>
1214 * </div>
1215 *
1216 * <script type="text/javascript">
1217 * $('.slideshow-container').dacSlideshow({
1218 * auto: true,
1219 * btnPrev: '.slideshow-prev',
1220 * btnNext: '.slideshow-next'
1221 * });
1222 * </script>
1223 *
1224 * Options:
1225 * btnPrev: optional identifier for previous button
1226 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001227 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001228 * auto: whether or not to auto-proceed
1229 * speed: animation speed
1230 * autoTime: time between auto-rotation
1231 * easing: easing function for transition
1232 * start: item to select by default
1233 * scroll: direction to scroll in
1234 * pagination: whether or not to include dotted pagination
1235 *
1236 */
1237
1238 (function($) {
1239 $.fn.dacSlideshow = function(o) {
1240
1241 //Options - see above
1242 o = $.extend({
1243 btnPrev: null,
1244 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001245 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001246 auto: true,
1247 speed: 500,
1248 autoTime: 12000,
1249 easing: null,
1250 start: 0,
1251 scroll: 1,
1252 pagination: true
1253
1254 }, o || {});
1255
1256 //Set up a carousel for each
1257 return this.each(function() {
1258
1259 var running = false;
1260 var animCss = o.vertical ? "top" : "left";
1261 var sizeCss = o.vertical ? "height" : "width";
1262 var div = $(this);
1263 var ul = $("ul", div);
1264 var tLi = $("li", ul);
1265 var tl = tLi.size();
1266 var timer = null;
1267
1268 var li = $("li", ul);
1269 var itemLength = li.size();
1270 var curr = o.start;
1271
1272 li.css({float: o.vertical ? "none" : "left"});
1273 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1274 div.css({position: "relative", "z-index": "2", left: "0px"});
1275
1276 var liSize = o.vertical ? height(li) : width(li);
1277 var ulSize = liSize * itemLength;
1278 var divSize = liSize;
1279
1280 li.css({width: li.width(), height: li.height()});
1281 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1282
1283 div.css(sizeCss, divSize+"px");
1284
1285 //Pagination
1286 if (o.pagination) {
1287 var pagination = $("<div class='pagination'></div>");
1288 var pag_ul = $("<ul></ul>");
1289 if (tl > 1) {
1290 for (var i=0;i<tl;i++) {
1291 var li = $("<li>"+i+"</li>");
1292 pag_ul.append(li);
1293 if (i==o.start) li.addClass('active');
1294 li.click(function() {
1295 go(parseInt($(this).text()));
1296 })
1297 }
1298 pagination.append(pag_ul);
1299 div.append(pagination);
1300 }
1301 }
1302
1303 //Previous button
1304 if(o.btnPrev)
1305 $(o.btnPrev).click(function(e) {
1306 e.preventDefault();
1307 return go(curr-o.scroll);
1308 });
1309
1310 //Next button
1311 if(o.btnNext)
1312 $(o.btnNext).click(function(e) {
1313 e.preventDefault();
1314 return go(curr+o.scroll);
1315 });
Scott Maineb410352013-01-14 19:03:40 -08001316
1317 //Pause button
1318 if(o.btnPause)
1319 $(o.btnPause).click(function(e) {
1320 e.preventDefault();
1321 if ($(this).hasClass('paused')) {
1322 startRotateTimer();
1323 } else {
1324 pauseRotateTimer();
1325 }
1326 });
Scott Mainf5089842012-08-14 16:31:07 -07001327
1328 //Auto rotation
1329 if(o.auto) startRotateTimer();
1330
1331 function startRotateTimer() {
1332 clearInterval(timer);
1333 timer = setInterval(function() {
1334 if (curr == tl-1) {
1335 go(0);
1336 } else {
1337 go(curr+o.scroll);
1338 }
1339 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001340 $(o.btnPause).removeClass('paused');
1341 }
1342
1343 function pauseRotateTimer() {
1344 clearInterval(timer);
1345 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001346 }
1347
1348 //Go to an item
1349 function go(to) {
1350 if(!running) {
1351
1352 if(to<0) {
1353 to = itemLength-1;
1354 } else if (to>itemLength-1) {
1355 to = 0;
1356 }
1357 curr = to;
1358
1359 running = true;
1360
1361 ul.animate(
1362 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1363 function() {
1364 running = false;
1365 }
1366 );
1367
1368 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1369 $( (curr-o.scroll<0 && o.btnPrev)
1370 ||
1371 (curr+o.scroll > itemLength && o.btnNext)
1372 ||
1373 []
1374 ).addClass("disabled");
1375
1376
1377 var nav_items = $('li', pagination);
1378 nav_items.removeClass('active');
1379 nav_items.eq(to).addClass('active');
1380
1381
1382 }
1383 if(o.auto) startRotateTimer();
1384 return false;
1385 };
1386 });
1387 };
1388
1389 function css(el, prop) {
1390 return parseInt($.css(el[0], prop)) || 0;
1391 };
1392 function width(el) {
1393 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1394 };
1395 function height(el) {
1396 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1397 };
1398
1399 })(jQuery);
1400
1401
Robert Lyd2dd6e52012-11-29 21:28:48 -08001402/*
Scott Mainf5089842012-08-14 16:31:07 -07001403 * dacSlideshow 1.0
1404 * Used on develop/index.html for side-sliding tabs
1405 *
1406 * Sample usage:
1407 * HTML -
1408 * <div class="slideshow-container">
1409 * <a href="" class="slideshow-prev">Prev</a>
1410 * <a href="" class="slideshow-next">Next</a>
1411 * <ul>
1412 * <li class="item"><img src="images/marquee1.jpg"></li>
1413 * <li class="item"><img src="images/marquee2.jpg"></li>
1414 * <li class="item"><img src="images/marquee3.jpg"></li>
1415 * <li class="item"><img src="images/marquee4.jpg"></li>
1416 * </ul>
1417 * </div>
1418 *
1419 * <script type="text/javascript">
1420 * $('.slideshow-container').dacSlideshow({
1421 * auto: true,
1422 * btnPrev: '.slideshow-prev',
1423 * btnNext: '.slideshow-next'
1424 * });
1425 * </script>
1426 *
1427 * Options:
1428 * btnPrev: optional identifier for previous button
1429 * btnNext: optional identifier for next button
1430 * auto: whether or not to auto-proceed
1431 * speed: animation speed
1432 * autoTime: time between auto-rotation
1433 * easing: easing function for transition
1434 * start: item to select by default
1435 * scroll: direction to scroll in
1436 * pagination: whether or not to include dotted pagination
1437 *
1438 */
1439 (function($) {
1440 $.fn.dacTabbedList = function(o) {
1441
1442 //Options - see above
1443 o = $.extend({
1444 speed : 250,
1445 easing: null,
1446 nav_id: null,
1447 frame_id: null
1448 }, o || {});
1449
1450 //Set up a carousel for each
1451 return this.each(function() {
1452
1453 var curr = 0;
1454 var running = false;
1455 var animCss = "margin-left";
1456 var sizeCss = "width";
1457 var div = $(this);
1458
1459 var nav = $(o.nav_id, div);
1460 var nav_li = $("li", nav);
1461 var nav_size = nav_li.size();
1462 var frame = div.find(o.frame_id);
1463 var content_width = $(frame).find('ul').width();
1464 //Buttons
1465 $(nav_li).click(function(e) {
1466 go($(nav_li).index($(this)));
1467 })
1468
1469 //Go to an item
1470 function go(to) {
1471 if(!running) {
1472 curr = to;
1473 running = true;
1474
1475 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1476 function() {
1477 running = false;
1478 }
1479 );
1480
1481
1482 nav_li.removeClass('active');
1483 nav_li.eq(to).addClass('active');
1484
1485
1486 }
1487 return false;
1488 };
1489 });
1490 };
1491
1492 function css(el, prop) {
1493 return parseInt($.css(el[0], prop)) || 0;
1494 };
1495 function width(el) {
1496 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1497 };
1498 function height(el) {
1499 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1500 };
1501
1502 })(jQuery);
1503
1504
1505
1506
1507
1508/* ######################################################## */
1509/* ################ SEARCH SUGGESTIONS ################## */
1510/* ######################################################## */
1511
1512
Scott Main7e447ed2013-02-19 17:22:37 -08001513
Scott Main0e76e7e2013-03-12 10:24:07 -07001514var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1515var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1516
Scott Mainf5089842012-08-14 16:31:07 -07001517var gMatches = new Array();
1518var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001519var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001520var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1521var gListLength = 0;
1522
1523
1524var gGoogleMatches = new Array();
1525var ROW_COUNT_GOOGLE = 15; // max number of results in list
1526var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001527
Scott Main0e76e7e2013-03-12 10:24:07 -07001528var gDocsMatches = new Array();
1529var ROW_COUNT_DOCS = 100; // max number of results in list
1530var gDocsListLength = 0;
1531
Scott Mainde295272013-03-25 15:48:35 -07001532function onSuggestionClick(link) {
1533 // When user clicks a suggested document, track it
1534 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1535 'from: ' + $("#search_autocomplete").val()]);
1536}
1537
Scott Mainf5089842012-08-14 16:31:07 -07001538function set_item_selected($li, selected)
1539{
1540 if (selected) {
1541 $li.attr('class','jd-autocomplete jd-selected');
1542 } else {
1543 $li.attr('class','jd-autocomplete');
1544 }
1545}
1546
1547function set_item_values(toroot, $li, match)
1548{
1549 var $link = $('a',$li);
1550 $link.html(match.__hilabel || match.label);
1551 $link.attr('href',toroot + match.link);
1552}
1553
Scott Main0e76e7e2013-03-12 10:24:07 -07001554function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001555 var $li = $("<li class='jd-autocomplete'></li>");
1556 $list.append($li);
1557
1558 $li.mousedown(function() {
1559 window.location = this.firstChild.getAttribute("href");
1560 });
1561 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001562 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001563 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001564 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1565 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001566 });
Scott Mainde295272013-03-25 15:48:35 -07001567 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001568 $li.attr('class','show-item');
1569 return $li;
1570}
1571
Scott Mainf5089842012-08-14 16:31:07 -07001572function sync_selection_table(toroot)
1573{
Scott Mainf5089842012-08-14 16:31:07 -07001574 var $li; //list item jquery object
1575 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001576
Scott Main0e76e7e2013-03-12 10:24:07 -07001577 // if there are NO results at all, hide all columns
1578 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1579 $('.suggest-card').hide(300);
1580 return;
1581 }
1582
1583 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001584 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001585 // reveal suggestion list
1586 $('.suggest-card.dummy').show();
1587 $('.suggest-card.reference').show();
1588 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001589
Scott Main0e76e7e2013-03-12 10:24:07 -07001590 // reset the lists
1591 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001592
Scott Main0e76e7e2013-03-12 10:24:07 -07001593 // ########### ANDROID RESULTS #############
1594 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001595
Scott Main0e76e7e2013-03-12 10:24:07 -07001596 // determine android results to show
1597 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1598 gMatches.length : ROW_COUNT_FRAMEWORK;
1599 for (i=0; i<gListLength; i++) {
1600 var $li = new_suggestion($(".suggest-card.reference ul"));
1601 set_item_values(toroot, $li, gMatches[i]);
1602 set_item_selected($li, i == gSelectedIndex);
1603 }
1604 }
Scott Main7e447ed2013-02-19 17:22:37 -08001605
Scott Main0e76e7e2013-03-12 10:24:07 -07001606 // ########### GOOGLE RESULTS #############
1607 if (gGoogleMatches.length > 0) {
1608 // show header for list
1609 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001610
Scott Main0e76e7e2013-03-12 10:24:07 -07001611 // determine google results to show
1612 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1613 for (i=0; i<gGoogleListLength; i++) {
1614 var $li = new_suggestion($(".suggest-card.reference ul"));
1615 set_item_values(toroot, $li, gGoogleMatches[i]);
1616 set_item_selected($li, i == gSelectedIndex);
1617 }
1618 }
Scott Mainf5089842012-08-14 16:31:07 -07001619 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001620 $('.suggest-card.reference').hide();
1621 $('.suggest-card.dummy').hide();
1622 }
1623
1624 // ########### JD DOC RESULTS #############
1625 if (gDocsMatches.length > 0) {
1626 // reset the lists
1627 $(".search_filtered_wrapper.docs li").remove();
1628
1629 // determine google results to show
1630 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1631 for (i=0; i<gDocsListLength; i++) {
1632 var sugg = gDocsMatches[i];
1633 var $li;
1634 if (sugg.type == "design") {
1635 $li = new_suggestion($(".suggest-card.design ul"));
1636 } else
1637 if (sugg.type == "distribute") {
1638 $li = new_suggestion($(".suggest-card.distribute ul"));
1639 } else
1640 if (sugg.type == "training") {
1641 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1642 } else
1643 if (sugg.type == "guide"||"google") {
1644 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1645 } else {
1646 continue;
1647 }
1648
1649 set_item_values(toroot, $li, sugg);
1650 set_item_selected($li, i == gSelectedIndex);
1651 }
1652
1653 // add heading and show or hide card
1654 if ($(".suggest-card.design li").length > 0) {
1655 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1656 $(".suggest-card.design").show(300);
1657 } else {
1658 $('.suggest-card.design').hide(300);
1659 }
1660 if ($(".suggest-card.distribute li").length > 0) {
1661 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1662 $(".suggest-card.distribute").show(300);
1663 } else {
1664 $('.suggest-card.distribute').hide(300);
1665 }
1666 if ($(".child-card.guides li").length > 0) {
1667 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1668 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1669 }
1670 if ($(".child-card.training li").length > 0) {
1671 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1672 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1673 }
1674
1675 if ($(".suggest-card.develop li").length > 0) {
1676 $(".suggest-card.develop").show(300);
1677 } else {
1678 $('.suggest-card.develop').hide(300);
1679 }
1680
1681 } else {
1682 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001683 }
1684}
1685
Scott Main0e76e7e2013-03-12 10:24:07 -07001686/** Called by the search input's onkeydown and onkeyup events.
1687 * Handles navigation with keyboard arrows, Enter key to invoke search,
1688 * otherwise invokes search suggestions on key-up event.
1689 * @param e The JS event
1690 * @param kd True if the event is key-down
1691 * @param toroot A string for the site's root path
1692 * @returns True if the event should bubble up
1693 */
Scott Mainf5089842012-08-14 16:31:07 -07001694function search_changed(e, kd, toroot)
1695{
1696 var search = document.getElementById("search_autocomplete");
1697 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001698 // get the ul hosting the currently selected item
1699 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1700 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1701 var $selectedUl = $columns[gSelectedColumn];
1702
Scott Mainf5089842012-08-14 16:31:07 -07001703 // show/hide the close button
1704 if (text != '') {
1705 $(".search .close").removeClass("hide");
1706 } else {
1707 $(".search .close").addClass("hide");
1708 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001709 // 27 = esc
1710 if (e.keyCode == 27) {
1711 // close all search results
1712 if (kd) $('.search .close').trigger('click');
1713 return true;
1714 }
Scott Mainf5089842012-08-14 16:31:07 -07001715 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001716 else if (e.keyCode == 13) {
1717 if (gSelectedIndex < 0) {
1718 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001719 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1720 // if results aren't showing (and text not empty), return true to allow search to execute
Scott Mainf5089842012-08-14 16:31:07 -07001721 return true;
1722 } else {
1723 // otherwise, results are already showing, so allow ajax to auto refresh the results
1724 // and ignore this Enter press to avoid the reload.
1725 return false;
1726 }
1727 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001728 // click the link corresponding to selected item
1729 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001730 return false;
1731 }
1732 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001733 // Stop here if Google results are showing
1734 else if ($("#searchResults").is(":visible")) {
1735 return true;
1736 }
1737 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001738 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001739 // if the next item is a header, skip it
1740 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001741 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001742 }
1743 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001744 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001745 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001746 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1747 // If user reaches top, reset selected column
1748 if (gSelectedIndex < 0) {
1749 gSelectedColumn = -1;
1750 }
Scott Mainf5089842012-08-14 16:31:07 -07001751 }
1752 return false;
1753 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001754 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001755 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001756 // if the next item is a header, skip it
1757 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001758 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001759 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001760 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1761 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1762 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001763 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001764 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001765 }
1766 return false;
1767 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001768 // Consider left/right arrow navigation
1769 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1770 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1771 // 37 LEFT ARROW
1772 // go left only if current column is not left-most column (last column)
1773 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1774 $('li', $selectedUl).removeClass('jd-selected');
1775 gSelectedColumn++;
1776 $selectedUl = $columns[gSelectedColumn];
1777 // keep or reset the selected item to last item as appropriate
1778 gSelectedIndex = gSelectedIndex >
1779 $("li", $selectedUl).length-1 ?
1780 $("li", $selectedUl).length-1 : gSelectedIndex;
1781 // if the corresponding item is a header, move down
1782 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1783 gSelectedIndex++;
1784 }
1785 // set item selected
1786 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1787 return false;
1788 }
1789 // 39 RIGHT ARROW
1790 // go right only if current column is not the right-most column (first column)
1791 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1792 $('li', $selectedUl).removeClass('jd-selected');
1793 gSelectedColumn--;
1794 $selectedUl = $columns[gSelectedColumn];
1795 // keep or reset the selected item to last item as appropriate
1796 gSelectedIndex = gSelectedIndex >
1797 $("li", $selectedUl).length-1 ?
1798 $("li", $selectedUl).length-1 : gSelectedIndex;
1799 // if the corresponding item is a header, move down
1800 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1801 gSelectedIndex++;
1802 }
1803 // set item selected
1804 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1805 return false;
1806 }
1807 }
1808
Scott Main7e447ed2013-02-19 17:22:37 -08001809 // if key-up event and not arrow down/up,
1810 // read the search query and add suggestsions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001811 else if (!kd && (e.keyCode != 40)
1812 && (e.keyCode != 38)
1813 && (e.keyCode != 37)
1814 && (e.keyCode != 39)) {
1815 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001816 gMatches = new Array();
1817 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001818 gGoogleMatches = new Array();
1819 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001820 gDocsMatches = new Array();
1821 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001822
1823 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001824 for (var i=0; i<DATA.length; i++) {
1825 var s = DATA[i];
1826 if (text.length != 0 &&
1827 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1828 gMatches[matchedCount] = s;
1829 matchedCount++;
1830 }
1831 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001832 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001833 for (var i=0; i<gMatches.length; i++) {
1834 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001835 }
1836
1837
1838 // Search for Google matches
1839 for (var i=0; i<GOOGLE_DATA.length; i++) {
1840 var s = GOOGLE_DATA[i];
1841 if (text.length != 0 &&
1842 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1843 gGoogleMatches[matchedCountGoogle] = s;
1844 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001845 }
1846 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001847 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001848 for (var i=0; i<gGoogleMatches.length; i++) {
1849 var s = gGoogleMatches[i];
1850 }
1851
Scott Mainf5089842012-08-14 16:31:07 -07001852 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001853
1854
1855
1856 // Search for JD docs
1857 if (text.length >= 3) {
1858 for (var i=0; i<JD_DATA.length; i++) {
1859 // Regex to match only the beginning of a word
1860 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1861 // current search comparison, with counters for tag and title,
1862 // used later to improve ranking
1863 var s = JD_DATA[i];
1864 s.matched_tag = 0;
1865 s.matched_title = 0;
1866 var matched = false;
1867
1868 // Check if query matches any tags; work backwards toward 1 to assist ranking
1869 for (var j = s.tags.length - 1; j >= 0; j--) {
1870 // it matches a tag
1871 if (s.tags[j].toLowerCase().match(textRegex)) {
1872 matched = true;
1873 s.matched_tag = j + 1; // add 1 to index position
1874 }
1875 }
1876 // Don't consider doc title for lessons (only for class landing pages)
1877 // ...it is not a training lesson (or is but has matched a tag)
1878 if (!(s.type == "training" && s.link.indexOf("index.html") == -1) || matched) {
1879 // it matches the doc title
1880 if (s.label.toLowerCase().match(textRegex)) {
1881 matched = true;
1882 s.matched_title = 1;
1883 }
1884 }
1885 if (matched) {
1886 gDocsMatches[matchedCountDocs] = s;
1887 matchedCountDocs++;
1888 }
1889 }
1890 rank_autocomplete_doc_results(text, gDocsMatches);
1891 }
1892
1893 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07001894 sync_selection_table(toroot);
1895 return true; // allow the event to bubble up to the search api
1896 }
1897}
1898
Scott Main0e76e7e2013-03-12 10:24:07 -07001899/* Order the jd doc result list based on match quality */
1900function rank_autocomplete_doc_results(query, matches) {
1901 query = query || '';
1902 if (!matches || !matches.length)
1903 return;
1904
1905 var _resultScoreFn = function(match) {
1906 var score = 1.0;
1907
1908 // if the query matched a tag
1909 if (match.matched_tag > 0) {
1910 // multiply score by factor relative to position in tags list (max of 3)
1911 score *= 3 / match.matched_tag;
1912
1913 // if it also matched the title
1914 if (match.matched_title > 0) {
1915 score *= 2;
1916 }
1917 } else if (match.matched_title > 0) {
1918 score *= 3;
1919 }
1920
1921 return score;
1922 };
1923
1924 for (var i=0; i<matches.length; i++) {
1925 matches[i].__resultScore = _resultScoreFn(matches[i]);
1926 }
1927
1928 matches.sort(function(a,b){
1929 var n = b.__resultScore - a.__resultScore;
1930 if (n == 0) // lexicographical sort if scores are the same
1931 n = (a.label < b.label) ? -1 : 1;
1932 return n;
1933 });
1934}
1935
Scott Main7e447ed2013-02-19 17:22:37 -08001936/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07001937function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07001938 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08001939 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07001940 return;
1941
1942 // helper function that gets the last occurence index of the given regex
1943 // in the given string, or -1 if not found
1944 var _lastSearch = function(s, re) {
1945 if (s == '')
1946 return -1;
1947 var l = -1;
1948 var tmp;
1949 while ((tmp = s.search(re)) >= 0) {
1950 if (l < 0) l = 0;
1951 l += tmp;
1952 s = s.substr(tmp + 1);
1953 }
1954 return l;
1955 };
1956
1957 // helper function that counts the occurrences of a given character in
1958 // a given string
1959 var _countChar = function(s, c) {
1960 var n = 0;
1961 for (var i=0; i<s.length; i++)
1962 if (s.charAt(i) == c) ++n;
1963 return n;
1964 };
1965
1966 var queryLower = query.toLowerCase();
1967 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1968 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1969 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1970
1971 var _resultScoreFn = function(result) {
1972 // scores are calculated based on exact and prefix matches,
1973 // and then number of path separators (dots) from the last
1974 // match (i.e. favoring classes and deep package names)
1975 var score = 1.0;
1976 var labelLower = result.label.toLowerCase();
1977 var t;
1978 t = _lastSearch(labelLower, partExactAlnumRE);
1979 if (t >= 0) {
1980 // exact part match
1981 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1982 score *= 200 / (partsAfter + 1);
1983 } else {
1984 t = _lastSearch(labelLower, partPrefixAlnumRE);
1985 if (t >= 0) {
1986 // part prefix match
1987 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1988 score *= 20 / (partsAfter + 1);
1989 }
1990 }
1991
1992 return score;
1993 };
1994
Scott Main7e447ed2013-02-19 17:22:37 -08001995 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001996 // if the API is deprecated, default score is 0; otherwise, perform scoring
1997 if (matches[i].deprecated == "true") {
1998 matches[i].__resultScore = 0;
1999 } else {
2000 matches[i].__resultScore = _resultScoreFn(matches[i]);
2001 }
Scott Mainf5089842012-08-14 16:31:07 -07002002 }
2003
Scott Main7e447ed2013-02-19 17:22:37 -08002004 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002005 var n = b.__resultScore - a.__resultScore;
2006 if (n == 0) // lexicographical sort if scores are the same
2007 n = (a.label < b.label) ? -1 : 1;
2008 return n;
2009 });
2010}
2011
Scott Main7e447ed2013-02-19 17:22:37 -08002012/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002013function highlight_autocomplete_result_labels(query) {
2014 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002015 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002016 return;
2017
2018 var queryLower = query.toLowerCase();
2019 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2020 var queryRE = new RegExp(
2021 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2022 for (var i=0; i<gMatches.length; i++) {
2023 gMatches[i].__hilabel = gMatches[i].label.replace(
2024 queryRE, '<b>$1</b>');
2025 }
Scott Main7e447ed2013-02-19 17:22:37 -08002026 for (var i=0; i<gGoogleMatches.length; i++) {
2027 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2028 queryRE, '<b>$1</b>');
2029 }
Scott Mainf5089842012-08-14 16:31:07 -07002030}
2031
2032function search_focus_changed(obj, focused)
2033{
2034 if (!focused) {
2035 if(obj.value == ""){
2036 $(".search .close").addClass("hide");
2037 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002038 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002039 }
2040}
2041
2042function submit_search() {
2043 var query = document.getElementById('search_autocomplete').value;
2044 location.hash = 'q=' + query;
2045 loadSearchResults();
2046 $("#searchResults").slideDown('slow');
2047 return false;
2048}
2049
2050
2051function hideResults() {
2052 $("#searchResults").slideUp();
2053 $(".search .close").addClass("hide");
2054 location.hash = '';
2055
2056 $("#search_autocomplete").val("").blur();
2057
2058 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2059 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002060
2061 // forcefully regain key-up event control (previously jacked by search api)
2062 $("#search_autocomplete").keyup(function(event) {
2063 return search_changed(event, false, toRoot);
2064 });
2065
Scott Mainf5089842012-08-14 16:31:07 -07002066 return false;
2067}
2068
2069
2070
2071/* ########################################################## */
2072/* ################ CUSTOM SEARCH ENGINE ################## */
2073/* ########################################################## */
2074
Scott Mainf5089842012-08-14 16:31:07 -07002075var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002076google.load('search', '1', {"callback" : function() {
2077 searchControl = new google.search.SearchControl();
2078 } });
Scott Mainf5089842012-08-14 16:31:07 -07002079
2080function loadSearchResults() {
2081 document.getElementById("search_autocomplete").style.color = "#000";
2082
Scott Mainf5089842012-08-14 16:31:07 -07002083 searchControl = new google.search.SearchControl();
2084
2085 // use our existing search form and use tabs when multiple searchers are used
2086 drawOptions = new google.search.DrawOptions();
2087 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2088 drawOptions.setInput(document.getElementById("search_autocomplete"));
2089
2090 // configure search result options
2091 searchOptions = new google.search.SearcherOptions();
2092 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2093
2094 // configure each of the searchers, for each tab
2095 devSiteSearcher = new google.search.WebSearch();
2096 devSiteSearcher.setUserDefinedLabel("All");
2097 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2098
2099 designSearcher = new google.search.WebSearch();
2100 designSearcher.setUserDefinedLabel("Design");
2101 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2102
2103 trainingSearcher = new google.search.WebSearch();
2104 trainingSearcher.setUserDefinedLabel("Training");
2105 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2106
2107 guidesSearcher = new google.search.WebSearch();
2108 guidesSearcher.setUserDefinedLabel("Guides");
2109 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2110
2111 referenceSearcher = new google.search.WebSearch();
2112 referenceSearcher.setUserDefinedLabel("Reference");
2113 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2114
Scott Maindf08ada2012-12-03 08:54:37 -08002115 googleSearcher = new google.search.WebSearch();
2116 googleSearcher.setUserDefinedLabel("Google Services");
2117 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2118
Scott Mainf5089842012-08-14 16:31:07 -07002119 blogSearcher = new google.search.WebSearch();
2120 blogSearcher.setUserDefinedLabel("Blog");
2121 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2122
2123 // add each searcher to the search control
2124 searchControl.addSearcher(devSiteSearcher, searchOptions);
2125 searchControl.addSearcher(designSearcher, searchOptions);
2126 searchControl.addSearcher(trainingSearcher, searchOptions);
2127 searchControl.addSearcher(guidesSearcher, searchOptions);
2128 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002129 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002130 searchControl.addSearcher(blogSearcher, searchOptions);
2131
2132 // configure result options
2133 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2134 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2135 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2136 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2137
2138 // upon ajax search, refresh the url and search title
2139 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2140 updateResultTitle(query);
2141 var query = document.getElementById('search_autocomplete').value;
2142 location.hash = 'q=' + query;
2143 });
2144
Scott Mainde295272013-03-25 15:48:35 -07002145 // once search results load, set up click listeners
2146 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2147 addResultClickListeners();
2148 });
2149
Scott Mainf5089842012-08-14 16:31:07 -07002150 // draw the search results box
2151 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2152
2153 // get query and execute the search
2154 searchControl.execute(decodeURI(getQuery(location.hash)));
2155
2156 document.getElementById("search_autocomplete").focus();
2157 addTabListeners();
2158}
2159// End of loadSearchResults
2160
2161
2162google.setOnLoadCallback(function(){
2163 if (location.hash.indexOf("q=") == -1) {
2164 // if there's no query in the url, don't search and make sure results are hidden
2165 $('#searchResults').hide();
2166 return;
2167 } else {
2168 // first time loading search results for this page
2169 $('#searchResults').slideDown('slow');
2170 $(".search .close").removeClass("hide");
2171 loadSearchResults();
2172 }
2173}, true);
2174
2175// when an event on the browser history occurs (back, forward, load) requery hash and do search
2176$(window).hashchange( function(){
2177 // Exit if the hash isn't a search query or there's an error in the query
2178 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2179 // If the results pane is open, close it.
2180 if (!$("#searchResults").is(":hidden")) {
2181 hideResults();
2182 }
2183 return;
2184 }
2185
2186 // Otherwise, we have a search to do
2187 var query = decodeURI(getQuery(location.hash));
2188 searchControl.execute(query);
2189 $('#searchResults').slideDown('slow');
2190 $("#search_autocomplete").focus();
2191 $(".search .close").removeClass("hide");
2192
2193 updateResultTitle(query);
2194});
2195
2196function updateResultTitle(query) {
2197 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2198}
2199
2200// forcefully regain key-up event control (previously jacked by search api)
2201$("#search_autocomplete").keyup(function(event) {
2202 return search_changed(event, false, toRoot);
2203});
2204
2205// add event listeners to each tab so we can track the browser history
2206function addTabListeners() {
2207 var tabHeaders = $(".gsc-tabHeader");
2208 for (var i = 0; i < tabHeaders.length; i++) {
2209 $(tabHeaders[i]).attr("id",i).click(function() {
2210 /*
2211 // make a copy of the page numbers for the search left pane
2212 setTimeout(function() {
2213 // remove any residual page numbers
2214 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
2215 // move the page numbers to the left position; make a clone,
2216 // because the element is drawn to the DOM only once
2217 // and because we're going to remove it (previous line),
2218 // we need it to be available to move again as the user navigates
2219 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2220 .clone().appendTo('#searchResults .gsc-tabsArea');
2221 }, 200);
2222 */
2223 });
2224 }
2225 setTimeout(function(){$(tabHeaders[0]).click()},200);
2226}
2227
Scott Mainde295272013-03-25 15:48:35 -07002228// add analytics tracking events to each result link
2229function addResultClickListeners() {
2230 $("#searchResults a.gs-title").each(function(index, link) {
2231 // When user clicks enter for Google search results, track it
2232 $(link).click(function() {
2233 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2234 'from: ' + $("#search_autocomplete").val()]);
2235 });
2236 });
2237}
2238
Scott Mainf5089842012-08-14 16:31:07 -07002239
2240function getQuery(hash) {
2241 var queryParts = hash.split('=');
2242 return queryParts[1];
2243}
2244
2245/* returns the given string with all HTML brackets converted to entities
2246 TODO: move this to the site's JS library */
2247function escapeHTML(string) {
2248 return string.replace(/</g,"&lt;")
2249 .replace(/>/g,"&gt;");
2250}
2251
2252
2253
2254
2255
2256
2257
2258/* ######################################################## */
2259/* ################# JAVADOC REFERENCE ################### */
2260/* ######################################################## */
2261
Scott Main65511c02012-09-07 15:51:32 -07002262/* Initialize some droiddoc stuff, but only if we're in the reference */
Robert Ly67d75f12012-12-03 12:53:42 -08002263if (location.pathname.indexOf("/reference")) {
2264 if(!location.pathname.indexOf("/reference-gms/packages.html")
2265 && !location.pathname.indexOf("/reference-gcm/packages.html")
2266 && !location.pathname.indexOf("/reference/com/google") == 0) {
2267 $(document).ready(function() {
2268 // init available apis based on user pref
2269 changeApiLevel();
2270 initSidenavHeightResize()
2271 });
2272 }
Scott Main65511c02012-09-07 15:51:32 -07002273}
Scott Mainf5089842012-08-14 16:31:07 -07002274
2275var API_LEVEL_COOKIE = "api_level";
2276var minLevel = 1;
2277var maxLevel = 1;
2278
2279/******* SIDENAV DIMENSIONS ************/
2280
2281 function initSidenavHeightResize() {
2282 // Change the drag bar size to nicely fit the scrollbar positions
2283 var $dragBar = $(".ui-resizable-s");
2284 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
2285
2286 $( "#resize-packages-nav" ).resizable({
2287 containment: "#nav-panels",
2288 handles: "s",
2289 alsoResize: "#packages-nav",
2290 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2291 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2292 });
2293
2294 }
2295
2296function updateSidenavFixedWidth() {
2297 if (!navBarIsFixed) return;
2298 $('#devdoc-nav').css({
2299 'width' : $('#side-nav').css('width'),
2300 'margin' : $('#side-nav').css('margin')
2301 });
2302 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
2303
2304 initSidenavHeightResize();
2305}
2306
2307function updateSidenavFullscreenWidth() {
2308 if (!navBarIsFixed) return;
2309 $('#devdoc-nav').css({
2310 'width' : $('#side-nav').css('width'),
2311 'margin' : $('#side-nav').css('margin')
2312 });
2313 $('#devdoc-nav .totop').css({'left': 'inherit'});
2314
2315 initSidenavHeightResize();
2316}
2317
2318function buildApiLevelSelector() {
2319 maxLevel = SINCE_DATA.length;
2320 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2321 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2322
2323 minLevel = parseInt($("#doc-api-level").attr("class"));
2324 // Handle provisional api levels; the provisional level will always be the highest possible level
2325 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2326 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2327 if (isNaN(minLevel) && minLevel.length) {
2328 minLevel = maxLevel;
2329 }
2330 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2331 for (var i = maxLevel-1; i >= 0; i--) {
2332 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2333 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2334 select.append(option);
2335 }
2336
2337 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2338 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2339 selectedLevelItem.setAttribute('selected',true);
2340}
2341
2342function changeApiLevel() {
2343 maxLevel = SINCE_DATA.length;
2344 var selectedLevel = maxLevel;
2345
2346 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2347 toggleVisisbleApis(selectedLevel, "body");
2348
2349 var date = new Date();
2350 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2351 var expiration = date.toGMTString();
2352 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2353
2354 if (selectedLevel < minLevel) {
2355 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002356 $("#naMessage").show().html("<div><p><strong>This " + thing
2357 + " requires API level " + minLevel + " or higher.</strong></p>"
2358 + "<p>This document is hidden because your selected API level for the documentation is "
2359 + selectedLevel + ". You can change the documentation API level with the selector "
2360 + "above the left navigation.</p>"
2361 + "<p>For more information about specifying the API level your app requires, "
2362 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2363 + ">Supporting Different Platform Versions</a>.</p>"
2364 + "<input type='button' value='OK, make this page visible' "
2365 + "title='Change the API level to " + minLevel + "' "
2366 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2367 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002368 } else {
2369 $("#naMessage").hide();
2370 }
2371}
2372
2373function toggleVisisbleApis(selectedLevel, context) {
2374 var apis = $(".api",context);
2375 apis.each(function(i) {
2376 var obj = $(this);
2377 var className = obj.attr("class");
2378 var apiLevelIndex = className.lastIndexOf("-")+1;
2379 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2380 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2381 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2382 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2383 return;
2384 }
2385 apiLevel = parseInt(apiLevel);
2386
2387 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2388 var selectedLevelNum = parseInt(selectedLevel)
2389 var apiLevelNum = parseInt(apiLevel);
2390 if (isNaN(apiLevelNum)) {
2391 apiLevelNum = maxLevel;
2392 }
2393
2394 // Grey things out that aren't available and give a tooltip title
2395 if (apiLevelNum > selectedLevelNum) {
2396 obj.addClass("absent").attr("title","Requires API Level \""
2397 + apiLevel + "\" or higher");
2398 }
2399 else obj.removeClass("absent").removeAttr("title");
2400 });
2401}
2402
2403
2404
2405
2406/* ################# SIDENAV TREE VIEW ################### */
2407
2408function new_node(me, mom, text, link, children_data, api_level)
2409{
2410 var node = new Object();
2411 node.children = Array();
2412 node.children_data = children_data;
2413 node.depth = mom.depth + 1;
2414
2415 node.li = document.createElement("li");
2416 mom.get_children_ul().appendChild(node.li);
2417
2418 node.label_div = document.createElement("div");
2419 node.label_div.className = "label";
2420 if (api_level != null) {
2421 $(node.label_div).addClass("api");
2422 $(node.label_div).addClass("api-level-"+api_level);
2423 }
2424 node.li.appendChild(node.label_div);
2425
2426 if (children_data != null) {
2427 node.expand_toggle = document.createElement("a");
2428 node.expand_toggle.href = "javascript:void(0)";
2429 node.expand_toggle.onclick = function() {
2430 if (node.expanded) {
2431 $(node.get_children_ul()).slideUp("fast");
2432 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2433 node.expanded = false;
2434 } else {
2435 expand_node(me, node);
2436 }
2437 };
2438 node.label_div.appendChild(node.expand_toggle);
2439
2440 node.plus_img = document.createElement("img");
2441 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2442 node.plus_img.className = "plus";
2443 node.plus_img.width = "8";
2444 node.plus_img.border = "0";
2445 node.expand_toggle.appendChild(node.plus_img);
2446
2447 node.expanded = false;
2448 }
2449
2450 var a = document.createElement("a");
2451 node.label_div.appendChild(a);
2452 node.label = document.createTextNode(text);
2453 a.appendChild(node.label);
2454 if (link) {
2455 a.href = me.toroot + link;
2456 } else {
2457 if (children_data != null) {
2458 a.className = "nolink";
2459 a.href = "javascript:void(0)";
2460 a.onclick = node.expand_toggle.onclick;
2461 // This next line shouldn't be necessary. I'll buy a beer for the first
2462 // person who figures out how to remove this line and have the link
2463 // toggle shut on the first try. --joeo@android.com
2464 node.expanded = false;
2465 }
2466 }
2467
2468
2469 node.children_ul = null;
2470 node.get_children_ul = function() {
2471 if (!node.children_ul) {
2472 node.children_ul = document.createElement("ul");
2473 node.children_ul.className = "children_ul";
2474 node.children_ul.style.display = "none";
2475 node.li.appendChild(node.children_ul);
2476 }
2477 return node.children_ul;
2478 };
2479
2480 return node;
2481}
2482
Robert Lyd2dd6e52012-11-29 21:28:48 -08002483
2484
2485
Scott Mainf5089842012-08-14 16:31:07 -07002486function expand_node(me, node)
2487{
2488 if (node.children_data && !node.expanded) {
2489 if (node.children_visited) {
2490 $(node.get_children_ul()).slideDown("fast");
2491 } else {
2492 get_node(me, node);
2493 if ($(node.label_div).hasClass("absent")) {
2494 $(node.get_children_ul()).addClass("absent");
2495 }
2496 $(node.get_children_ul()).slideDown("fast");
2497 }
2498 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2499 node.expanded = true;
2500
2501 // perform api level toggling because new nodes are new to the DOM
2502 var selectedLevel = $("#apiLevelSelector option:selected").val();
2503 toggleVisisbleApis(selectedLevel, "#side-nav");
2504 }
2505}
2506
2507function get_node(me, mom)
2508{
2509 mom.children_visited = true;
2510 for (var i in mom.children_data) {
2511 var node_data = mom.children_data[i];
2512 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2513 node_data[2], node_data[3]);
2514 }
2515}
2516
2517function this_page_relative(toroot)
2518{
2519 var full = document.location.pathname;
2520 var file = "";
2521 if (toroot.substr(0, 1) == "/") {
2522 if (full.substr(0, toroot.length) == toroot) {
2523 return full.substr(toroot.length);
2524 } else {
2525 // the file isn't under toroot. Fail.
2526 return null;
2527 }
2528 } else {
2529 if (toroot != "./") {
2530 toroot = "./" + toroot;
2531 }
2532 do {
2533 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2534 var pos = full.lastIndexOf("/");
2535 file = full.substr(pos) + file;
2536 full = full.substr(0, pos);
2537 toroot = toroot.substr(0, toroot.length-3);
2538 }
2539 } while (toroot != "" && toroot != "/");
2540 return file.substr(1);
2541 }
2542}
2543
2544function find_page(url, data)
2545{
2546 var nodes = data;
2547 var result = null;
2548 for (var i in nodes) {
2549 var d = nodes[i];
2550 if (d[1] == url) {
2551 return new Array(i);
2552 }
2553 else if (d[2] != null) {
2554 result = find_page(url, d[2]);
2555 if (result != null) {
2556 return (new Array(i).concat(result));
2557 }
2558 }
2559 }
2560 return null;
2561}
2562
Scott Mainf5089842012-08-14 16:31:07 -07002563function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002564 // load json file for navtree data
2565 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2566 // when the file is loaded, initialize the tree
2567 if(jqxhr.status === 200) {
2568 init_navtree("tree-list", toroot, NAVTREE_DATA);
2569 }
2570 });
Scott Mainf5089842012-08-14 16:31:07 -07002571
2572 // perform api level toggling because because the whole tree is new to the DOM
2573 var selectedLevel = $("#apiLevelSelector option:selected").val();
2574 toggleVisisbleApis(selectedLevel, "#side-nav");
2575}
2576
2577function init_navtree(navtree_id, toroot, root_nodes)
2578{
2579 var me = new Object();
2580 me.toroot = toroot;
2581 me.node = new Object();
2582
2583 me.node.li = document.getElementById(navtree_id);
2584 me.node.children_data = root_nodes;
2585 me.node.children = new Array();
2586 me.node.children_ul = document.createElement("ul");
2587 me.node.get_children_ul = function() { return me.node.children_ul; };
2588 //me.node.children_ul.className = "children_ul";
2589 me.node.li.appendChild(me.node.children_ul);
2590 me.node.depth = 0;
2591
2592 get_node(me, me.node);
2593
2594 me.this_page = this_page_relative(toroot);
2595 me.breadcrumbs = find_page(me.this_page, root_nodes);
2596 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2597 var mom = me.node;
2598 for (var i in me.breadcrumbs) {
2599 var j = me.breadcrumbs[i];
2600 mom = mom.children[j];
2601 expand_node(me, mom);
2602 }
2603 mom.label_div.className = mom.label_div.className + " selected";
2604 addLoadEvent(function() {
2605 scrollIntoView("nav-tree");
2606 });
2607 }
2608}
2609
Robert Lyd2dd6e52012-11-29 21:28:48 -08002610/* TODO: eliminate redundancy with non-google functions */
2611function init_google_navtree(navtree_id, toroot, root_nodes)
2612{
2613 var me = new Object();
2614 me.toroot = toroot;
2615 me.node = new Object();
2616
2617 me.node.li = document.getElementById(navtree_id);
2618 me.node.children_data = root_nodes;
2619 me.node.children = new Array();
2620 me.node.children_ul = document.createElement("ul");
2621 me.node.get_children_ul = function() { return me.node.children_ul; };
2622 //me.node.children_ul.className = "children_ul";
2623 me.node.li.appendChild(me.node.children_ul);
2624 me.node.depth = 0;
2625
2626 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002627}
2628
2629function new_google_node(me, mom, text, link, children_data, api_level)
2630{
2631 var node = new Object();
2632 var child;
2633 node.children = Array();
2634 node.children_data = children_data;
2635 node.depth = mom.depth + 1;
2636 node.get_children_ul = function() {
2637 if (!node.children_ul) {
Scott Mainac71b2b2012-11-30 14:40:58 -08002638 node.children_ul = document.createElement("ul");
2639 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002640 node.li.appendChild(node.children_ul);
2641 }
2642 return node.children_ul;
2643 };
2644 node.li = document.createElement("li");
2645
2646 mom.get_children_ul().appendChild(node.li);
2647
2648
2649 if(link) {
2650 child = document.createElement("a");
2651
2652 }
2653 else {
2654 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08002655 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002656
2657 }
2658 if (children_data != null) {
2659 node.li.className="nav-section";
2660 node.label_div = document.createElement("div");
2661 node.label_div.className = "nav-section-header-ref";
2662 node.li.appendChild(node.label_div);
2663 get_google_node(me, node);
2664 node.label_div.appendChild(child);
2665 }
2666 else {
2667 node.li.appendChild(child);
2668 }
2669 if(link) {
2670 child.href = me.toroot + link;
2671 }
2672 node.label = document.createTextNode(text);
2673 child.appendChild(node.label);
2674
2675 node.children_ul = null;
2676
2677 return node;
2678}
2679
2680function get_google_node(me, mom)
2681{
2682 mom.children_visited = true;
2683 var linkText;
2684 for (var i in mom.children_data) {
2685 var node_data = mom.children_data[i];
2686 linkText = node_data[0];
2687
2688 if(linkText.match("^"+"com.google.android")=="com.google.android"){
2689 linkText = linkText.substr(19, linkText.length);
2690 }
2691 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
2692 node_data[2], node_data[3]);
2693 }
2694}
2695function showGoogleRefTree() {
2696 init_default_google_navtree(toRoot);
2697 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002698}
2699
2700function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07002701 // load json file for navtree data
2702 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
2703 // when the file is loaded, initialize the tree
2704 if(jqxhr.status === 200) {
2705 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
2706 highlightSidenav();
2707 resizeNav();
2708 }
2709 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08002710}
2711
2712function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07002713 // load json file for navtree data
2714 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
2715 // when the file is loaded, initialize the tree
2716 if(jqxhr.status === 200) {
2717 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
2718 highlightSidenav();
2719 resizeNav();
2720 }
2721 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08002722}
2723
Scott Mainf5089842012-08-14 16:31:07 -07002724/* TOGGLE INHERITED MEMBERS */
2725
2726/* Toggle an inherited class (arrow toggle)
2727 * @param linkObj The link that was clicked.
2728 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2729 * 'null' to simply toggle.
2730 */
2731function toggleInherited(linkObj, expand) {
2732 var base = linkObj.getAttribute("id");
2733 var list = document.getElementById(base + "-list");
2734 var summary = document.getElementById(base + "-summary");
2735 var trigger = document.getElementById(base + "-trigger");
2736 var a = $(linkObj);
2737 if ( (expand == null && a.hasClass("closed")) || expand ) {
2738 list.style.display = "none";
2739 summary.style.display = "block";
2740 trigger.src = toRoot + "assets/images/triangle-opened.png";
2741 a.removeClass("closed");
2742 a.addClass("opened");
2743 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2744 list.style.display = "block";
2745 summary.style.display = "none";
2746 trigger.src = toRoot + "assets/images/triangle-closed.png";
2747 a.removeClass("opened");
2748 a.addClass("closed");
2749 }
2750 return false;
2751}
2752
2753/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2754 * @param linkObj The link that was clicked.
2755 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2756 * 'null' to simply toggle.
2757 */
2758function toggleAllInherited(linkObj, expand) {
2759 var a = $(linkObj);
2760 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2761 var expandos = $(".jd-expando-trigger", table);
2762 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2763 expandos.each(function(i) {
2764 toggleInherited(this, true);
2765 });
2766 a.text("[Collapse]");
2767 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2768 expandos.each(function(i) {
2769 toggleInherited(this, false);
2770 });
2771 a.text("[Expand]");
2772 }
2773 return false;
2774}
2775
2776/* Toggle all inherited members in the class (link in the class title)
2777 */
2778function toggleAllClassInherited() {
2779 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2780 var toggles = $(".toggle-all", $("#body-content"));
2781 if (a.text() == "[Expand All]") {
2782 toggles.each(function(i) {
2783 toggleAllInherited(this, true);
2784 });
2785 a.text("[Collapse All]");
2786 } else {
2787 toggles.each(function(i) {
2788 toggleAllInherited(this, false);
2789 });
2790 a.text("[Expand All]");
2791 }
2792 return false;
2793}
2794
2795/* Expand all inherited members in the class. Used when initiating page search */
2796function ensureAllInheritedExpanded() {
2797 var toggles = $(".toggle-all", $("#body-content"));
2798 toggles.each(function(i) {
2799 toggleAllInherited(this, true);
2800 });
2801 $("#toggleAllClassInherited").text("[Collapse All]");
2802}
2803
2804
2805/* HANDLE KEY EVENTS
2806 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2807 */
2808var agent = navigator['userAgent'].toLowerCase();
2809var mac = agent.indexOf("macintosh") != -1;
2810
2811$(document).keydown( function(e) {
2812var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2813 if (control && e.which == 70) { // 70 is "F"
2814 ensureAllInheritedExpanded();
2815 }
2816});