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