blob: c21929ea16fa896c77d01b7cd5da03db654994fc [file] [log] [blame]
Scott Maine4d8f1b2012-06-21 18:03:05 -07001var classesNav;
2var devdocNav;
3var sidenav;
4var cookie_namespace = 'android_developer';
5var NAV_PREF_TREE = "tree";
6var NAV_PREF_PANELS = "panels";
7var nav_pref;
Scott Maine4d8f1b2012-06-21 18:03:05 -07008var isMobile = false; // true if mobile, so we can adjust some layout
Scott Mainf6145542013-04-01 16:38:11 -07009var mPagePath; // initialized in ready() function
Scott Maine4d8f1b2012-06-21 18:03:05 -070010
Scott Main1b3db112012-07-03 14:06:22 -070011var basePath = getBaseUri(location.pathname);
12var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
Scott Main7e447ed2013-02-19 17:22:37 -080013var GOOGLE_DATA; // combined data for google service apis, used for search suggest
Scott Main3b90aff2013-08-01 18:09:35 -070014
Scott Main25e73002013-03-27 15:24:06 -070015// Ensure that all ajax getScript() requests allow caching
16$.ajaxSetup({
17 cache: true
18});
Scott Maine4d8f1b2012-06-21 18:03:05 -070019
20/****** ON LOAD SET UP STUFF *********/
21
22var navBarIsFixed = false;
23$(document).ready(function() {
Scott Main7e447ed2013-02-19 17:22:37 -080024
Scott Main0e76e7e2013-03-12 10:24:07 -070025 // load json file for JD doc search suggestions
26 $.getScript(toRoot + 'reference/jd_lists.js');
Scott Main7e447ed2013-02-19 17:22:37 -080027 // load json file for Android API search suggestions
28 $.getScript(toRoot + 'reference/lists.js');
29 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080030 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080031 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
32 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080033 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080034 if(jqxhr.status === 200) {
35 // combine GCM and GMS data
36 GOOGLE_DATA = GMS_DATA;
37 var start = GOOGLE_DATA.length;
38 for (var i=0; i<GCM_DATA.length; i++) {
39 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
40 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
41 }
42 }
43 });
44 }
45 });
46
Scott Main0e76e7e2013-03-12 10:24:07 -070047 // setup keyboard listener for search shortcut
48 $('body').keyup(function(event) {
49 if (event.which == 191) {
50 $('#search_autocomplete').focus();
51 }
52 });
Scott Main015d6162013-01-29 09:01:52 -080053
Scott Maine4d8f1b2012-06-21 18:03:05 -070054 // init the fullscreen toggle click event
55 $('#nav-swap .fullscreen').click(function(){
56 if ($(this).hasClass('disabled')) {
57 toggleFullscreen(true);
58 } else {
59 toggleFullscreen(false);
60 }
61 });
Scott Main3b90aff2013-08-01 18:09:35 -070062
Scott Maine4d8f1b2012-06-21 18:03:05 -070063 // initialize the divs with custom scrollbars
64 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070065
Scott Maine4d8f1b2012-06-21 18:03:05 -070066 // add HRs below all H2s (except for a few other h2 variants)
Scott Maindb3678b2012-10-23 14:13:41 -070067 $('h2').not('#qv h2').not('#tb h2').not('.sidebox h2').not('#devdoc-nav h2').not('h2.norule').css({marginBottom:0}).after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070068
69 // set up the search close button
70 $('.search .close').click(function() {
71 $searchInput = $('#search_autocomplete');
72 $searchInput.attr('value', '');
73 $(this).addClass("hide");
74 $("#search-container").removeClass('active');
75 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070076 search_focus_changed($searchInput.get(), false);
77 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070078 });
79
80 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070081 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070082 $("#btn-quicknav").click(function() {
83 if (quicknav_open) {
84 $(this).removeClass('active');
85 quicknav_open = false;
86 collapse();
87 } else {
88 $(this).addClass('active');
89 quicknav_open = true;
90 expand();
91 }
92 })
Scott Main3b90aff2013-08-01 18:09:35 -070093
Scott Maine4d8f1b2012-06-21 18:03:05 -070094 var expand = function() {
95 $('#header-wrap').addClass('quicknav');
96 $('#quicknav').stop().show().animate({opacity:'1'});
97 }
Scott Main3b90aff2013-08-01 18:09:35 -070098
Scott Maine4d8f1b2012-06-21 18:03:05 -070099 var collapse = function() {
100 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
101 $(this).hide();
102 $('#header-wrap').removeClass('quicknav');
103 });
104 }
Scott Main3b90aff2013-08-01 18:09:35 -0700105
106
Scott Maine4d8f1b2012-06-21 18:03:05 -0700107 //Set up search
108 $("#search_autocomplete").focus(function() {
109 $("#search-container").addClass('active');
110 })
111 $("#search-container").mouseover(function() {
112 $("#search-container").addClass('active');
113 $("#search_autocomplete").focus();
114 })
115 $("#search-container").mouseout(function() {
116 if ($("#search_autocomplete").is(":focus")) return;
117 if ($("#search_autocomplete").val() == '') {
118 setTimeout(function(){
119 $("#search-container").removeClass('active');
120 $("#search_autocomplete").blur();
121 },250);
122 }
123 })
124 $("#search_autocomplete").blur(function() {
125 if ($("#search_autocomplete").val() == '') {
126 $("#search-container").removeClass('active');
127 }
128 })
129
Scott Main3b90aff2013-08-01 18:09:35 -0700130
Scott Maine4d8f1b2012-06-21 18:03:05 -0700131 // prep nav expandos
132 var pagePath = document.location.pathname;
133 // account for intl docs by removing the intl/*/ path
134 if (pagePath.indexOf("/intl/") == 0) {
135 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
136 }
Scott Mainac2aef52013-02-12 14:15:23 -0800137
Scott Maine4d8f1b2012-06-21 18:03:05 -0700138 if (pagePath.indexOf(SITE_ROOT) == 0) {
139 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
140 pagePath += 'index.html';
141 }
142 }
143
Scott Main01a25452013-02-12 17:32:27 -0800144 // Need a copy of the pagePath before it gets changed in the next block;
145 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
146 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700147 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
148 // If running locally, SITE_ROOT will be a relative path, so account for that by
149 // finding the relative URL to this page. This will allow us to find links on the page
150 // leading back to this page.
151 var pathParts = pagePath.split('/');
152 var relativePagePathParts = [];
153 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
154 for (var i = 0; i < upDirs; i++) {
155 relativePagePathParts.push('..');
156 }
157 for (var i = 0; i < upDirs; i++) {
158 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
159 }
160 relativePagePathParts.push(pathParts[pathParts.length - 1]);
161 pagePath = relativePagePathParts.join('/');
162 } else {
163 // Otherwise the page path is already an absolute URL
164 }
165
Scott Mainac2aef52013-02-12 14:15:23 -0800166 // Highlight the header tabs...
167 // highlight Design tab
168 if ($("body").hasClass("design")) {
169 $("#header li.design a").addClass("selected");
170
171 // highlight Develop tab
172 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
173 $("#header li.develop a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800174 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800175 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800176 if (rootDir == "training") {
177 $("#nav-x li.training a").addClass("selected");
178 } else if (rootDir == "guide") {
179 $("#nav-x li.guide a").addClass("selected");
180 } else if (rootDir == "reference") {
181 // If the root is reference, but page is also part of Google Services, select Google
182 if ($("body").hasClass("google")) {
183 $("#nav-x li.google a").addClass("selected");
184 } else {
185 $("#nav-x li.reference a").addClass("selected");
186 }
187 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
188 $("#nav-x li.tools a").addClass("selected");
189 } else if ($("body").hasClass("google")) {
190 $("#nav-x li.google a").addClass("selected");
191 }
192
193 // highlight Distribute tab
194 } else if ($("body").hasClass("distribute")) {
195 $("#header li.distribute a").addClass("selected");
196 }
197
Scott Mainf6145542013-04-01 16:38:11 -0700198 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
199 // and highlight the sidenav
200 mPagePath = pagePath;
201 highlightSidenav();
Scott Mainac2aef52013-02-12 14:15:23 -0800202
Scott Mainf6145542013-04-01 16:38:11 -0700203 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700204 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700205 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700206 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800207 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700208
209 // set up prev links
210 var $prevLink = [];
211 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700212
Scott Maine4d8f1b2012-06-21 18:03:05 -0700213 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
214false; // navigate across topic boundaries only in design docs
215 if ($prevListItem.length) {
216 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700217 // jump to last topic of previous section
218 $prevLink = $prevListItem.find('a:last');
219 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700220 // jump to previous topic in this section
221 $prevLink = $prevListItem.find('a:eq(0)');
222 }
223 } else {
224 // jump to this section's index page (if it exists)
225 var $parentListItem = $selListItem.parents('li');
226 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700227
Scott Maine4d8f1b2012-06-21 18:03:05 -0700228 // except if cross boundaries aren't allowed, and we're at the top of a section already
229 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700230 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700231 && $selListItem.hasClass('nav-section')) {
232 $prevLink = [];
233 }
234 }
235
Scott Maine4d8f1b2012-06-21 18:03:05 -0700236 // set up next links
237 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700238 var startClass = false;
239 var training = $(".next-class-link").length; // decides whether to provide "next class" link
240 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700241
Scott Maine4d8f1b2012-06-21 18:03:05 -0700242 if ($selListItem.hasClass('nav-section')) {
243 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700244 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700245
246 // if there aren't any children, go to the next section (required for About pages)
247 if($nextLink.length == 0) {
248 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700249 } else if ($('.topic-start-link').length) {
250 // as long as there's a child link and there is a "topic start link" (we're on a landing)
251 // then set the landing page "start link" text to be the first doc title
252 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700253 }
Scott Main3b90aff2013-08-01 18:09:35 -0700254
Scott Main5a1123e2012-09-26 12:51:28 -0700255 // If the selected page has a description, then it's a class or article homepage
256 if ($selListItem.find('a[description]').length) {
257 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700258 startClass = true;
259 }
260 } else {
261 // jump to the next topic in this section (if it exists)
262 $nextLink = $selListItem.next('li').find('a:eq(0)');
263 if (!$nextLink.length) {
Scott Main5a1123e2012-09-26 12:51:28 -0700264 isCrossingBoundary = true;
265 // no more topics in this section, jump to the first topic in the next section
266 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
267 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
268 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700269 }
270 }
271 }
Scott Main5a1123e2012-09-26 12:51:28 -0700272
273 if (startClass) {
274 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
275
Scott Main3b90aff2013-08-01 18:09:35 -0700276 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700277 // then we need to add a bottom border to button
278 if (!$("#tb").length) {
279 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700280 }
Scott Main5a1123e2012-09-26 12:51:28 -0700281 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
282 $('.content-footer.next-class').show();
283 $('.next-page-link').attr('href','')
284 .removeClass("hide").addClass("disabled")
285 .click(function() { return false; });
Scott Main3b90aff2013-08-01 18:09:35 -0700286
Scott Main5a1123e2012-09-26 12:51:28 -0700287 $('.next-class-link').attr('href',$nextLink.attr('href'))
288 .removeClass("hide").append($nextLink.html());
289 $('.next-class-link').find('.new').empty();
290 } else {
291 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
292 }
293
294 if (!startClass && $prevLink.length) {
295 var prevHref = $prevLink.attr('href');
296 if (prevHref == SITE_ROOT + 'index.html') {
297 // Don't show Previous when it leads to the homepage
298 } else {
299 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
300 }
Scott Main3b90aff2013-08-01 18:09:35 -0700301 }
Scott Main5a1123e2012-09-26 12:51:28 -0700302
303 // If this is a training 'article', there should be no prev/next nav
304 // ... if the grandparent is the "nav" ... and it has no child list items...
305 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
306 !$selListItem.find('li').length) {
307 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
308 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700309 }
Scott Main3b90aff2013-08-01 18:09:35 -0700310
Scott Maine4d8f1b2012-06-21 18:03:05 -0700311 }
Scott Main3b90aff2013-08-01 18:09:35 -0700312
313
314
Scott Main5a1123e2012-09-26 12:51:28 -0700315 // Set up the course landing pages for Training with class names and descriptions
316 if ($('body.trainingcourse').length) {
317 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
318 var $classDescriptions = $classLinks.attr('description');
Scott Main3b90aff2013-08-01 18:09:35 -0700319
Scott Main5a1123e2012-09-26 12:51:28 -0700320 var $olClasses = $('<ol class="class-list"></ol>');
321 var $liClass;
322 var $imgIcon;
323 var $h2Title;
324 var $pSummary;
325 var $olLessons;
326 var $liLesson;
327 $classLinks.each(function(index) {
328 $liClass = $('<li></li>');
329 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
330 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700331
Scott Main5a1123e2012-09-26 12:51:28 -0700332 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700333
Scott Main5a1123e2012-09-26 12:51:28 -0700334 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700335
Scott Main5a1123e2012-09-26 12:51:28 -0700336 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700337 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
338 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700339 $lessons.each(function(index) {
340 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
341 });
342 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700343 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
344 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700345 $pSummary.addClass('article');
346 }
347
348 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
349 $olClasses.append($liClass);
350 });
351 $('.jd-descr').append($olClasses);
352 }
353
Scott Maine4d8f1b2012-06-21 18:03:05 -0700354
355
356
357 // Set up expand/collapse behavior
358 $('#nav li.nav-section .nav-section-header').click(function() {
359 var section = $(this).closest('li.nav-section');
360 if (section.hasClass('expanded')) {
361 /* hide me */
362 // if (section.hasClass('selected') || section.find('li').hasClass('selected')) {
363 // /* but not if myself or my descendents are selected */
364 // return;
365 // }
366 section.children('ul').slideUp(250, function() {
367 section.closest('li').removeClass('expanded');
368 resizeNav();
369 });
370 } else {
371 /* show me */
372 // first hide all other siblings
373 var $others = $('li.nav-section.expanded', $(this).closest('ul'));
374 $others.removeClass('expanded').children('ul').slideUp(250);
Scott Main3b90aff2013-08-01 18:09:35 -0700375
Scott Maine4d8f1b2012-06-21 18:03:05 -0700376 // now expand me
377 section.closest('li').addClass('expanded');
378 section.children('ul').slideDown(250, function() {
379 resizeNav();
380 });
381 }
382 });
Scott Main3b90aff2013-08-01 18:09:35 -0700383
Scott Maine4d8f1b2012-06-21 18:03:05 -0700384 $(".scroll-pane").scroll(function(event) {
385 event.preventDefault();
386 return false;
387 });
388
389 /* Resize nav height when window height changes */
390 $(window).resize(function() {
391 if ($('#side-nav').length == 0) return;
392 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
393 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
394 // make sidenav behave when resizing the window and side-scolling is a concern
395 if (navBarIsFixed) {
396 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
397 updateSideNavPosition();
398 } else {
399 updateSidenavFullscreenWidth();
400 }
401 }
402 resizeNav();
403 });
404
405
406 // Set up fixed navbar
407 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
408 $(window).scroll(function(event) {
409 if ($('#side-nav').length == 0) return;
410 if (event.target.nodeName == "DIV") {
411 // Dump scroll event if the target is a DIV, because that means the event is coming
412 // from a scrollable div and so there's no need to make adjustments to our layout
413 return;
414 }
Scott Main3b90aff2013-08-01 18:09:35 -0700415 var scrollTop = $(window).scrollTop();
Scott Maine4d8f1b2012-06-21 18:03:05 -0700416 var headerHeight = $('#header').outerHeight();
417 var subheaderHeight = $('#nav-x').outerHeight();
Scott Main3b90aff2013-08-01 18:09:35 -0700418 var searchResultHeight = $('#searchResults').is(":visible") ?
Scott Maine4d8f1b2012-06-21 18:03:05 -0700419 $('#searchResults').outerHeight() : 0;
420 var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight;
Scott Mainb8d06a52012-12-19 18:38:24 -0800421 // we set the navbar fixed when the scroll position is beyond the height of the site header...
Scott Maine4d8f1b2012-06-21 18:03:05 -0700422 var navBarShouldBeFixed = scrollTop > totalHeaderHeight;
Scott Mainb8d06a52012-12-19 18:38:24 -0800423 // ... except if the document content is shorter than the sidenav height.
424 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
425 if ($("#doc-col").height() < $("#side-nav").height()) {
426 navBarShouldBeFixed = false;
427 }
Scott Main3b90aff2013-08-01 18:09:35 -0700428
Scott Maine4d8f1b2012-06-21 18:03:05 -0700429 var scrollLeft = $(window).scrollLeft();
430 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
431 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
432 updateSideNavPosition();
433 prevScrollLeft = scrollLeft;
434 }
Scott Main3b90aff2013-08-01 18:09:35 -0700435
436 // Don't continue if the header is sufficently far away
Scott Maine4d8f1b2012-06-21 18:03:05 -0700437 // (to avoid intensive resizing that slows scrolling)
438 if (navBarIsFixed && navBarShouldBeFixed) {
439 return;
440 }
Scott Main3b90aff2013-08-01 18:09:35 -0700441
Scott Maine4d8f1b2012-06-21 18:03:05 -0700442 if (navBarIsFixed != navBarShouldBeFixed) {
443 if (navBarShouldBeFixed) {
444 // make it fixed
445 var width = $('#devdoc-nav').width();
446 $('#devdoc-nav')
447 .addClass('fixed')
448 .css({'width':width+'px'})
449 .prependTo('#body-content');
450 // add neato "back to top" button
451 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -0700452
Scott Maine4d8f1b2012-06-21 18:03:05 -0700453 // update the sidenaav position for side scrolling
454 updateSideNavPosition();
455 } else {
456 // make it static again
457 $('#devdoc-nav')
458 .removeClass('fixed')
459 .css({'width':'auto','margin':''})
460 .prependTo('#side-nav');
461 $('#devdoc-nav a.totop').hide();
462 }
463 navBarIsFixed = navBarShouldBeFixed;
Scott Main3b90aff2013-08-01 18:09:35 -0700464 }
465
Scott Maine4d8f1b2012-06-21 18:03:05 -0700466 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
467 });
468
Scott Main3b90aff2013-08-01 18:09:35 -0700469
Scott Maine4d8f1b2012-06-21 18:03:05 -0700470 var navBarLeftPos;
471 if ($('#devdoc-nav').length) {
472 setNavBarLeftPos();
473 }
474
475
476 // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away
477 // from the page)
478 $('.nav-section-header').find('a:eq(0)').click(function(evt) {
479 window.location.href = $(this).attr('href');
480 return false;
481 });
482
483 // Set up play-on-hover <video> tags.
484 $('video.play-on-hover').bind('click', function(){
485 $(this).get(0).load(); // in case the video isn't seekable
486 $(this).get(0).play();
487 });
488
489 // Set up tooltips
490 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700491 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700492 var $target = $(this);
493 var $tooltip = $('<div>')
494 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700495 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700496 .hide()
497 .appendTo('body');
498 $target.removeAttr('title');
499
500 $target.hover(function() {
501 // in
502 var targetRect = $target.offset();
503 targetRect.width = $target.width();
504 targetRect.height = $target.height();
505
506 $tooltip.css({
507 left: targetRect.left,
508 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
509 });
510 $tooltip.addClass('below');
511 $tooltip.show();
512 }, function() {
513 // out
514 $tooltip.hide();
515 });
516 });
517
518 // Set up <h2> deeplinks
519 $('h2').click(function() {
520 var id = $(this).attr('id');
521 if (id) {
522 document.location.hash = id;
523 }
524 });
525
526 //Loads the +1 button
527 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
528 po.src = 'https://apis.google.com/js/plusone.js';
529 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
530
531
Scott Main3b90aff2013-08-01 18:09:35 -0700532 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700533 // which avoids the visible width from changing each time the bar appears
534 var $sidenav = $("#side-nav");
535 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700536
Scott Maine4d8f1b2012-06-21 18:03:05 -0700537 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
538
539
540 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700541
Scott Maine4d8f1b2012-06-21 18:03:05 -0700542 if ($(".scroll-pane").length > 1) {
543 // Check if there's a user preference for the panel heights
544 var cookieHeight = readCookie("reference_height");
545 if (cookieHeight) {
546 restoreHeight(cookieHeight);
547 }
548 }
Scott Main3b90aff2013-08-01 18:09:35 -0700549
Scott Maine4d8f1b2012-06-21 18:03:05 -0700550 resizeNav();
551
Scott Main015d6162013-01-29 09:01:52 -0800552 /* init the language selector based on user cookie for lang */
553 loadLangPref();
554 changeNavLang(getLangPref());
555
556 /* setup event handlers to ensure the overflow menu is visible while picking lang */
557 $("#language select")
558 .mousedown(function() {
559 $("div.morehover").addClass("hover"); })
560 .blur(function() {
561 $("div.morehover").removeClass("hover"); });
562
563 /* some global variable setup */
564 resizePackagesNav = $("#resize-packages-nav");
565 classesNav = $("#classes-nav");
566 devdocNav = $("#devdoc-nav");
567
568 var cookiePath = "";
569 if (location.href.indexOf("/reference/") != -1) {
570 cookiePath = "reference_";
571 } else if (location.href.indexOf("/guide/") != -1) {
572 cookiePath = "guide_";
573 } else if (location.href.indexOf("/tools/") != -1) {
574 cookiePath = "tools_";
575 } else if (location.href.indexOf("/training/") != -1) {
576 cookiePath = "training_";
577 } else if (location.href.indexOf("/design/") != -1) {
578 cookiePath = "design_";
579 } else if (location.href.indexOf("/distribute/") != -1) {
580 cookiePath = "distribute_";
581 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700582
583});
Scott Main7e447ed2013-02-19 17:22:37 -0800584// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700585
586
Scott Mainf6145542013-04-01 16:38:11 -0700587function highlightSidenav() {
588 // select current page in sidenav and header, and set up prev/next links if they exist
589 var $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
590 var $selListItem;
591 if ($selNavLink.length) {
592
593 // Find this page's <li> in sidenav and set selected
594 $selListItem = $selNavLink.closest('li');
595 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700596
Scott Mainf6145542013-04-01 16:38:11 -0700597 // Traverse up the tree and expand all parent nav-sections
598 $selNavLink.parents('li.nav-section').each(function() {
599 $(this).addClass('expanded');
600 $(this).children('ul').show();
601 });
602 }
603}
604
Scott Maine4d8f1b2012-06-21 18:03:05 -0700605
606function toggleFullscreen(enable) {
607 var delay = 20;
608 var enabled = true;
609 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
610 if (enable) {
611 // Currently NOT USING fullscreen; enable fullscreen
612 stylesheet.removeAttr('disabled');
613 $('#nav-swap .fullscreen').removeClass('disabled');
614 $('#devdoc-nav').css({left:''});
615 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
616 enabled = true;
617 } else {
618 // Currently USING fullscreen; disable fullscreen
619 stylesheet.attr('disabled', 'disabled');
620 $('#nav-swap .fullscreen').addClass('disabled');
621 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
622 enabled = false;
623 }
624 writeCookie("fullscreen", enabled, null, null);
625 setNavBarLeftPos();
626 resizeNav(delay);
627 updateSideNavPosition();
628 setTimeout(initSidenavHeightResize,delay);
629}
630
631
632function setNavBarLeftPos() {
633 navBarLeftPos = $('#body-content').offset().left;
634}
635
636
637function updateSideNavPosition() {
638 var newLeft = $(window).scrollLeft() - navBarLeftPos;
639 $('#devdoc-nav').css({left: -newLeft});
640 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
641}
Scott Main3b90aff2013-08-01 18:09:35 -0700642
Scott Maine4d8f1b2012-06-21 18:03:05 -0700643
644
645
646
647
648
649
650// TODO: use $(document).ready instead
651function addLoadEvent(newfun) {
652 var current = window.onload;
653 if (typeof window.onload != 'function') {
654 window.onload = newfun;
655 } else {
656 window.onload = function() {
657 current();
658 newfun();
659 }
660 }
661}
662
663var agent = navigator['userAgent'].toLowerCase();
664// If a mobile phone, set flag and do mobile setup
665if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
666 (agent.indexOf("blackberry") != -1) ||
667 (agent.indexOf("webos") != -1) ||
668 (agent.indexOf("mini") != -1)) { // opera mini browsers
669 isMobile = true;
670}
671
672
Scott Maine4d8f1b2012-06-21 18:03:05 -0700673addLoadEvent( function() {
674 $("pre:not(.no-pretty-print)").addClass("prettyprint");
675 prettyPrint();
676} );
677
Scott Maine4d8f1b2012-06-21 18:03:05 -0700678
679
680
681/* ######### RESIZE THE SIDENAV HEIGHT ########## */
682
683function resizeNav(delay) {
684 var $nav = $("#devdoc-nav");
685 var $window = $(window);
686 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700687
Scott Maine4d8f1b2012-06-21 18:03:05 -0700688 // Get the height of entire window and the total header height.
689 // Then figure out based on scroll position whether the header is visible
690 var windowHeight = $window.height();
691 var scrollTop = $window.scrollTop();
692 var headerHeight = $('#header').outerHeight();
693 var subheaderHeight = $('#nav-x').outerHeight();
694 var headerVisible = (scrollTop < (headerHeight + subheaderHeight));
Scott Main3b90aff2013-08-01 18:09:35 -0700695
696 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700697 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700698 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700699 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700700
Scott Maine4d8f1b2012-06-21 18:03:05 -0700701 // Depending on whether the header is visible, set the side nav's height.
702 if (headerVisible) {
703 // The sidenav height grows as the header goes off screen
704 navHeight = windowHeight - (headerHeight + subheaderHeight - scrollTop) - topMargin;
705 } else {
706 // Once header is off screen, the nav height is almost full window height
707 navHeight = windowHeight - topMargin;
708 }
Scott Main3b90aff2013-08-01 18:09:35 -0700709
710
711
Scott Maine4d8f1b2012-06-21 18:03:05 -0700712 $scrollPanes = $(".scroll-pane");
713 if ($scrollPanes.length > 1) {
714 // subtract the height of the api level widget and nav swapper from the available nav height
715 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700716
Scott Maine4d8f1b2012-06-21 18:03:05 -0700717 $("#swapper").css({height:navHeight + "px"});
718 if ($("#nav-tree").is(":visible")) {
719 $("#nav-tree").css({height:navHeight});
720 }
Scott Main3b90aff2013-08-01 18:09:35 -0700721
722 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700723 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700724
725 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700726 // then the package panel should begin to shrink
727 if (parseInt(classesHeight) <= 0) {
728 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
729 $("#packages-nav").css({height:navHeight - 10});
730 }
Scott Main3b90aff2013-08-01 18:09:35 -0700731
Scott Maine4d8f1b2012-06-21 18:03:05 -0700732 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
733 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700734
735
Scott Maine4d8f1b2012-06-21 18:03:05 -0700736 } else {
737 $nav.height(navHeight);
738 }
Scott Main3b90aff2013-08-01 18:09:35 -0700739
Scott Maine4d8f1b2012-06-21 18:03:05 -0700740 if (delay) {
741 updateFromResize = true;
742 delayedReInitScrollbars(delay);
743 } else {
744 reInitScrollbars();
745 }
Scott Main3b90aff2013-08-01 18:09:35 -0700746
Scott Maine4d8f1b2012-06-21 18:03:05 -0700747}
748
749var updateScrollbars = false;
750var updateFromResize = false;
751
752/* Re-initialize the scrollbars to account for changed nav size.
753 * This method postpones the actual update by a 1/4 second in order to optimize the
754 * scroll performance while the header is still visible, because re-initializing the
755 * scroll panes is an intensive process.
756 */
757function delayedReInitScrollbars(delay) {
758 // If we're scheduled for an update, but have received another resize request
759 // before the scheduled resize has occured, just ignore the new request
760 // (and wait for the scheduled one).
761 if (updateScrollbars && updateFromResize) {
762 updateFromResize = false;
763 return;
764 }
Scott Main3b90aff2013-08-01 18:09:35 -0700765
Scott Maine4d8f1b2012-06-21 18:03:05 -0700766 // We're scheduled for an update and the update request came from this method's setTimeout
767 if (updateScrollbars && !updateFromResize) {
768 reInitScrollbars();
769 updateScrollbars = false;
770 } else {
771 updateScrollbars = true;
772 updateFromResize = false;
773 setTimeout('delayedReInitScrollbars()',delay);
774 }
775}
776
777/* Re-initialize the scrollbars to account for changed nav size. */
778function reInitScrollbars() {
779 var pane = $(".scroll-pane").each(function(){
780 var api = $(this).data('jsp');
781 if (!api) { setTimeout(reInitScrollbars,300); return;}
782 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700783 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700784 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
785}
786
787
788/* Resize the height of the nav panels in the reference,
789 * and save the new size to a cookie */
790function saveNavPanels() {
791 var basePath = getBaseUri(location.pathname);
792 var section = basePath.substring(1,basePath.indexOf("/",1));
793 writeCookie("height", resizePackagesNav.css("height"), section, null);
794}
795
796
797
798function restoreHeight(packageHeight) {
799 $("#resize-packages-nav").height(packageHeight);
800 $("#packages-nav").height(packageHeight);
801 // var classesHeight = navHeight - packageHeight;
802 // $("#classes-nav").css({height:classesHeight});
803 // $("#classes-nav .jspContainer").css({height:classesHeight});
804}
805
806
807
808/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
809
810
811
812
813
Scott Main3b90aff2013-08-01 18:09:35 -0700814/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700815 This is called when the page finished loading. */
816function scrollIntoView(nav) {
817 var $nav = $("#"+nav);
818 var element = $nav.jScrollPane({/* ...settings... */});
819 var api = element.data('jsp');
820
821 if ($nav.is(':visible')) {
822 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700823 if ($selected.length == 0) {
824 // If no selected item found, exit
825 return;
826 }
Scott Main52dd2062013-08-15 12:22:28 -0700827 // get the selected item's offset from its container nav by measuring the item's offset
828 // relative to the document then subtract the container nav's offset relative to the document
829 var selectedOffset = $selected.offset().top - $nav.offset().top;
830 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
831 // if it's more than 80% down the nav
832 // scroll the item up by an amount equal to 80% the container nav's height
833 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700834 }
835 }
836}
837
838
839
840
841
842
843/* Show popup dialogs */
844function showDialog(id) {
845 $dialog = $("#"+id);
846 $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>');
847 $dialog.wrapInner('<div/>');
848 $dialog.removeClass("hide");
849}
850
851
852
853
854
855/* ######### COOKIES! ########## */
856
857function readCookie(cookie) {
858 var myCookie = cookie_namespace+"_"+cookie+"=";
859 if (document.cookie) {
860 var index = document.cookie.indexOf(myCookie);
861 if (index != -1) {
862 var valStart = index + myCookie.length;
863 var valEnd = document.cookie.indexOf(";", valStart);
864 if (valEnd == -1) {
865 valEnd = document.cookie.length;
866 }
867 var val = document.cookie.substring(valStart, valEnd);
868 return val;
869 }
870 }
871 return 0;
872}
873
874function writeCookie(cookie, val, section, expiration) {
875 if (val==undefined) return;
876 section = section == null ? "_" : "_"+section+"_";
877 if (expiration == null) {
878 var date = new Date();
879 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
880 expiration = date.toGMTString();
881 }
Scott Main3b90aff2013-08-01 18:09:35 -0700882 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700883 + "; expires=" + expiration+"; path=/";
884 document.cookie = cookieValue;
885}
886
887/* ######### END COOKIES! ########## */
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
Scott Maind7026f72013-06-17 15:08:49 -0700907/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700908
909
910
911
912
913function toggle(obj, slide) {
914 var ul = $("ul:first", obj);
915 var li = ul.parent();
916 if (li.hasClass("closed")) {
917 if (slide) {
918 ul.slideDown("fast");
919 } else {
920 ul.show();
921 }
922 li.removeClass("closed");
923 li.addClass("open");
924 $(".toggle-img", li).attr("title", "hide pages");
925 } else {
926 ul.slideUp("fast");
927 li.removeClass("open");
928 li.addClass("closed");
929 $(".toggle-img", li).attr("title", "show pages");
930 }
931}
932
933
Scott Maine4d8f1b2012-06-21 18:03:05 -0700934function buildToggleLists() {
935 $(".toggle-list").each(
936 function(i) {
937 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
938 $(this).addClass("closed");
939 });
940}
941
942
943
Scott Maind7026f72013-06-17 15:08:49 -0700944function hideNestedItems(list, toggle) {
945 $list = $(list);
946 // hide nested lists
947 if($list.hasClass('showing')) {
948 $("li ol", $list).hide('fast');
949 $list.removeClass('showing');
950 // show nested lists
951 } else {
952 $("li ol", $list).show('fast');
953 $list.addClass('showing');
954 }
955 $(".more,.less",$(toggle)).toggle();
956}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985/* REFERENCE NAV SWAP */
986
987
988function getNavPref() {
989 var v = readCookie('reference_nav');
990 if (v != NAV_PREF_TREE) {
991 v = NAV_PREF_PANELS;
992 }
993 return v;
994}
995
996function chooseDefaultNav() {
997 nav_pref = getNavPref();
998 if (nav_pref == NAV_PREF_TREE) {
999 $("#nav-panels").toggle();
1000 $("#panel-link").toggle();
1001 $("#nav-tree").toggle();
1002 $("#tree-link").toggle();
1003 }
1004}
1005
1006function swapNav() {
1007 if (nav_pref == NAV_PREF_TREE) {
1008 nav_pref = NAV_PREF_PANELS;
1009 } else {
1010 nav_pref = NAV_PREF_TREE;
1011 init_default_navtree(toRoot);
1012 }
1013 var date = new Date();
1014 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1015 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1016
1017 $("#nav-panels").toggle();
1018 $("#panel-link").toggle();
1019 $("#nav-tree").toggle();
1020 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001021
Scott Maine4d8f1b2012-06-21 18:03:05 -07001022 resizeNav();
1023
1024 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1025 $("#nav-tree .jspContainer:visible")
1026 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1027 // Another nasty hack to make the scrollbar appear now that we have height
1028 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001029
Scott Maine4d8f1b2012-06-21 18:03:05 -07001030 if ($("#nav-tree").is(':visible')) {
1031 scrollIntoView("nav-tree");
1032 } else {
1033 scrollIntoView("packages-nav");
1034 scrollIntoView("classes-nav");
1035 }
1036}
1037
1038
1039
Scott Mainf5089842012-08-14 16:31:07 -07001040/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001041/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001042/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001043
1044function getBaseUri(uri) {
1045 var intlUrl = (uri.substring(0,6) == "/intl/");
1046 if (intlUrl) {
1047 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1048 base = base.substring(base.indexOf('/')+1, base.length);
1049 //alert("intl, returning base url: /" + base);
1050 return ("/" + base);
1051 } else {
1052 //alert("not intl, returning uri as found.");
1053 return uri;
1054 }
1055}
1056
1057function requestAppendHL(uri) {
1058//append "?hl=<lang> to an outgoing request (such as to blog)
1059 var lang = getLangPref();
1060 if (lang) {
1061 var q = 'hl=' + lang;
1062 uri += '?' + q;
1063 window.location = uri;
1064 return false;
1065 } else {
1066 return true;
1067 }
1068}
1069
1070
Scott Maine4d8f1b2012-06-21 18:03:05 -07001071function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001072 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1073 $links.each(function(i){ // for each link with a translation
1074 var $link = $(this);
1075 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1076 // put the desired language from the attribute as the text
1077 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001078 }
Scott Main6eb95f12012-10-02 17:12:23 -07001079 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001080}
1081
Scott Main015d6162013-01-29 09:01:52 -08001082function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001083 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001084 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001085 // keep this for 50 years
1086 //alert("expires: " + expires)
1087 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001088
1089 // ####### TODO: Remove this condition once we're stable on devsite #######
1090 // This condition is only needed if we still need to support legacy GAE server
1091 if (devsite) {
1092 // Switch language when on Devsite server
1093 if (submit) {
1094 $("#setlang").submit();
1095 }
1096 } else {
1097 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001098 if (submit) {
1099 window.location = getBaseUri(location.pathname);
1100 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001101 }
1102}
1103
1104function loadLangPref() {
1105 var lang = readCookie("pref_lang");
1106 if (lang != 0) {
1107 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1108 }
1109}
1110
1111function getLangPref() {
1112 var lang = $("#language").find(":selected").attr("value");
1113 if (!lang) {
1114 lang = readCookie("pref_lang");
1115 }
1116 return (lang != 0) ? lang : 'en';
1117}
1118
1119/* ########## END LOCALIZATION ############ */
1120
1121
1122
1123
1124
1125
1126/* Used to hide and reveal supplemental content, such as long code samples.
1127 See the companion CSS in android-developer-docs.css */
1128function toggleContent(obj) {
1129 var div = $(obj.parentNode.parentNode);
1130 var toggleMe = $(".toggle-content-toggleme",div);
1131 if (div.hasClass("closed")) { // if it's closed, open it
1132 toggleMe.slideDown();
1133 $(".toggle-content-text", obj).toggle();
1134 div.removeClass("closed").addClass("open");
Scott Main3b90aff2013-08-01 18:09:35 -07001135 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001136 + "assets/images/triangle-opened.png");
1137 } else { // if it's open, close it
1138 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
1139 $(".toggle-content-text", obj).toggle();
1140 div.removeClass("open").addClass("closed");
Scott Main3b90aff2013-08-01 18:09:35 -07001141 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001142 + "assets/images/triangle-closed.png");
1143 });
1144 }
1145 return false;
1146}
Scott Mainf5089842012-08-14 16:31:07 -07001147
1148
Scott Maindb3678b2012-10-23 14:13:41 -07001149/* New version of expandable content */
1150function toggleExpandable(link,id) {
1151 if($(id).is(':visible')) {
1152 $(id).slideUp();
1153 $(link).removeClass('expanded');
1154 } else {
1155 $(id).slideDown();
1156 $(link).addClass('expanded');
1157 }
1158}
1159
1160function hideExpandable(ids) {
1161 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001162 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001163}
1164
Scott Mainf5089842012-08-14 16:31:07 -07001165
1166
1167
1168
Scott Main3b90aff2013-08-01 18:09:35 -07001169/*
Scott Mainf5089842012-08-14 16:31:07 -07001170 * Slideshow 1.0
1171 * Used on /index.html and /develop/index.html for carousel
1172 *
1173 * Sample usage:
1174 * HTML -
1175 * <div class="slideshow-container">
1176 * <a href="" class="slideshow-prev">Prev</a>
1177 * <a href="" class="slideshow-next">Next</a>
1178 * <ul>
1179 * <li class="item"><img src="images/marquee1.jpg"></li>
1180 * <li class="item"><img src="images/marquee2.jpg"></li>
1181 * <li class="item"><img src="images/marquee3.jpg"></li>
1182 * <li class="item"><img src="images/marquee4.jpg"></li>
1183 * </ul>
1184 * </div>
1185 *
1186 * <script type="text/javascript">
1187 * $('.slideshow-container').dacSlideshow({
1188 * auto: true,
1189 * btnPrev: '.slideshow-prev',
1190 * btnNext: '.slideshow-next'
1191 * });
1192 * </script>
1193 *
1194 * Options:
1195 * btnPrev: optional identifier for previous button
1196 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001197 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001198 * auto: whether or not to auto-proceed
1199 * speed: animation speed
1200 * autoTime: time between auto-rotation
1201 * easing: easing function for transition
1202 * start: item to select by default
1203 * scroll: direction to scroll in
1204 * pagination: whether or not to include dotted pagination
1205 *
1206 */
1207
1208 (function($) {
1209 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001210
Scott Mainf5089842012-08-14 16:31:07 -07001211 //Options - see above
1212 o = $.extend({
1213 btnPrev: null,
1214 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001215 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001216 auto: true,
1217 speed: 500,
1218 autoTime: 12000,
1219 easing: null,
1220 start: 0,
1221 scroll: 1,
1222 pagination: true
1223
1224 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001225
1226 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001227 return this.each(function() {
1228
1229 var running = false;
1230 var animCss = o.vertical ? "top" : "left";
1231 var sizeCss = o.vertical ? "height" : "width";
1232 var div = $(this);
1233 var ul = $("ul", div);
1234 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001235 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001236 var timer = null;
1237
1238 var li = $("li", ul);
1239 var itemLength = li.size();
1240 var curr = o.start;
1241
1242 li.css({float: o.vertical ? "none" : "left"});
1243 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1244 div.css({position: "relative", "z-index": "2", left: "0px"});
1245
1246 var liSize = o.vertical ? height(li) : width(li);
1247 var ulSize = liSize * itemLength;
1248 var divSize = liSize;
1249
1250 li.css({width: li.width(), height: li.height()});
1251 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1252
1253 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001254
Scott Mainf5089842012-08-14 16:31:07 -07001255 //Pagination
1256 if (o.pagination) {
1257 var pagination = $("<div class='pagination'></div>");
1258 var pag_ul = $("<ul></ul>");
1259 if (tl > 1) {
1260 for (var i=0;i<tl;i++) {
1261 var li = $("<li>"+i+"</li>");
1262 pag_ul.append(li);
1263 if (i==o.start) li.addClass('active');
1264 li.click(function() {
1265 go(parseInt($(this).text()));
1266 })
1267 }
1268 pagination.append(pag_ul);
1269 div.append(pagination);
1270 }
1271 }
Scott Main3b90aff2013-08-01 18:09:35 -07001272
Scott Mainf5089842012-08-14 16:31:07 -07001273 //Previous button
1274 if(o.btnPrev)
1275 $(o.btnPrev).click(function(e) {
1276 e.preventDefault();
1277 return go(curr-o.scroll);
1278 });
1279
1280 //Next button
1281 if(o.btnNext)
1282 $(o.btnNext).click(function(e) {
1283 e.preventDefault();
1284 return go(curr+o.scroll);
1285 });
Scott Maineb410352013-01-14 19:03:40 -08001286
1287 //Pause button
1288 if(o.btnPause)
1289 $(o.btnPause).click(function(e) {
1290 e.preventDefault();
1291 if ($(this).hasClass('paused')) {
1292 startRotateTimer();
1293 } else {
1294 pauseRotateTimer();
1295 }
1296 });
Scott Main3b90aff2013-08-01 18:09:35 -07001297
Scott Mainf5089842012-08-14 16:31:07 -07001298 //Auto rotation
1299 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001300
Scott Mainf5089842012-08-14 16:31:07 -07001301 function startRotateTimer() {
1302 clearInterval(timer);
1303 timer = setInterval(function() {
1304 if (curr == tl-1) {
1305 go(0);
1306 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001307 go(curr+o.scroll);
1308 }
Scott Mainf5089842012-08-14 16:31:07 -07001309 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001310 $(o.btnPause).removeClass('paused');
1311 }
1312
1313 function pauseRotateTimer() {
1314 clearInterval(timer);
1315 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001316 }
1317
1318 //Go to an item
1319 function go(to) {
1320 if(!running) {
1321
1322 if(to<0) {
1323 to = itemLength-1;
1324 } else if (to>itemLength-1) {
1325 to = 0;
1326 }
1327 curr = to;
1328
1329 running = true;
1330
1331 ul.animate(
1332 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1333 function() {
1334 running = false;
1335 }
1336 );
1337
1338 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1339 $( (curr-o.scroll<0 && o.btnPrev)
1340 ||
1341 (curr+o.scroll > itemLength && o.btnNext)
1342 ||
1343 []
1344 ).addClass("disabled");
1345
Scott Main3b90aff2013-08-01 18:09:35 -07001346
Scott Mainf5089842012-08-14 16:31:07 -07001347 var nav_items = $('li', pagination);
1348 nav_items.removeClass('active');
1349 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001350
Scott Mainf5089842012-08-14 16:31:07 -07001351
1352 }
1353 if(o.auto) startRotateTimer();
1354 return false;
1355 };
1356 });
1357 };
1358
1359 function css(el, prop) {
1360 return parseInt($.css(el[0], prop)) || 0;
1361 };
1362 function width(el) {
1363 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1364 };
1365 function height(el) {
1366 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1367 };
1368
1369 })(jQuery);
1370
1371
Scott Main3b90aff2013-08-01 18:09:35 -07001372/*
Scott Mainf5089842012-08-14 16:31:07 -07001373 * dacSlideshow 1.0
1374 * Used on develop/index.html for side-sliding tabs
1375 *
1376 * Sample usage:
1377 * HTML -
1378 * <div class="slideshow-container">
1379 * <a href="" class="slideshow-prev">Prev</a>
1380 * <a href="" class="slideshow-next">Next</a>
1381 * <ul>
1382 * <li class="item"><img src="images/marquee1.jpg"></li>
1383 * <li class="item"><img src="images/marquee2.jpg"></li>
1384 * <li class="item"><img src="images/marquee3.jpg"></li>
1385 * <li class="item"><img src="images/marquee4.jpg"></li>
1386 * </ul>
1387 * </div>
1388 *
1389 * <script type="text/javascript">
1390 * $('.slideshow-container').dacSlideshow({
1391 * auto: true,
1392 * btnPrev: '.slideshow-prev',
1393 * btnNext: '.slideshow-next'
1394 * });
1395 * </script>
1396 *
1397 * Options:
1398 * btnPrev: optional identifier for previous button
1399 * btnNext: optional identifier for next button
1400 * auto: whether or not to auto-proceed
1401 * speed: animation speed
1402 * autoTime: time between auto-rotation
1403 * easing: easing function for transition
1404 * start: item to select by default
1405 * scroll: direction to scroll in
1406 * pagination: whether or not to include dotted pagination
1407 *
1408 */
1409 (function($) {
1410 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001411
Scott Mainf5089842012-08-14 16:31:07 -07001412 //Options - see above
1413 o = $.extend({
1414 speed : 250,
1415 easing: null,
1416 nav_id: null,
1417 frame_id: null
1418 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001419
1420 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001421 return this.each(function() {
1422
1423 var curr = 0;
1424 var running = false;
1425 var animCss = "margin-left";
1426 var sizeCss = "width";
1427 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001428
Scott Mainf5089842012-08-14 16:31:07 -07001429 var nav = $(o.nav_id, div);
1430 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001431 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001432 var frame = div.find(o.frame_id);
1433 var content_width = $(frame).find('ul').width();
1434 //Buttons
1435 $(nav_li).click(function(e) {
1436 go($(nav_li).index($(this)));
1437 })
Scott Main3b90aff2013-08-01 18:09:35 -07001438
Scott Mainf5089842012-08-14 16:31:07 -07001439 //Go to an item
1440 function go(to) {
1441 if(!running) {
1442 curr = to;
1443 running = true;
1444
1445 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1446 function() {
1447 running = false;
1448 }
1449 );
1450
Scott Main3b90aff2013-08-01 18:09:35 -07001451
Scott Mainf5089842012-08-14 16:31:07 -07001452 nav_li.removeClass('active');
1453 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001454
Scott Mainf5089842012-08-14 16:31:07 -07001455
1456 }
1457 return false;
1458 };
1459 });
1460 };
1461
1462 function css(el, prop) {
1463 return parseInt($.css(el[0], prop)) || 0;
1464 };
1465 function width(el) {
1466 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1467 };
1468 function height(el) {
1469 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1470 };
1471
1472 })(jQuery);
1473
1474
1475
1476
1477
1478/* ######################################################## */
1479/* ################ SEARCH SUGGESTIONS ################## */
1480/* ######################################################## */
1481
1482
Scott Main7e447ed2013-02-19 17:22:37 -08001483
Scott Main0e76e7e2013-03-12 10:24:07 -07001484var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1485var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1486
Scott Mainf5089842012-08-14 16:31:07 -07001487var gMatches = new Array();
1488var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001489var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001490var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1491var gListLength = 0;
1492
1493
1494var gGoogleMatches = new Array();
1495var ROW_COUNT_GOOGLE = 15; // max number of results in list
1496var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001497
Scott Main0e76e7e2013-03-12 10:24:07 -07001498var gDocsMatches = new Array();
1499var ROW_COUNT_DOCS = 100; // max number of results in list
1500var gDocsListLength = 0;
1501
Scott Mainde295272013-03-25 15:48:35 -07001502function onSuggestionClick(link) {
1503 // When user clicks a suggested document, track it
1504 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1505 'from: ' + $("#search_autocomplete").val()]);
1506}
1507
Scott Mainf5089842012-08-14 16:31:07 -07001508function set_item_selected($li, selected)
1509{
1510 if (selected) {
1511 $li.attr('class','jd-autocomplete jd-selected');
1512 } else {
1513 $li.attr('class','jd-autocomplete');
1514 }
1515}
1516
1517function set_item_values(toroot, $li, match)
1518{
1519 var $link = $('a',$li);
1520 $link.html(match.__hilabel || match.label);
1521 $link.attr('href',toroot + match.link);
1522}
1523
Scott Main0e76e7e2013-03-12 10:24:07 -07001524function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001525 var $li = $("<li class='jd-autocomplete'></li>");
1526 $list.append($li);
1527
1528 $li.mousedown(function() {
1529 window.location = this.firstChild.getAttribute("href");
1530 });
1531 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001532 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001533 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001534 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1535 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001536 });
Scott Mainde295272013-03-25 15:48:35 -07001537 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001538 $li.attr('class','show-item');
1539 return $li;
1540}
1541
Scott Mainf5089842012-08-14 16:31:07 -07001542function sync_selection_table(toroot)
1543{
Scott Mainf5089842012-08-14 16:31:07 -07001544 var $li; //list item jquery object
1545 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001546
Scott Main0e76e7e2013-03-12 10:24:07 -07001547 // if there are NO results at all, hide all columns
1548 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1549 $('.suggest-card').hide(300);
1550 return;
1551 }
1552
1553 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001554 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001555 // reveal suggestion list
1556 $('.suggest-card.dummy').show();
1557 $('.suggest-card.reference').show();
1558 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001559
Scott Main0e76e7e2013-03-12 10:24:07 -07001560 // reset the lists
1561 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001562
Scott Main0e76e7e2013-03-12 10:24:07 -07001563 // ########### ANDROID RESULTS #############
1564 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001565
Scott Main0e76e7e2013-03-12 10:24:07 -07001566 // determine android results to show
1567 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1568 gMatches.length : ROW_COUNT_FRAMEWORK;
1569 for (i=0; i<gListLength; i++) {
1570 var $li = new_suggestion($(".suggest-card.reference ul"));
1571 set_item_values(toroot, $li, gMatches[i]);
1572 set_item_selected($li, i == gSelectedIndex);
1573 }
1574 }
Scott Main7e447ed2013-02-19 17:22:37 -08001575
Scott Main0e76e7e2013-03-12 10:24:07 -07001576 // ########### GOOGLE RESULTS #############
1577 if (gGoogleMatches.length > 0) {
1578 // show header for list
1579 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001580
Scott Main0e76e7e2013-03-12 10:24:07 -07001581 // determine google results to show
1582 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1583 for (i=0; i<gGoogleListLength; i++) {
1584 var $li = new_suggestion($(".suggest-card.reference ul"));
1585 set_item_values(toroot, $li, gGoogleMatches[i]);
1586 set_item_selected($li, i == gSelectedIndex);
1587 }
1588 }
Scott Mainf5089842012-08-14 16:31:07 -07001589 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001590 $('.suggest-card.reference').hide();
1591 $('.suggest-card.dummy').hide();
1592 }
1593
1594 // ########### JD DOC RESULTS #############
1595 if (gDocsMatches.length > 0) {
1596 // reset the lists
1597 $(".search_filtered_wrapper.docs li").remove();
1598
1599 // determine google results to show
1600 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1601 for (i=0; i<gDocsListLength; i++) {
1602 var sugg = gDocsMatches[i];
1603 var $li;
1604 if (sugg.type == "design") {
1605 $li = new_suggestion($(".suggest-card.design ul"));
1606 } else
1607 if (sugg.type == "distribute") {
1608 $li = new_suggestion($(".suggest-card.distribute ul"));
1609 } else
1610 if (sugg.type == "training") {
1611 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1612 } else
1613 if (sugg.type == "guide"||"google") {
1614 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1615 } else {
1616 continue;
1617 }
1618
1619 set_item_values(toroot, $li, sugg);
1620 set_item_selected($li, i == gSelectedIndex);
1621 }
1622
1623 // add heading and show or hide card
1624 if ($(".suggest-card.design li").length > 0) {
1625 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1626 $(".suggest-card.design").show(300);
1627 } else {
1628 $('.suggest-card.design').hide(300);
1629 }
1630 if ($(".suggest-card.distribute li").length > 0) {
1631 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1632 $(".suggest-card.distribute").show(300);
1633 } else {
1634 $('.suggest-card.distribute').hide(300);
1635 }
1636 if ($(".child-card.guides li").length > 0) {
1637 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1638 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1639 }
1640 if ($(".child-card.training li").length > 0) {
1641 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1642 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1643 }
1644
1645 if ($(".suggest-card.develop li").length > 0) {
1646 $(".suggest-card.develop").show(300);
1647 } else {
1648 $('.suggest-card.develop').hide(300);
1649 }
1650
1651 } else {
1652 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001653 }
1654}
1655
Scott Main0e76e7e2013-03-12 10:24:07 -07001656/** Called by the search input's onkeydown and onkeyup events.
1657 * Handles navigation with keyboard arrows, Enter key to invoke search,
1658 * otherwise invokes search suggestions on key-up event.
1659 * @param e The JS event
1660 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001661 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001662 * @returns True if the event should bubble up
1663 */
Scott Mainf5089842012-08-14 16:31:07 -07001664function search_changed(e, kd, toroot)
1665{
1666 var search = document.getElementById("search_autocomplete");
1667 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001668 // get the ul hosting the currently selected item
1669 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1670 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1671 var $selectedUl = $columns[gSelectedColumn];
1672
Scott Mainf5089842012-08-14 16:31:07 -07001673 // show/hide the close button
1674 if (text != '') {
1675 $(".search .close").removeClass("hide");
1676 } else {
1677 $(".search .close").addClass("hide");
1678 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001679 // 27 = esc
1680 if (e.keyCode == 27) {
1681 // close all search results
1682 if (kd) $('.search .close').trigger('click');
1683 return true;
1684 }
Scott Mainf5089842012-08-14 16:31:07 -07001685 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001686 else if (e.keyCode == 13) {
1687 if (gSelectedIndex < 0) {
1688 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001689 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1690 // if results aren't showing (and text not empty), return true to allow search to execute
Scott Mainf5089842012-08-14 16:31:07 -07001691 return true;
1692 } else {
1693 // otherwise, results are already showing, so allow ajax to auto refresh the results
1694 // and ignore this Enter press to avoid the reload.
1695 return false;
1696 }
1697 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001698 // click the link corresponding to selected item
1699 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001700 return false;
1701 }
1702 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001703 // Stop here if Google results are showing
1704 else if ($("#searchResults").is(":visible")) {
1705 return true;
1706 }
1707 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001708 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001709 // if the next item is a header, skip it
1710 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001711 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001712 }
1713 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001714 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001715 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001716 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1717 // If user reaches top, reset selected column
1718 if (gSelectedIndex < 0) {
1719 gSelectedColumn = -1;
1720 }
Scott Mainf5089842012-08-14 16:31:07 -07001721 }
1722 return false;
1723 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001724 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001725 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001726 // if the next item is a header, skip it
1727 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001728 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001729 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001730 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1731 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1732 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001733 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001734 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001735 }
1736 return false;
1737 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001738 // Consider left/right arrow navigation
1739 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1740 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1741 // 37 LEFT ARROW
1742 // go left only if current column is not left-most column (last column)
1743 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1744 $('li', $selectedUl).removeClass('jd-selected');
1745 gSelectedColumn++;
1746 $selectedUl = $columns[gSelectedColumn];
1747 // keep or reset the selected item to last item as appropriate
1748 gSelectedIndex = gSelectedIndex >
1749 $("li", $selectedUl).length-1 ?
1750 $("li", $selectedUl).length-1 : gSelectedIndex;
1751 // if the corresponding item is a header, move down
1752 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1753 gSelectedIndex++;
1754 }
Scott Main3b90aff2013-08-01 18:09:35 -07001755 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001756 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1757 return false;
1758 }
1759 // 39 RIGHT ARROW
1760 // go right only if current column is not the right-most column (first column)
1761 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1762 $('li', $selectedUl).removeClass('jd-selected');
1763 gSelectedColumn--;
1764 $selectedUl = $columns[gSelectedColumn];
1765 // keep or reset the selected item to last item as appropriate
1766 gSelectedIndex = gSelectedIndex >
1767 $("li", $selectedUl).length-1 ?
1768 $("li", $selectedUl).length-1 : gSelectedIndex;
1769 // if the corresponding item is a header, move down
1770 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1771 gSelectedIndex++;
1772 }
Scott Main3b90aff2013-08-01 18:09:35 -07001773 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001774 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1775 return false;
1776 }
1777 }
1778
Scott Main7e447ed2013-02-19 17:22:37 -08001779 // if key-up event and not arrow down/up,
1780 // read the search query and add suggestsions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001781 else if (!kd && (e.keyCode != 40)
1782 && (e.keyCode != 38)
1783 && (e.keyCode != 37)
1784 && (e.keyCode != 39)) {
1785 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001786 gMatches = new Array();
1787 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001788 gGoogleMatches = new Array();
1789 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001790 gDocsMatches = new Array();
1791 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001792
1793 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001794 for (var i=0; i<DATA.length; i++) {
1795 var s = DATA[i];
1796 if (text.length != 0 &&
1797 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1798 gMatches[matchedCount] = s;
1799 matchedCount++;
1800 }
1801 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001802 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001803 for (var i=0; i<gMatches.length; i++) {
1804 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001805 }
1806
1807
1808 // Search for Google matches
1809 for (var i=0; i<GOOGLE_DATA.length; i++) {
1810 var s = GOOGLE_DATA[i];
1811 if (text.length != 0 &&
1812 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1813 gGoogleMatches[matchedCountGoogle] = s;
1814 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001815 }
1816 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001817 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001818 for (var i=0; i<gGoogleMatches.length; i++) {
1819 var s = gGoogleMatches[i];
1820 }
1821
Scott Mainf5089842012-08-14 16:31:07 -07001822 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001823
1824
1825
1826 // Search for JD docs
1827 if (text.length >= 3) {
1828 for (var i=0; i<JD_DATA.length; i++) {
1829 // Regex to match only the beginning of a word
1830 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1831 // current search comparison, with counters for tag and title,
1832 // used later to improve ranking
1833 var s = JD_DATA[i];
1834 s.matched_tag = 0;
1835 s.matched_title = 0;
1836 var matched = false;
1837
1838 // Check if query matches any tags; work backwards toward 1 to assist ranking
1839 for (var j = s.tags.length - 1; j >= 0; j--) {
1840 // it matches a tag
1841 if (s.tags[j].toLowerCase().match(textRegex)) {
1842 matched = true;
1843 s.matched_tag = j + 1; // add 1 to index position
1844 }
1845 }
1846 // Don't consider doc title for lessons (only for class landing pages)
1847 // ...it is not a training lesson (or is but has matched a tag)
1848 if (!(s.type == "training" && s.link.indexOf("index.html") == -1) || matched) {
1849 // it matches the doc title
1850 if (s.label.toLowerCase().match(textRegex)) {
1851 matched = true;
1852 s.matched_title = 1;
1853 }
1854 }
1855 if (matched) {
1856 gDocsMatches[matchedCountDocs] = s;
1857 matchedCountDocs++;
1858 }
1859 }
1860 rank_autocomplete_doc_results(text, gDocsMatches);
1861 }
1862
1863 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07001864 sync_selection_table(toroot);
1865 return true; // allow the event to bubble up to the search api
1866 }
1867}
1868
Scott Main0e76e7e2013-03-12 10:24:07 -07001869/* Order the jd doc result list based on match quality */
1870function rank_autocomplete_doc_results(query, matches) {
1871 query = query || '';
1872 if (!matches || !matches.length)
1873 return;
1874
1875 var _resultScoreFn = function(match) {
1876 var score = 1.0;
1877
1878 // if the query matched a tag
1879 if (match.matched_tag > 0) {
1880 // multiply score by factor relative to position in tags list (max of 3)
1881 score *= 3 / match.matched_tag;
1882
1883 // if it also matched the title
1884 if (match.matched_title > 0) {
1885 score *= 2;
1886 }
1887 } else if (match.matched_title > 0) {
1888 score *= 3;
1889 }
1890
1891 return score;
1892 };
1893
1894 for (var i=0; i<matches.length; i++) {
1895 matches[i].__resultScore = _resultScoreFn(matches[i]);
1896 }
1897
1898 matches.sort(function(a,b){
1899 var n = b.__resultScore - a.__resultScore;
1900 if (n == 0) // lexicographical sort if scores are the same
1901 n = (a.label < b.label) ? -1 : 1;
1902 return n;
1903 });
1904}
1905
Scott Main7e447ed2013-02-19 17:22:37 -08001906/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07001907function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07001908 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08001909 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07001910 return;
1911
1912 // helper function that gets the last occurence index of the given regex
1913 // in the given string, or -1 if not found
1914 var _lastSearch = function(s, re) {
1915 if (s == '')
1916 return -1;
1917 var l = -1;
1918 var tmp;
1919 while ((tmp = s.search(re)) >= 0) {
1920 if (l < 0) l = 0;
1921 l += tmp;
1922 s = s.substr(tmp + 1);
1923 }
1924 return l;
1925 };
1926
1927 // helper function that counts the occurrences of a given character in
1928 // a given string
1929 var _countChar = function(s, c) {
1930 var n = 0;
1931 for (var i=0; i<s.length; i++)
1932 if (s.charAt(i) == c) ++n;
1933 return n;
1934 };
1935
1936 var queryLower = query.toLowerCase();
1937 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1938 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1939 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1940
1941 var _resultScoreFn = function(result) {
1942 // scores are calculated based on exact and prefix matches,
1943 // and then number of path separators (dots) from the last
1944 // match (i.e. favoring classes and deep package names)
1945 var score = 1.0;
1946 var labelLower = result.label.toLowerCase();
1947 var t;
1948 t = _lastSearch(labelLower, partExactAlnumRE);
1949 if (t >= 0) {
1950 // exact part match
1951 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1952 score *= 200 / (partsAfter + 1);
1953 } else {
1954 t = _lastSearch(labelLower, partPrefixAlnumRE);
1955 if (t >= 0) {
1956 // part prefix match
1957 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1958 score *= 20 / (partsAfter + 1);
1959 }
1960 }
1961
1962 return score;
1963 };
1964
Scott Main7e447ed2013-02-19 17:22:37 -08001965 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001966 // if the API is deprecated, default score is 0; otherwise, perform scoring
1967 if (matches[i].deprecated == "true") {
1968 matches[i].__resultScore = 0;
1969 } else {
1970 matches[i].__resultScore = _resultScoreFn(matches[i]);
1971 }
Scott Mainf5089842012-08-14 16:31:07 -07001972 }
1973
Scott Main7e447ed2013-02-19 17:22:37 -08001974 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07001975 var n = b.__resultScore - a.__resultScore;
1976 if (n == 0) // lexicographical sort if scores are the same
1977 n = (a.label < b.label) ? -1 : 1;
1978 return n;
1979 });
1980}
1981
Scott Main7e447ed2013-02-19 17:22:37 -08001982/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07001983function highlight_autocomplete_result_labels(query) {
1984 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08001985 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07001986 return;
1987
1988 var queryLower = query.toLowerCase();
1989 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
1990 var queryRE = new RegExp(
1991 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
1992 for (var i=0; i<gMatches.length; i++) {
1993 gMatches[i].__hilabel = gMatches[i].label.replace(
1994 queryRE, '<b>$1</b>');
1995 }
Scott Main7e447ed2013-02-19 17:22:37 -08001996 for (var i=0; i<gGoogleMatches.length; i++) {
1997 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
1998 queryRE, '<b>$1</b>');
1999 }
Scott Mainf5089842012-08-14 16:31:07 -07002000}
2001
2002function search_focus_changed(obj, focused)
2003{
Scott Main3b90aff2013-08-01 18:09:35 -07002004 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002005 if(obj.value == ""){
2006 $(".search .close").addClass("hide");
2007 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002008 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002009 }
2010}
2011
2012function submit_search() {
2013 var query = document.getElementById('search_autocomplete').value;
2014 location.hash = 'q=' + query;
2015 loadSearchResults();
2016 $("#searchResults").slideDown('slow');
2017 return false;
2018}
2019
2020
2021function hideResults() {
2022 $("#searchResults").slideUp();
2023 $(".search .close").addClass("hide");
2024 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002025
Scott Mainf5089842012-08-14 16:31:07 -07002026 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002027
Scott Mainf5089842012-08-14 16:31:07 -07002028 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2029 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002030
2031 // forcefully regain key-up event control (previously jacked by search api)
2032 $("#search_autocomplete").keyup(function(event) {
2033 return search_changed(event, false, toRoot);
2034 });
2035
Scott Mainf5089842012-08-14 16:31:07 -07002036 return false;
2037}
2038
2039
2040
2041/* ########################################################## */
2042/* ################ CUSTOM SEARCH ENGINE ################## */
2043/* ########################################################## */
2044
Scott Mainf5089842012-08-14 16:31:07 -07002045var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002046google.load('search', '1', {"callback" : function() {
2047 searchControl = new google.search.SearchControl();
2048 } });
Scott Mainf5089842012-08-14 16:31:07 -07002049
2050function loadSearchResults() {
2051 document.getElementById("search_autocomplete").style.color = "#000";
2052
Scott Mainf5089842012-08-14 16:31:07 -07002053 searchControl = new google.search.SearchControl();
2054
2055 // use our existing search form and use tabs when multiple searchers are used
2056 drawOptions = new google.search.DrawOptions();
2057 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2058 drawOptions.setInput(document.getElementById("search_autocomplete"));
2059
2060 // configure search result options
2061 searchOptions = new google.search.SearcherOptions();
2062 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2063
2064 // configure each of the searchers, for each tab
2065 devSiteSearcher = new google.search.WebSearch();
2066 devSiteSearcher.setUserDefinedLabel("All");
2067 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2068
2069 designSearcher = new google.search.WebSearch();
2070 designSearcher.setUserDefinedLabel("Design");
2071 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2072
2073 trainingSearcher = new google.search.WebSearch();
2074 trainingSearcher.setUserDefinedLabel("Training");
2075 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2076
2077 guidesSearcher = new google.search.WebSearch();
2078 guidesSearcher.setUserDefinedLabel("Guides");
2079 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2080
2081 referenceSearcher = new google.search.WebSearch();
2082 referenceSearcher.setUserDefinedLabel("Reference");
2083 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2084
Scott Maindf08ada2012-12-03 08:54:37 -08002085 googleSearcher = new google.search.WebSearch();
2086 googleSearcher.setUserDefinedLabel("Google Services");
2087 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2088
Scott Mainf5089842012-08-14 16:31:07 -07002089 blogSearcher = new google.search.WebSearch();
2090 blogSearcher.setUserDefinedLabel("Blog");
2091 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2092
2093 // add each searcher to the search control
2094 searchControl.addSearcher(devSiteSearcher, searchOptions);
2095 searchControl.addSearcher(designSearcher, searchOptions);
2096 searchControl.addSearcher(trainingSearcher, searchOptions);
2097 searchControl.addSearcher(guidesSearcher, searchOptions);
2098 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002099 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002100 searchControl.addSearcher(blogSearcher, searchOptions);
2101
2102 // configure result options
2103 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2104 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2105 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2106 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2107
2108 // upon ajax search, refresh the url and search title
2109 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2110 updateResultTitle(query);
2111 var query = document.getElementById('search_autocomplete').value;
2112 location.hash = 'q=' + query;
2113 });
2114
Scott Mainde295272013-03-25 15:48:35 -07002115 // once search results load, set up click listeners
2116 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2117 addResultClickListeners();
2118 });
2119
Scott Mainf5089842012-08-14 16:31:07 -07002120 // draw the search results box
2121 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2122
2123 // get query and execute the search
2124 searchControl.execute(decodeURI(getQuery(location.hash)));
2125
2126 document.getElementById("search_autocomplete").focus();
2127 addTabListeners();
2128}
2129// End of loadSearchResults
2130
2131
2132google.setOnLoadCallback(function(){
2133 if (location.hash.indexOf("q=") == -1) {
2134 // if there's no query in the url, don't search and make sure results are hidden
2135 $('#searchResults').hide();
2136 return;
2137 } else {
2138 // first time loading search results for this page
2139 $('#searchResults').slideDown('slow');
2140 $(".search .close").removeClass("hide");
2141 loadSearchResults();
2142 }
2143}, true);
2144
2145// when an event on the browser history occurs (back, forward, load) requery hash and do search
2146$(window).hashchange( function(){
2147 // Exit if the hash isn't a search query or there's an error in the query
2148 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2149 // If the results pane is open, close it.
2150 if (!$("#searchResults").is(":hidden")) {
2151 hideResults();
2152 }
2153 return;
2154 }
2155
2156 // Otherwise, we have a search to do
2157 var query = decodeURI(getQuery(location.hash));
2158 searchControl.execute(query);
2159 $('#searchResults').slideDown('slow');
2160 $("#search_autocomplete").focus();
2161 $(".search .close").removeClass("hide");
2162
2163 updateResultTitle(query);
2164});
2165
2166function updateResultTitle(query) {
2167 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2168}
2169
2170// forcefully regain key-up event control (previously jacked by search api)
2171$("#search_autocomplete").keyup(function(event) {
2172 return search_changed(event, false, toRoot);
2173});
2174
2175// add event listeners to each tab so we can track the browser history
2176function addTabListeners() {
2177 var tabHeaders = $(".gsc-tabHeader");
2178 for (var i = 0; i < tabHeaders.length; i++) {
2179 $(tabHeaders[i]).attr("id",i).click(function() {
2180 /*
2181 // make a copy of the page numbers for the search left pane
2182 setTimeout(function() {
2183 // remove any residual page numbers
2184 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002185 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002186 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002187 // and because we're going to remove it (previous line),
2188 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002189 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2190 .clone().appendTo('#searchResults .gsc-tabsArea');
2191 }, 200);
2192 */
2193 });
2194 }
2195 setTimeout(function(){$(tabHeaders[0]).click()},200);
2196}
2197
Scott Mainde295272013-03-25 15:48:35 -07002198// add analytics tracking events to each result link
2199function addResultClickListeners() {
2200 $("#searchResults a.gs-title").each(function(index, link) {
2201 // When user clicks enter for Google search results, track it
2202 $(link).click(function() {
2203 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2204 'from: ' + $("#search_autocomplete").val()]);
2205 });
2206 });
2207}
2208
Scott Mainf5089842012-08-14 16:31:07 -07002209
2210function getQuery(hash) {
2211 var queryParts = hash.split('=');
2212 return queryParts[1];
2213}
2214
2215/* returns the given string with all HTML brackets converted to entities
2216 TODO: move this to the site's JS library */
2217function escapeHTML(string) {
2218 return string.replace(/</g,"&lt;")
2219 .replace(/>/g,"&gt;");
2220}
2221
2222
2223
2224
2225
2226
2227
2228/* ######################################################## */
2229/* ################# JAVADOC REFERENCE ################### */
2230/* ######################################################## */
2231
Scott Main65511c02012-09-07 15:51:32 -07002232/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002233if (location.pathname.indexOf("/reference") == 0) {
2234 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2235 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2236 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002237 $(document).ready(function() {
2238 // init available apis based on user pref
2239 changeApiLevel();
2240 initSidenavHeightResize()
2241 });
2242 }
Scott Main65511c02012-09-07 15:51:32 -07002243}
Scott Mainf5089842012-08-14 16:31:07 -07002244
2245var API_LEVEL_COOKIE = "api_level";
2246var minLevel = 1;
2247var maxLevel = 1;
2248
2249/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002250
Scott Mainf5089842012-08-14 16:31:07 -07002251 function initSidenavHeightResize() {
2252 // Change the drag bar size to nicely fit the scrollbar positions
2253 var $dragBar = $(".ui-resizable-s");
2254 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002255
2256 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002257 containment: "#nav-panels",
2258 handles: "s",
2259 alsoResize: "#packages-nav",
2260 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2261 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2262 });
Scott Main3b90aff2013-08-01 18:09:35 -07002263
Scott Mainf5089842012-08-14 16:31:07 -07002264 }
Scott Main3b90aff2013-08-01 18:09:35 -07002265
Scott Mainf5089842012-08-14 16:31:07 -07002266function updateSidenavFixedWidth() {
2267 if (!navBarIsFixed) return;
2268 $('#devdoc-nav').css({
2269 'width' : $('#side-nav').css('width'),
2270 'margin' : $('#side-nav').css('margin')
2271 });
2272 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002273
Scott Mainf5089842012-08-14 16:31:07 -07002274 initSidenavHeightResize();
2275}
2276
2277function updateSidenavFullscreenWidth() {
2278 if (!navBarIsFixed) return;
2279 $('#devdoc-nav').css({
2280 'width' : $('#side-nav').css('width'),
2281 'margin' : $('#side-nav').css('margin')
2282 });
2283 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002284
Scott Mainf5089842012-08-14 16:31:07 -07002285 initSidenavHeightResize();
2286}
2287
2288function buildApiLevelSelector() {
2289 maxLevel = SINCE_DATA.length;
2290 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2291 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2292
2293 minLevel = parseInt($("#doc-api-level").attr("class"));
2294 // Handle provisional api levels; the provisional level will always be the highest possible level
2295 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2296 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2297 if (isNaN(minLevel) && minLevel.length) {
2298 minLevel = maxLevel;
2299 }
2300 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2301 for (var i = maxLevel-1; i >= 0; i--) {
2302 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2303 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2304 select.append(option);
2305 }
2306
2307 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2308 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2309 selectedLevelItem.setAttribute('selected',true);
2310}
2311
2312function changeApiLevel() {
2313 maxLevel = SINCE_DATA.length;
2314 var selectedLevel = maxLevel;
2315
2316 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2317 toggleVisisbleApis(selectedLevel, "body");
2318
2319 var date = new Date();
2320 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2321 var expiration = date.toGMTString();
2322 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2323
2324 if (selectedLevel < minLevel) {
2325 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002326 $("#naMessage").show().html("<div><p><strong>This " + thing
2327 + " requires API level " + minLevel + " or higher.</strong></p>"
2328 + "<p>This document is hidden because your selected API level for the documentation is "
2329 + selectedLevel + ". You can change the documentation API level with the selector "
2330 + "above the left navigation.</p>"
2331 + "<p>For more information about specifying the API level your app requires, "
2332 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2333 + ">Supporting Different Platform Versions</a>.</p>"
2334 + "<input type='button' value='OK, make this page visible' "
2335 + "title='Change the API level to " + minLevel + "' "
2336 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2337 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002338 } else {
2339 $("#naMessage").hide();
2340 }
2341}
2342
2343function toggleVisisbleApis(selectedLevel, context) {
2344 var apis = $(".api",context);
2345 apis.each(function(i) {
2346 var obj = $(this);
2347 var className = obj.attr("class");
2348 var apiLevelIndex = className.lastIndexOf("-")+1;
2349 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2350 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2351 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2352 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2353 return;
2354 }
2355 apiLevel = parseInt(apiLevel);
2356
2357 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2358 var selectedLevelNum = parseInt(selectedLevel)
2359 var apiLevelNum = parseInt(apiLevel);
2360 if (isNaN(apiLevelNum)) {
2361 apiLevelNum = maxLevel;
2362 }
2363
2364 // Grey things out that aren't available and give a tooltip title
2365 if (apiLevelNum > selectedLevelNum) {
2366 obj.addClass("absent").attr("title","Requires API Level \""
2367 + apiLevel + "\" or higher");
Scott Main3b90aff2013-08-01 18:09:35 -07002368 }
Scott Mainf5089842012-08-14 16:31:07 -07002369 else obj.removeClass("absent").removeAttr("title");
2370 });
2371}
2372
2373
2374
2375
2376/* ################# SIDENAV TREE VIEW ################### */
2377
2378function new_node(me, mom, text, link, children_data, api_level)
2379{
2380 var node = new Object();
2381 node.children = Array();
2382 node.children_data = children_data;
2383 node.depth = mom.depth + 1;
2384
2385 node.li = document.createElement("li");
2386 mom.get_children_ul().appendChild(node.li);
2387
2388 node.label_div = document.createElement("div");
2389 node.label_div.className = "label";
2390 if (api_level != null) {
2391 $(node.label_div).addClass("api");
2392 $(node.label_div).addClass("api-level-"+api_level);
2393 }
2394 node.li.appendChild(node.label_div);
2395
2396 if (children_data != null) {
2397 node.expand_toggle = document.createElement("a");
2398 node.expand_toggle.href = "javascript:void(0)";
2399 node.expand_toggle.onclick = function() {
2400 if (node.expanded) {
2401 $(node.get_children_ul()).slideUp("fast");
2402 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2403 node.expanded = false;
2404 } else {
2405 expand_node(me, node);
2406 }
2407 };
2408 node.label_div.appendChild(node.expand_toggle);
2409
2410 node.plus_img = document.createElement("img");
2411 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2412 node.plus_img.className = "plus";
2413 node.plus_img.width = "8";
2414 node.plus_img.border = "0";
2415 node.expand_toggle.appendChild(node.plus_img);
2416
2417 node.expanded = false;
2418 }
2419
2420 var a = document.createElement("a");
2421 node.label_div.appendChild(a);
2422 node.label = document.createTextNode(text);
2423 a.appendChild(node.label);
2424 if (link) {
2425 a.href = me.toroot + link;
2426 } else {
2427 if (children_data != null) {
2428 a.className = "nolink";
2429 a.href = "javascript:void(0)";
2430 a.onclick = node.expand_toggle.onclick;
2431 // This next line shouldn't be necessary. I'll buy a beer for the first
2432 // person who figures out how to remove this line and have the link
2433 // toggle shut on the first try. --joeo@android.com
2434 node.expanded = false;
2435 }
2436 }
Scott Main3b90aff2013-08-01 18:09:35 -07002437
Scott Mainf5089842012-08-14 16:31:07 -07002438
2439 node.children_ul = null;
2440 node.get_children_ul = function() {
2441 if (!node.children_ul) {
2442 node.children_ul = document.createElement("ul");
2443 node.children_ul.className = "children_ul";
2444 node.children_ul.style.display = "none";
2445 node.li.appendChild(node.children_ul);
2446 }
2447 return node.children_ul;
2448 };
2449
2450 return node;
2451}
2452
Robert Lyd2dd6e52012-11-29 21:28:48 -08002453
2454
2455
Scott Mainf5089842012-08-14 16:31:07 -07002456function expand_node(me, node)
2457{
2458 if (node.children_data && !node.expanded) {
2459 if (node.children_visited) {
2460 $(node.get_children_ul()).slideDown("fast");
2461 } else {
2462 get_node(me, node);
2463 if ($(node.label_div).hasClass("absent")) {
2464 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002465 }
Scott Mainf5089842012-08-14 16:31:07 -07002466 $(node.get_children_ul()).slideDown("fast");
2467 }
2468 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2469 node.expanded = true;
2470
2471 // perform api level toggling because new nodes are new to the DOM
2472 var selectedLevel = $("#apiLevelSelector option:selected").val();
2473 toggleVisisbleApis(selectedLevel, "#side-nav");
2474 }
2475}
2476
2477function get_node(me, mom)
2478{
2479 mom.children_visited = true;
2480 for (var i in mom.children_data) {
2481 var node_data = mom.children_data[i];
2482 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2483 node_data[2], node_data[3]);
2484 }
2485}
2486
2487function this_page_relative(toroot)
2488{
2489 var full = document.location.pathname;
2490 var file = "";
2491 if (toroot.substr(0, 1) == "/") {
2492 if (full.substr(0, toroot.length) == toroot) {
2493 return full.substr(toroot.length);
2494 } else {
2495 // the file isn't under toroot. Fail.
2496 return null;
2497 }
2498 } else {
2499 if (toroot != "./") {
2500 toroot = "./" + toroot;
2501 }
2502 do {
2503 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2504 var pos = full.lastIndexOf("/");
2505 file = full.substr(pos) + file;
2506 full = full.substr(0, pos);
2507 toroot = toroot.substr(0, toroot.length-3);
2508 }
2509 } while (toroot != "" && toroot != "/");
2510 return file.substr(1);
2511 }
2512}
2513
2514function find_page(url, data)
2515{
2516 var nodes = data;
2517 var result = null;
2518 for (var i in nodes) {
2519 var d = nodes[i];
2520 if (d[1] == url) {
2521 return new Array(i);
2522 }
2523 else if (d[2] != null) {
2524 result = find_page(url, d[2]);
2525 if (result != null) {
2526 return (new Array(i).concat(result));
2527 }
2528 }
2529 }
2530 return null;
2531}
2532
Scott Mainf5089842012-08-14 16:31:07 -07002533function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002534 // load json file for navtree data
2535 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2536 // when the file is loaded, initialize the tree
2537 if(jqxhr.status === 200) {
2538 init_navtree("tree-list", toroot, NAVTREE_DATA);
2539 }
2540 });
Scott Main3b90aff2013-08-01 18:09:35 -07002541
Scott Mainf5089842012-08-14 16:31:07 -07002542 // perform api level toggling because because the whole tree is new to the DOM
2543 var selectedLevel = $("#apiLevelSelector option:selected").val();
2544 toggleVisisbleApis(selectedLevel, "#side-nav");
2545}
2546
2547function init_navtree(navtree_id, toroot, root_nodes)
2548{
2549 var me = new Object();
2550 me.toroot = toroot;
2551 me.node = new Object();
2552
2553 me.node.li = document.getElementById(navtree_id);
2554 me.node.children_data = root_nodes;
2555 me.node.children = new Array();
2556 me.node.children_ul = document.createElement("ul");
2557 me.node.get_children_ul = function() { return me.node.children_ul; };
2558 //me.node.children_ul.className = "children_ul";
2559 me.node.li.appendChild(me.node.children_ul);
2560 me.node.depth = 0;
2561
2562 get_node(me, me.node);
2563
2564 me.this_page = this_page_relative(toroot);
2565 me.breadcrumbs = find_page(me.this_page, root_nodes);
2566 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2567 var mom = me.node;
2568 for (var i in me.breadcrumbs) {
2569 var j = me.breadcrumbs[i];
2570 mom = mom.children[j];
2571 expand_node(me, mom);
2572 }
2573 mom.label_div.className = mom.label_div.className + " selected";
2574 addLoadEvent(function() {
2575 scrollIntoView("nav-tree");
2576 });
2577 }
2578}
2579
Robert Lyd2dd6e52012-11-29 21:28:48 -08002580/* TODO: eliminate redundancy with non-google functions */
2581function init_google_navtree(navtree_id, toroot, root_nodes)
2582{
2583 var me = new Object();
2584 me.toroot = toroot;
2585 me.node = new Object();
2586
2587 me.node.li = document.getElementById(navtree_id);
2588 me.node.children_data = root_nodes;
2589 me.node.children = new Array();
2590 me.node.children_ul = document.createElement("ul");
2591 me.node.get_children_ul = function() { return me.node.children_ul; };
2592 //me.node.children_ul.className = "children_ul";
2593 me.node.li.appendChild(me.node.children_ul);
2594 me.node.depth = 0;
2595
2596 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002597}
2598
2599function new_google_node(me, mom, text, link, children_data, api_level)
2600{
2601 var node = new Object();
2602 var child;
2603 node.children = Array();
2604 node.children_data = children_data;
2605 node.depth = mom.depth + 1;
2606 node.get_children_ul = function() {
2607 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07002608 node.children_ul = document.createElement("ul");
2609 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002610 node.li.appendChild(node.children_ul);
2611 }
2612 return node.children_ul;
2613 };
2614 node.li = document.createElement("li");
2615
2616 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07002617
2618
Robert Lyd2dd6e52012-11-29 21:28:48 -08002619 if(link) {
2620 child = document.createElement("a");
2621
2622 }
2623 else {
2624 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08002625 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002626
2627 }
2628 if (children_data != null) {
2629 node.li.className="nav-section";
2630 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07002631 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002632 node.li.appendChild(node.label_div);
2633 get_google_node(me, node);
2634 node.label_div.appendChild(child);
2635 }
2636 else {
2637 node.li.appendChild(child);
2638 }
2639 if(link) {
2640 child.href = me.toroot + link;
2641 }
2642 node.label = document.createTextNode(text);
2643 child.appendChild(node.label);
2644
2645 node.children_ul = null;
2646
2647 return node;
2648}
2649
2650function get_google_node(me, mom)
2651{
2652 mom.children_visited = true;
2653 var linkText;
2654 for (var i in mom.children_data) {
2655 var node_data = mom.children_data[i];
2656 linkText = node_data[0];
2657
2658 if(linkText.match("^"+"com.google.android")=="com.google.android"){
2659 linkText = linkText.substr(19, linkText.length);
2660 }
2661 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
2662 node_data[2], node_data[3]);
2663 }
2664}
2665function showGoogleRefTree() {
2666 init_default_google_navtree(toRoot);
2667 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002668}
2669
2670function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07002671 // load json file for navtree data
2672 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
2673 // when the file is loaded, initialize the tree
2674 if(jqxhr.status === 200) {
2675 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
2676 highlightSidenav();
2677 resizeNav();
2678 }
2679 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08002680}
2681
2682function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07002683 // load json file for navtree data
2684 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
2685 // when the file is loaded, initialize the tree
2686 if(jqxhr.status === 200) {
2687 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
2688 highlightSidenav();
2689 resizeNav();
2690 }
2691 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08002692}
2693
Scott Mainf5089842012-08-14 16:31:07 -07002694/* TOGGLE INHERITED MEMBERS */
2695
2696/* Toggle an inherited class (arrow toggle)
2697 * @param linkObj The link that was clicked.
2698 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2699 * 'null' to simply toggle.
2700 */
2701function toggleInherited(linkObj, expand) {
2702 var base = linkObj.getAttribute("id");
2703 var list = document.getElementById(base + "-list");
2704 var summary = document.getElementById(base + "-summary");
2705 var trigger = document.getElementById(base + "-trigger");
2706 var a = $(linkObj);
2707 if ( (expand == null && a.hasClass("closed")) || expand ) {
2708 list.style.display = "none";
2709 summary.style.display = "block";
2710 trigger.src = toRoot + "assets/images/triangle-opened.png";
2711 a.removeClass("closed");
2712 a.addClass("opened");
2713 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2714 list.style.display = "block";
2715 summary.style.display = "none";
2716 trigger.src = toRoot + "assets/images/triangle-closed.png";
2717 a.removeClass("opened");
2718 a.addClass("closed");
2719 }
2720 return false;
2721}
2722
2723/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2724 * @param linkObj The link that was clicked.
2725 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2726 * 'null' to simply toggle.
2727 */
2728function toggleAllInherited(linkObj, expand) {
2729 var a = $(linkObj);
2730 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2731 var expandos = $(".jd-expando-trigger", table);
2732 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2733 expandos.each(function(i) {
2734 toggleInherited(this, true);
2735 });
2736 a.text("[Collapse]");
2737 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2738 expandos.each(function(i) {
2739 toggleInherited(this, false);
2740 });
2741 a.text("[Expand]");
2742 }
2743 return false;
2744}
2745
2746/* Toggle all inherited members in the class (link in the class title)
2747 */
2748function toggleAllClassInherited() {
2749 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2750 var toggles = $(".toggle-all", $("#body-content"));
2751 if (a.text() == "[Expand All]") {
2752 toggles.each(function(i) {
2753 toggleAllInherited(this, true);
2754 });
2755 a.text("[Collapse All]");
2756 } else {
2757 toggles.each(function(i) {
2758 toggleAllInherited(this, false);
2759 });
2760 a.text("[Expand All]");
2761 }
2762 return false;
2763}
2764
2765/* Expand all inherited members in the class. Used when initiating page search */
2766function ensureAllInheritedExpanded() {
2767 var toggles = $(".toggle-all", $("#body-content"));
2768 toggles.each(function(i) {
2769 toggleAllInherited(this, true);
2770 });
2771 $("#toggleAllClassInherited").text("[Collapse All]");
2772}
2773
2774
2775/* HANDLE KEY EVENTS
2776 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2777 */
2778var agent = navigator['userAgent'].toLowerCase();
2779var mac = agent.indexOf("macintosh") != -1;
2780
2781$(document).keydown( function(e) {
2782var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2783 if (control && e.which == 70) { // 70 is "F"
2784 ensureAllInheritedExpanded();
2785 }
2786});