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