blob: e6befe36da29b47e7f1cb8e1676a73f9814ffdc3 [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
Scott Main719acb42013-12-05 16:05:09 -080026 $.getScript(toRoot + 'jd_lists_unified.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");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700170 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800171
172 // highlight Develop tab
173 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
174 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700175 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800176 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800177 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800178 if (rootDir == "training") {
179 $("#nav-x li.training a").addClass("selected");
180 } else if (rootDir == "guide") {
181 $("#nav-x li.guide a").addClass("selected");
182 } else if (rootDir == "reference") {
183 // If the root is reference, but page is also part of Google Services, select Google
184 if ($("body").hasClass("google")) {
185 $("#nav-x li.google a").addClass("selected");
186 } else {
187 $("#nav-x li.reference a").addClass("selected");
188 }
189 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
190 $("#nav-x li.tools a").addClass("selected");
191 } else if ($("body").hasClass("google")) {
192 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700193 } else if ($("body").hasClass("samples")) {
194 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800195 }
196
197 // highlight Distribute tab
198 } else if ($("body").hasClass("distribute")) {
199 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700200 $("#sticky-header").addClass("distribute");
201
202 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
203 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
204 if (secondFrag == "users") {
205 $("#nav-x li.users a").addClass("selected");
206 } else if (secondFrag == "engage") {
207 $("#nav-x li.engage a").addClass("selected");
208 } else if (secondFrag == "monetize") {
209 $("#nav-x li.monetize a").addClass("selected");
210 } else if (secondFrag == "tools") {
211 $("#nav-x li.disttools a").addClass("selected");
212 } else if (secondFrag == "stories") {
213 $("#nav-x li.stories a").addClass("selected");
214 } else if (secondFrag == "essentials") {
215 $("#nav-x li.essentials a").addClass("selected");
216 } else if (secondFrag == "googleplay") {
217 $("#nav-x li.googleplay a").addClass("selected");
218 }
219 } else if ($("body").hasClass("about")) {
220 $("#sticky-header").addClass("about");
221 }
Scott Mainac2aef52013-02-12 14:15:23 -0800222
Scott Mainf6145542013-04-01 16:38:11 -0700223 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
224 // and highlight the sidenav
225 mPagePath = pagePath;
226 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700227 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800228
Scott Mainf6145542013-04-01 16:38:11 -0700229 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700230 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700231 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700232 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800233 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700234
235 // set up prev links
236 var $prevLink = [];
237 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700238
Scott Maine4d8f1b2012-06-21 18:03:05 -0700239 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
240false; // navigate across topic boundaries only in design docs
241 if ($prevListItem.length) {
242 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700243 // jump to last topic of previous section
244 $prevLink = $prevListItem.find('a:last');
245 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700246 // jump to previous topic in this section
247 $prevLink = $prevListItem.find('a:eq(0)');
248 }
249 } else {
250 // jump to this section's index page (if it exists)
251 var $parentListItem = $selListItem.parents('li');
252 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700253
Scott Maine4d8f1b2012-06-21 18:03:05 -0700254 // except if cross boundaries aren't allowed, and we're at the top of a section already
255 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700256 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700257 && $selListItem.hasClass('nav-section')) {
258 $prevLink = [];
259 }
260 }
261
Scott Maine4d8f1b2012-06-21 18:03:05 -0700262 // set up next links
263 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700264 var startClass = false;
265 var training = $(".next-class-link").length; // decides whether to provide "next class" link
266 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700267
Scott Main1a00f7f2013-10-29 11:11:19 -0700268 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700269 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700270 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700271
272 // if there aren't any children, go to the next section (required for About pages)
273 if($nextLink.length == 0) {
274 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700275 } else if ($('.topic-start-link').length) {
276 // as long as there's a child link and there is a "topic start link" (we're on a landing)
277 // then set the landing page "start link" text to be the first doc title
278 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700279 }
Scott Main3b90aff2013-08-01 18:09:35 -0700280
Scott Main5a1123e2012-09-26 12:51:28 -0700281 // If the selected page has a description, then it's a class or article homepage
282 if ($selListItem.find('a[description]').length) {
283 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700284 startClass = true;
285 }
286 } else {
287 // jump to the next topic in this section (if it exists)
288 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700289 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700290 isCrossingBoundary = true;
291 // no more topics in this section, jump to the first topic in the next section
292 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
293 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
294 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700295 if ($nextLink.length == 0) {
296 // if that doesn't work, we're at the end of the list, so disable NEXT link
297 $('.next-page-link').attr('href','').addClass("disabled")
298 .click(function() { return false; });
299 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700300 }
301 }
302 }
Scott Main5a1123e2012-09-26 12:51:28 -0700303
304 if (startClass) {
305 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
306
Scott Main3b90aff2013-08-01 18:09:35 -0700307 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700308 // then we need to add a bottom border to button
309 if (!$("#tb").length) {
310 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700311 }
Scott Main5a1123e2012-09-26 12:51:28 -0700312 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
313 $('.content-footer.next-class').show();
314 $('.next-page-link').attr('href','')
315 .removeClass("hide").addClass("disabled")
316 .click(function() { return false; });
Scott Main1a00f7f2013-10-29 11:11:19 -0700317 if ($nextLink.length) {
318 $('.next-class-link').attr('href',$nextLink.attr('href'))
319 .removeClass("hide").append($nextLink.html());
320 $('.next-class-link').find('.new').empty();
321 }
Scott Main5a1123e2012-09-26 12:51:28 -0700322 } else {
323 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
324 }
325
326 if (!startClass && $prevLink.length) {
327 var prevHref = $prevLink.attr('href');
328 if (prevHref == SITE_ROOT + 'index.html') {
329 // Don't show Previous when it leads to the homepage
330 } else {
331 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
332 }
Scott Main3b90aff2013-08-01 18:09:35 -0700333 }
Scott Main5a1123e2012-09-26 12:51:28 -0700334
335 // If this is a training 'article', there should be no prev/next nav
336 // ... if the grandparent is the "nav" ... and it has no child list items...
337 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
338 !$selListItem.find('li').length) {
339 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
340 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700341 }
Scott Main3b90aff2013-08-01 18:09:35 -0700342
Scott Maine4d8f1b2012-06-21 18:03:05 -0700343 }
Scott Main3b90aff2013-08-01 18:09:35 -0700344
345
346
Scott Main5a1123e2012-09-26 12:51:28 -0700347 // Set up the course landing pages for Training with class names and descriptions
348 if ($('body.trainingcourse').length) {
349 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
350 var $classDescriptions = $classLinks.attr('description');
Scott Main3b90aff2013-08-01 18:09:35 -0700351
Scott Main5a1123e2012-09-26 12:51:28 -0700352 var $olClasses = $('<ol class="class-list"></ol>');
353 var $liClass;
354 var $imgIcon;
355 var $h2Title;
356 var $pSummary;
357 var $olLessons;
358 var $liLesson;
359 $classLinks.each(function(index) {
360 $liClass = $('<li></li>');
361 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
362 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700363
Scott Main5a1123e2012-09-26 12:51:28 -0700364 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700365
Scott Main5a1123e2012-09-26 12:51:28 -0700366 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700367
Scott Main5a1123e2012-09-26 12:51:28 -0700368 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700369 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
370 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700371 $lessons.each(function(index) {
372 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
373 });
374 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700375 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
376 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700377 $pSummary.addClass('article');
378 }
379
380 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
381 $olClasses.append($liClass);
382 });
383 $('.jd-descr').append($olClasses);
384 }
385
Scott Maine4d8f1b2012-06-21 18:03:05 -0700386 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700387 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700388
Scott Main3b90aff2013-08-01 18:09:35 -0700389
Scott Maine4d8f1b2012-06-21 18:03:05 -0700390 $(".scroll-pane").scroll(function(event) {
391 event.preventDefault();
392 return false;
393 });
394
395 /* Resize nav height when window height changes */
396 $(window).resize(function() {
397 if ($('#side-nav').length == 0) return;
398 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
399 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
400 // make sidenav behave when resizing the window and side-scolling is a concern
401 if (navBarIsFixed) {
402 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
403 updateSideNavPosition();
404 } else {
405 updateSidenavFullscreenWidth();
406 }
407 }
408 resizeNav();
409 });
410
411
Scott Maine4d8f1b2012-06-21 18:03:05 -0700412 var navBarLeftPos;
413 if ($('#devdoc-nav').length) {
414 setNavBarLeftPos();
415 }
416
417
Scott Maine4d8f1b2012-06-21 18:03:05 -0700418 // Set up play-on-hover <video> tags.
419 $('video.play-on-hover').bind('click', function(){
420 $(this).get(0).load(); // in case the video isn't seekable
421 $(this).get(0).play();
422 });
423
424 // Set up tooltips
425 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700426 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700427 var $target = $(this);
428 var $tooltip = $('<div>')
429 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700430 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700431 .hide()
432 .appendTo('body');
433 $target.removeAttr('title');
434
435 $target.hover(function() {
436 // in
437 var targetRect = $target.offset();
438 targetRect.width = $target.width();
439 targetRect.height = $target.height();
440
441 $tooltip.css({
442 left: targetRect.left,
443 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
444 });
445 $tooltip.addClass('below');
446 $tooltip.show();
447 }, function() {
448 // out
449 $tooltip.hide();
450 });
451 });
452
453 // Set up <h2> deeplinks
454 $('h2').click(function() {
455 var id = $(this).attr('id');
456 if (id) {
457 document.location.hash = id;
458 }
459 });
460
461 //Loads the +1 button
462 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
463 po.src = 'https://apis.google.com/js/plusone.js';
464 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
465
466
Scott Main3b90aff2013-08-01 18:09:35 -0700467 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700468 // which avoids the visible width from changing each time the bar appears
469 var $sidenav = $("#side-nav");
470 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700471
Scott Maine4d8f1b2012-06-21 18:03:05 -0700472 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
473
474
475 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700476
Scott Maine4d8f1b2012-06-21 18:03:05 -0700477 if ($(".scroll-pane").length > 1) {
478 // Check if there's a user preference for the panel heights
479 var cookieHeight = readCookie("reference_height");
480 if (cookieHeight) {
481 restoreHeight(cookieHeight);
482 }
483 }
Scott Main3b90aff2013-08-01 18:09:35 -0700484
Scott Maine4d8f1b2012-06-21 18:03:05 -0700485 resizeNav();
486
Scott Main015d6162013-01-29 09:01:52 -0800487 /* init the language selector based on user cookie for lang */
488 loadLangPref();
489 changeNavLang(getLangPref());
490
491 /* setup event handlers to ensure the overflow menu is visible while picking lang */
492 $("#language select")
493 .mousedown(function() {
494 $("div.morehover").addClass("hover"); })
495 .blur(function() {
496 $("div.morehover").removeClass("hover"); });
497
498 /* some global variable setup */
499 resizePackagesNav = $("#resize-packages-nav");
500 classesNav = $("#classes-nav");
501 devdocNav = $("#devdoc-nav");
502
503 var cookiePath = "";
504 if (location.href.indexOf("/reference/") != -1) {
505 cookiePath = "reference_";
506 } else if (location.href.indexOf("/guide/") != -1) {
507 cookiePath = "guide_";
508 } else if (location.href.indexOf("/tools/") != -1) {
509 cookiePath = "tools_";
510 } else if (location.href.indexOf("/training/") != -1) {
511 cookiePath = "training_";
512 } else if (location.href.indexOf("/design/") != -1) {
513 cookiePath = "design_";
514 } else if (location.href.indexOf("/distribute/") != -1) {
515 cookiePath = "distribute_";
516 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700517
518});
Scott Main7e447ed2013-02-19 17:22:37 -0800519// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700520
521
Scott Mainad08f072013-08-20 16:49:57 -0700522function initExpandableNavItems(rootTag) {
523 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
524 var section = $(this).closest('li.nav-section');
525 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700526 /* hide me and descendants */
527 section.find('ul').slideUp(250, function() {
528 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700529 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700530 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700531 resizeNav();
532 });
533 } else {
534 /* show me */
535 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700536 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700537 $others.removeClass('expanded').children('ul').slideUp(250);
538
539 // now expand me
540 section.closest('li').addClass('expanded');
541 section.children('ul').slideDown(250, function() {
542 resizeNav();
543 });
544 }
545 });
Scott Mainf0093852013-08-22 11:37:11 -0700546
547 // Stop expand/collapse behavior when clicking on nav section links
548 // (since we're navigating away from the page)
549 // This selector captures the first instance of <a>, but not those with "#" as the href.
550 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
551 window.location.href = $(this).attr('href');
552 return false;
553 });
Scott Mainad08f072013-08-20 16:49:57 -0700554}
555
Dirk Doughertyc3921652014-05-13 16:55:26 -0700556
557/** Create the list of breadcrumb links in the sticky header */
558function buildBreadcrumbs() {
559 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
560 // Add the secondary horizontal nav item, if provided
561 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
562 if ($selectedSecondNav.length) {
563 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
564 }
565 // Add the primary horizontal nav
566 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
567 // If there's no header nav item, use the logo link and title from alt text
568 if ($selectedFirstNav.length < 1) {
569 $selectedFirstNav = $("<a>")
570 .attr('href', $("div#header .logo a").attr('href'))
571 .text($("div#header .logo img").attr('alt'));
572 }
573 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
574}
575
576
577
Scott Maine624b3f2013-09-12 12:56:41 -0700578/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700579function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700580 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
581 if ($("ul#nav li.selected").length) {
582 unHighlightSidenav();
583 }
584 // look for URL in sidenav, including the hash
585 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
586
587 // If the selNavLink is still empty, look for it without the hash
588 if ($selNavLink.length == 0) {
589 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
590 }
591
Scott Mainf6145542013-04-01 16:38:11 -0700592 var $selListItem;
593 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700594 // Find this page's <li> in sidenav and set selected
595 $selListItem = $selNavLink.closest('li');
596 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700597
Scott Mainf6145542013-04-01 16:38:11 -0700598 // Traverse up the tree and expand all parent nav-sections
599 $selNavLink.parents('li.nav-section').each(function() {
600 $(this).addClass('expanded');
601 $(this).children('ul').show();
602 });
603 }
604}
605
Scott Maine624b3f2013-09-12 12:56:41 -0700606function unHighlightSidenav() {
607 $("ul#nav li.selected").removeClass("selected");
608 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
609}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700610
611function toggleFullscreen(enable) {
612 var delay = 20;
613 var enabled = true;
614 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
615 if (enable) {
616 // Currently NOT USING fullscreen; enable fullscreen
617 stylesheet.removeAttr('disabled');
618 $('#nav-swap .fullscreen').removeClass('disabled');
619 $('#devdoc-nav').css({left:''});
620 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
621 enabled = true;
622 } else {
623 // Currently USING fullscreen; disable fullscreen
624 stylesheet.attr('disabled', 'disabled');
625 $('#nav-swap .fullscreen').addClass('disabled');
626 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
627 enabled = false;
628 }
629 writeCookie("fullscreen", enabled, null, null);
630 setNavBarLeftPos();
631 resizeNav(delay);
632 updateSideNavPosition();
633 setTimeout(initSidenavHeightResize,delay);
634}
635
636
637function setNavBarLeftPos() {
638 navBarLeftPos = $('#body-content').offset().left;
639}
640
641
642function updateSideNavPosition() {
643 var newLeft = $(window).scrollLeft() - navBarLeftPos;
644 $('#devdoc-nav').css({left: -newLeft});
645 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
646}
Scott Main3b90aff2013-08-01 18:09:35 -0700647
Scott Maine4d8f1b2012-06-21 18:03:05 -0700648// TODO: use $(document).ready instead
649function addLoadEvent(newfun) {
650 var current = window.onload;
651 if (typeof window.onload != 'function') {
652 window.onload = newfun;
653 } else {
654 window.onload = function() {
655 current();
656 newfun();
657 }
658 }
659}
660
661var agent = navigator['userAgent'].toLowerCase();
662// If a mobile phone, set flag and do mobile setup
663if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
664 (agent.indexOf("blackberry") != -1) ||
665 (agent.indexOf("webos") != -1) ||
666 (agent.indexOf("mini") != -1)) { // opera mini browsers
667 isMobile = true;
668}
669
670
Scott Main498d7102013-08-21 15:47:38 -0700671$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700672 $("pre:not(.no-pretty-print)").addClass("prettyprint");
673 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700674});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700675
Scott Maine4d8f1b2012-06-21 18:03:05 -0700676
677
678
679/* ######### RESIZE THE SIDENAV HEIGHT ########## */
680
681function resizeNav(delay) {
682 var $nav = $("#devdoc-nav");
683 var $window = $(window);
684 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700685
Scott Maine4d8f1b2012-06-21 18:03:05 -0700686 // Get the height of entire window and the total header height.
687 // Then figure out based on scroll position whether the header is visible
688 var windowHeight = $window.height();
689 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700690 var headerHeight = $('#header-wrapper').outerHeight();
691 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700692
693 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700694 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700695 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700696 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700697
Scott Maine4d8f1b2012-06-21 18:03:05 -0700698 // Depending on whether the header is visible, set the side nav's height.
699 if (headerVisible) {
700 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700701 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700702 } else {
703 // Once header is off screen, the nav height is almost full window height
704 navHeight = windowHeight - topMargin;
705 }
Scott Main3b90aff2013-08-01 18:09:35 -0700706
707
708
Scott Maine4d8f1b2012-06-21 18:03:05 -0700709 $scrollPanes = $(".scroll-pane");
710 if ($scrollPanes.length > 1) {
711 // subtract the height of the api level widget and nav swapper from the available nav height
712 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700713
Scott Maine4d8f1b2012-06-21 18:03:05 -0700714 $("#swapper").css({height:navHeight + "px"});
715 if ($("#nav-tree").is(":visible")) {
716 $("#nav-tree").css({height:navHeight});
717 }
Scott Main3b90aff2013-08-01 18:09:35 -0700718
719 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700720 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700721
722 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700723 // then the package panel should begin to shrink
724 if (parseInt(classesHeight) <= 0) {
725 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
726 $("#packages-nav").css({height:navHeight - 10});
727 }
Scott Main3b90aff2013-08-01 18:09:35 -0700728
Scott Maine4d8f1b2012-06-21 18:03:05 -0700729 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
730 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700731
732
Scott Maine4d8f1b2012-06-21 18:03:05 -0700733 } else {
734 $nav.height(navHeight);
735 }
Scott Main3b90aff2013-08-01 18:09:35 -0700736
Scott Maine4d8f1b2012-06-21 18:03:05 -0700737 if (delay) {
738 updateFromResize = true;
739 delayedReInitScrollbars(delay);
740 } else {
741 reInitScrollbars();
742 }
Scott Main3b90aff2013-08-01 18:09:35 -0700743
Scott Maine4d8f1b2012-06-21 18:03:05 -0700744}
745
746var updateScrollbars = false;
747var updateFromResize = false;
748
749/* Re-initialize the scrollbars to account for changed nav size.
750 * This method postpones the actual update by a 1/4 second in order to optimize the
751 * scroll performance while the header is still visible, because re-initializing the
752 * scroll panes is an intensive process.
753 */
754function delayedReInitScrollbars(delay) {
755 // If we're scheduled for an update, but have received another resize request
756 // before the scheduled resize has occured, just ignore the new request
757 // (and wait for the scheduled one).
758 if (updateScrollbars && updateFromResize) {
759 updateFromResize = false;
760 return;
761 }
Scott Main3b90aff2013-08-01 18:09:35 -0700762
Scott Maine4d8f1b2012-06-21 18:03:05 -0700763 // We're scheduled for an update and the update request came from this method's setTimeout
764 if (updateScrollbars && !updateFromResize) {
765 reInitScrollbars();
766 updateScrollbars = false;
767 } else {
768 updateScrollbars = true;
769 updateFromResize = false;
770 setTimeout('delayedReInitScrollbars()',delay);
771 }
772}
773
774/* Re-initialize the scrollbars to account for changed nav size. */
775function reInitScrollbars() {
776 var pane = $(".scroll-pane").each(function(){
777 var api = $(this).data('jsp');
778 if (!api) { setTimeout(reInitScrollbars,300); return;}
779 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700780 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700781 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
782}
783
784
785/* Resize the height of the nav panels in the reference,
786 * and save the new size to a cookie */
787function saveNavPanels() {
788 var basePath = getBaseUri(location.pathname);
789 var section = basePath.substring(1,basePath.indexOf("/",1));
790 writeCookie("height", resizePackagesNav.css("height"), section, null);
791}
792
793
794
795function restoreHeight(packageHeight) {
796 $("#resize-packages-nav").height(packageHeight);
797 $("#packages-nav").height(packageHeight);
798 // var classesHeight = navHeight - packageHeight;
799 // $("#classes-nav").css({height:classesHeight});
800 // $("#classes-nav .jspContainer").css({height:classesHeight});
801}
802
803
804
805/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
806
807
808
809
810
Scott Main3b90aff2013-08-01 18:09:35 -0700811/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700812 This is called when the page finished loading. */
813function scrollIntoView(nav) {
814 var $nav = $("#"+nav);
815 var element = $nav.jScrollPane({/* ...settings... */});
816 var api = element.data('jsp');
817
818 if ($nav.is(':visible')) {
819 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700820 if ($selected.length == 0) {
821 // If no selected item found, exit
822 return;
823 }
Scott Main52dd2062013-08-15 12:22:28 -0700824 // get the selected item's offset from its container nav by measuring the item's offset
825 // relative to the document then subtract the container nav's offset relative to the document
826 var selectedOffset = $selected.offset().top - $nav.offset().top;
827 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
828 // if it's more than 80% down the nav
829 // scroll the item up by an amount equal to 80% the container nav's height
830 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700831 }
832 }
833}
834
835
836
837
838
839
840/* Show popup dialogs */
841function showDialog(id) {
842 $dialog = $("#"+id);
843 $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>');
844 $dialog.wrapInner('<div/>');
845 $dialog.removeClass("hide");
846}
847
848
849
850
851
852/* ######### COOKIES! ########## */
853
854function readCookie(cookie) {
855 var myCookie = cookie_namespace+"_"+cookie+"=";
856 if (document.cookie) {
857 var index = document.cookie.indexOf(myCookie);
858 if (index != -1) {
859 var valStart = index + myCookie.length;
860 var valEnd = document.cookie.indexOf(";", valStart);
861 if (valEnd == -1) {
862 valEnd = document.cookie.length;
863 }
864 var val = document.cookie.substring(valStart, valEnd);
865 return val;
866 }
867 }
868 return 0;
869}
870
871function writeCookie(cookie, val, section, expiration) {
872 if (val==undefined) return;
873 section = section == null ? "_" : "_"+section+"_";
874 if (expiration == null) {
875 var date = new Date();
876 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
877 expiration = date.toGMTString();
878 }
Scott Main3b90aff2013-08-01 18:09:35 -0700879 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700880 + "; expires=" + expiration+"; path=/";
881 document.cookie = cookieValue;
882}
883
884/* ######### END COOKIES! ########## */
885
886
887
888
Dirk Doughertyc3921652014-05-13 16:55:26 -0700889var stickyTop;
890/* Sets the vertical scoll position at which the sticky bar should appear.
891 This method is called to reset the position when search results appear or hide */
892function setStickyTop() {
893 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
894}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700895
896
Dirk Doughertyc3921652014-05-13 16:55:26 -0700897/*
898 * Displays sticky nav bar on pages when dac header scrolls out of view
899 */
900(function() {
901 $(document).ready(function() {
902
903 setStickyTop();
904 var sticky = false;
905 var hiding = false;
906 var $stickyEl = $('#sticky-header');
907 var $menuEl = $('.menu-container');
908
909 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
910
911 $(window).scroll(function() {
912 // Exit if there's no sidenav
913 if ($('#side-nav').length == 0) return;
914 // Exit if the mouse target is a DIV, because that means the event is coming
915 // from a scrollable div and so there's no need to make adjustments to our layout
916 if (event.target.nodeName == "DIV") {
917 return;
918 }
919
920
921 var top = $(window).scrollTop();
922 // we set the navbar fixed when the scroll position is beyond the height of the site header...
923 var shouldBeSticky = top >= stickyTop;
924 // ... except if the document content is shorter than the sidenav height.
925 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
926 if ($("#doc-col").height() < $("#side-nav").height()) {
927 shouldBeSticky = false;
928 }
929
930 // Don't continue if the header is sufficently far away
931 // (to avoid intensive resizing that slows scrolling)
932 if (sticky && shouldBeSticky) {
933 return;
934 }
935
936 // Account for horizontal scroll
937 var scrollLeft = $(window).scrollLeft();
938 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
939 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
940 updateSideNavPosition();
941 prevScrollLeft = scrollLeft;
942 }
943
944 // If sticky header visible and position is now near top, hide sticky
945 if (sticky && !shouldBeSticky) {
946 sticky = false;
947 hiding = true;
948 // make the sidenav static again
949 $('#devdoc-nav')
950 .removeClass('fixed')
951 .css({'width':'auto','margin':''})
952 .prependTo('#side-nav');
953 // delay hide the sticky
954 $menuEl.removeClass('sticky-menu');
955 $stickyEl.fadeOut(250);
956 hiding = false;
957
958 // update the sidenaav position for side scrolling
959 updateSideNavPosition();
960 } else if (!sticky && shouldBeSticky) {
961 sticky = true;
962 $stickyEl.fadeIn(10);
963 $menuEl.addClass('sticky-menu');
964
965 // make the sidenav fixed
966 var width = $('#devdoc-nav').width();
967 $('#devdoc-nav')
968 .addClass('fixed')
969 .css({'width':width+'px'})
970 .prependTo('#body-content');
971
972 // update the sidenaav position for side scrolling
973 updateSideNavPosition();
974
975 } else if (hiding && top < 15) {
976 $menuEl.removeClass('sticky-menu');
977 $stickyEl.hide();
978 hiding = false;
979 }
980
981 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
982 });
983
984 // Stack hover states
985 $('.section-card-menu').each(function(index, el) {
986 var height = $(el).height();
987 $(el).css({height:height+'px', position:'relative'});
988 var $cardInfo = $(el).find('.card-info');
989
990 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
991 });
992
993 resizeNav(); // must resize once loading is finished
994 });
995
996})();
997
Scott Maine4d8f1b2012-06-21 18:03:05 -0700998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
Scott Maind7026f72013-06-17 15:08:49 -07001011/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001012
1013
1014
1015
1016
1017function toggle(obj, slide) {
1018 var ul = $("ul:first", obj);
1019 var li = ul.parent();
1020 if (li.hasClass("closed")) {
1021 if (slide) {
1022 ul.slideDown("fast");
1023 } else {
1024 ul.show();
1025 }
1026 li.removeClass("closed");
1027 li.addClass("open");
1028 $(".toggle-img", li).attr("title", "hide pages");
1029 } else {
1030 ul.slideUp("fast");
1031 li.removeClass("open");
1032 li.addClass("closed");
1033 $(".toggle-img", li).attr("title", "show pages");
1034 }
1035}
1036
1037
Scott Maine4d8f1b2012-06-21 18:03:05 -07001038function buildToggleLists() {
1039 $(".toggle-list").each(
1040 function(i) {
1041 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1042 $(this).addClass("closed");
1043 });
1044}
1045
1046
1047
Scott Maind7026f72013-06-17 15:08:49 -07001048function hideNestedItems(list, toggle) {
1049 $list = $(list);
1050 // hide nested lists
1051 if($list.hasClass('showing')) {
1052 $("li ol", $list).hide('fast');
1053 $list.removeClass('showing');
1054 // show nested lists
1055 } else {
1056 $("li ol", $list).show('fast');
1057 $list.addClass('showing');
1058 }
1059 $(".more,.less",$(toggle)).toggle();
1060}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089/* REFERENCE NAV SWAP */
1090
1091
1092function getNavPref() {
1093 var v = readCookie('reference_nav');
1094 if (v != NAV_PREF_TREE) {
1095 v = NAV_PREF_PANELS;
1096 }
1097 return v;
1098}
1099
1100function chooseDefaultNav() {
1101 nav_pref = getNavPref();
1102 if (nav_pref == NAV_PREF_TREE) {
1103 $("#nav-panels").toggle();
1104 $("#panel-link").toggle();
1105 $("#nav-tree").toggle();
1106 $("#tree-link").toggle();
1107 }
1108}
1109
1110function swapNav() {
1111 if (nav_pref == NAV_PREF_TREE) {
1112 nav_pref = NAV_PREF_PANELS;
1113 } else {
1114 nav_pref = NAV_PREF_TREE;
1115 init_default_navtree(toRoot);
1116 }
1117 var date = new Date();
1118 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1119 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1120
1121 $("#nav-panels").toggle();
1122 $("#panel-link").toggle();
1123 $("#nav-tree").toggle();
1124 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001125
Scott Maine4d8f1b2012-06-21 18:03:05 -07001126 resizeNav();
1127
1128 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1129 $("#nav-tree .jspContainer:visible")
1130 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1131 // Another nasty hack to make the scrollbar appear now that we have height
1132 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001133
Scott Maine4d8f1b2012-06-21 18:03:05 -07001134 if ($("#nav-tree").is(':visible')) {
1135 scrollIntoView("nav-tree");
1136 } else {
1137 scrollIntoView("packages-nav");
1138 scrollIntoView("classes-nav");
1139 }
1140}
1141
1142
1143
Scott Mainf5089842012-08-14 16:31:07 -07001144/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001145/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001146/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001147
1148function getBaseUri(uri) {
1149 var intlUrl = (uri.substring(0,6) == "/intl/");
1150 if (intlUrl) {
1151 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1152 base = base.substring(base.indexOf('/')+1, base.length);
1153 //alert("intl, returning base url: /" + base);
1154 return ("/" + base);
1155 } else {
1156 //alert("not intl, returning uri as found.");
1157 return uri;
1158 }
1159}
1160
1161function requestAppendHL(uri) {
1162//append "?hl=<lang> to an outgoing request (such as to blog)
1163 var lang = getLangPref();
1164 if (lang) {
1165 var q = 'hl=' + lang;
1166 uri += '?' + q;
1167 window.location = uri;
1168 return false;
1169 } else {
1170 return true;
1171 }
1172}
1173
1174
Scott Maine4d8f1b2012-06-21 18:03:05 -07001175function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001176 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1177 $links.each(function(i){ // for each link with a translation
1178 var $link = $(this);
1179 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1180 // put the desired language from the attribute as the text
1181 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001182 }
Scott Main6eb95f12012-10-02 17:12:23 -07001183 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001184}
1185
Scott Main015d6162013-01-29 09:01:52 -08001186function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001187 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001188 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001189 // keep this for 50 years
1190 //alert("expires: " + expires)
1191 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001192
1193 // ####### TODO: Remove this condition once we're stable on devsite #######
1194 // This condition is only needed if we still need to support legacy GAE server
1195 if (devsite) {
1196 // Switch language when on Devsite server
1197 if (submit) {
1198 $("#setlang").submit();
1199 }
1200 } else {
1201 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001202 if (submit) {
1203 window.location = getBaseUri(location.pathname);
1204 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001205 }
1206}
1207
1208function loadLangPref() {
1209 var lang = readCookie("pref_lang");
1210 if (lang != 0) {
1211 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1212 }
1213}
1214
1215function getLangPref() {
1216 var lang = $("#language").find(":selected").attr("value");
1217 if (!lang) {
1218 lang = readCookie("pref_lang");
1219 }
1220 return (lang != 0) ? lang : 'en';
1221}
1222
1223/* ########## END LOCALIZATION ############ */
1224
1225
1226
1227
1228
1229
1230/* Used to hide and reveal supplemental content, such as long code samples.
1231 See the companion CSS in android-developer-docs.css */
1232function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001233 var div = $(obj).closest(".toggle-content");
1234 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001235 if (div.hasClass("closed")) { // if it's closed, open it
1236 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001237 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001238 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001239 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001240 + "assets/images/triangle-opened.png");
1241 } else { // if it's open, close it
1242 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001243 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001244 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001245 div.find(".toggle-content").removeClass("open").addClass("closed")
1246 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001247 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001248 + "assets/images/triangle-closed.png");
1249 });
1250 }
1251 return false;
1252}
Scott Mainf5089842012-08-14 16:31:07 -07001253
1254
Scott Maindb3678b2012-10-23 14:13:41 -07001255/* New version of expandable content */
1256function toggleExpandable(link,id) {
1257 if($(id).is(':visible')) {
1258 $(id).slideUp();
1259 $(link).removeClass('expanded');
1260 } else {
1261 $(id).slideDown();
1262 $(link).addClass('expanded');
1263 }
1264}
1265
1266function hideExpandable(ids) {
1267 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001268 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001269}
1270
Scott Mainf5089842012-08-14 16:31:07 -07001271
1272
1273
1274
Scott Main3b90aff2013-08-01 18:09:35 -07001275/*
Scott Mainf5089842012-08-14 16:31:07 -07001276 * Slideshow 1.0
1277 * Used on /index.html and /develop/index.html for carousel
1278 *
1279 * Sample usage:
1280 * HTML -
1281 * <div class="slideshow-container">
1282 * <a href="" class="slideshow-prev">Prev</a>
1283 * <a href="" class="slideshow-next">Next</a>
1284 * <ul>
1285 * <li class="item"><img src="images/marquee1.jpg"></li>
1286 * <li class="item"><img src="images/marquee2.jpg"></li>
1287 * <li class="item"><img src="images/marquee3.jpg"></li>
1288 * <li class="item"><img src="images/marquee4.jpg"></li>
1289 * </ul>
1290 * </div>
1291 *
1292 * <script type="text/javascript">
1293 * $('.slideshow-container').dacSlideshow({
1294 * auto: true,
1295 * btnPrev: '.slideshow-prev',
1296 * btnNext: '.slideshow-next'
1297 * });
1298 * </script>
1299 *
1300 * Options:
1301 * btnPrev: optional identifier for previous button
1302 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001303 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001304 * auto: whether or not to auto-proceed
1305 * speed: animation speed
1306 * autoTime: time between auto-rotation
1307 * easing: easing function for transition
1308 * start: item to select by default
1309 * scroll: direction to scroll in
1310 * pagination: whether or not to include dotted pagination
1311 *
1312 */
1313
1314 (function($) {
1315 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001316
Scott Mainf5089842012-08-14 16:31:07 -07001317 //Options - see above
1318 o = $.extend({
1319 btnPrev: null,
1320 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001321 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001322 auto: true,
1323 speed: 500,
1324 autoTime: 12000,
1325 easing: null,
1326 start: 0,
1327 scroll: 1,
1328 pagination: true
1329
1330 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001331
1332 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001333 return this.each(function() {
1334
1335 var running = false;
1336 var animCss = o.vertical ? "top" : "left";
1337 var sizeCss = o.vertical ? "height" : "width";
1338 var div = $(this);
1339 var ul = $("ul", div);
1340 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001341 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001342 var timer = null;
1343
1344 var li = $("li", ul);
1345 var itemLength = li.size();
1346 var curr = o.start;
1347
1348 li.css({float: o.vertical ? "none" : "left"});
1349 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1350 div.css({position: "relative", "z-index": "2", left: "0px"});
1351
1352 var liSize = o.vertical ? height(li) : width(li);
1353 var ulSize = liSize * itemLength;
1354 var divSize = liSize;
1355
1356 li.css({width: li.width(), height: li.height()});
1357 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1358
1359 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001360
Scott Mainf5089842012-08-14 16:31:07 -07001361 //Pagination
1362 if (o.pagination) {
1363 var pagination = $("<div class='pagination'></div>");
1364 var pag_ul = $("<ul></ul>");
1365 if (tl > 1) {
1366 for (var i=0;i<tl;i++) {
1367 var li = $("<li>"+i+"</li>");
1368 pag_ul.append(li);
1369 if (i==o.start) li.addClass('active');
1370 li.click(function() {
1371 go(parseInt($(this).text()));
1372 })
1373 }
1374 pagination.append(pag_ul);
1375 div.append(pagination);
1376 }
1377 }
Scott Main3b90aff2013-08-01 18:09:35 -07001378
Scott Mainf5089842012-08-14 16:31:07 -07001379 //Previous button
1380 if(o.btnPrev)
1381 $(o.btnPrev).click(function(e) {
1382 e.preventDefault();
1383 return go(curr-o.scroll);
1384 });
1385
1386 //Next button
1387 if(o.btnNext)
1388 $(o.btnNext).click(function(e) {
1389 e.preventDefault();
1390 return go(curr+o.scroll);
1391 });
Scott Maineb410352013-01-14 19:03:40 -08001392
1393 //Pause button
1394 if(o.btnPause)
1395 $(o.btnPause).click(function(e) {
1396 e.preventDefault();
1397 if ($(this).hasClass('paused')) {
1398 startRotateTimer();
1399 } else {
1400 pauseRotateTimer();
1401 }
1402 });
Scott Main3b90aff2013-08-01 18:09:35 -07001403
Scott Mainf5089842012-08-14 16:31:07 -07001404 //Auto rotation
1405 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001406
Scott Mainf5089842012-08-14 16:31:07 -07001407 function startRotateTimer() {
1408 clearInterval(timer);
1409 timer = setInterval(function() {
1410 if (curr == tl-1) {
1411 go(0);
1412 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001413 go(curr+o.scroll);
1414 }
Scott Mainf5089842012-08-14 16:31:07 -07001415 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001416 $(o.btnPause).removeClass('paused');
1417 }
1418
1419 function pauseRotateTimer() {
1420 clearInterval(timer);
1421 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001422 }
1423
1424 //Go to an item
1425 function go(to) {
1426 if(!running) {
1427
1428 if(to<0) {
1429 to = itemLength-1;
1430 } else if (to>itemLength-1) {
1431 to = 0;
1432 }
1433 curr = to;
1434
1435 running = true;
1436
1437 ul.animate(
1438 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1439 function() {
1440 running = false;
1441 }
1442 );
1443
1444 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1445 $( (curr-o.scroll<0 && o.btnPrev)
1446 ||
1447 (curr+o.scroll > itemLength && o.btnNext)
1448 ||
1449 []
1450 ).addClass("disabled");
1451
Scott Main3b90aff2013-08-01 18:09:35 -07001452
Scott Mainf5089842012-08-14 16:31:07 -07001453 var nav_items = $('li', pagination);
1454 nav_items.removeClass('active');
1455 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001456
Scott Mainf5089842012-08-14 16:31:07 -07001457
1458 }
1459 if(o.auto) startRotateTimer();
1460 return false;
1461 };
1462 });
1463 };
1464
1465 function css(el, prop) {
1466 return parseInt($.css(el[0], prop)) || 0;
1467 };
1468 function width(el) {
1469 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1470 };
1471 function height(el) {
1472 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1473 };
1474
1475 })(jQuery);
1476
1477
Scott Main3b90aff2013-08-01 18:09:35 -07001478/*
Scott Mainf5089842012-08-14 16:31:07 -07001479 * dacSlideshow 1.0
1480 * Used on develop/index.html for side-sliding tabs
1481 *
1482 * Sample usage:
1483 * HTML -
1484 * <div class="slideshow-container">
1485 * <a href="" class="slideshow-prev">Prev</a>
1486 * <a href="" class="slideshow-next">Next</a>
1487 * <ul>
1488 * <li class="item"><img src="images/marquee1.jpg"></li>
1489 * <li class="item"><img src="images/marquee2.jpg"></li>
1490 * <li class="item"><img src="images/marquee3.jpg"></li>
1491 * <li class="item"><img src="images/marquee4.jpg"></li>
1492 * </ul>
1493 * </div>
1494 *
1495 * <script type="text/javascript">
1496 * $('.slideshow-container').dacSlideshow({
1497 * auto: true,
1498 * btnPrev: '.slideshow-prev',
1499 * btnNext: '.slideshow-next'
1500 * });
1501 * </script>
1502 *
1503 * Options:
1504 * btnPrev: optional identifier for previous button
1505 * btnNext: optional identifier for next button
1506 * auto: whether or not to auto-proceed
1507 * speed: animation speed
1508 * autoTime: time between auto-rotation
1509 * easing: easing function for transition
1510 * start: item to select by default
1511 * scroll: direction to scroll in
1512 * pagination: whether or not to include dotted pagination
1513 *
1514 */
1515 (function($) {
1516 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001517
Scott Mainf5089842012-08-14 16:31:07 -07001518 //Options - see above
1519 o = $.extend({
1520 speed : 250,
1521 easing: null,
1522 nav_id: null,
1523 frame_id: null
1524 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001525
1526 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001527 return this.each(function() {
1528
1529 var curr = 0;
1530 var running = false;
1531 var animCss = "margin-left";
1532 var sizeCss = "width";
1533 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001534
Scott Mainf5089842012-08-14 16:31:07 -07001535 var nav = $(o.nav_id, div);
1536 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001537 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001538 var frame = div.find(o.frame_id);
1539 var content_width = $(frame).find('ul').width();
1540 //Buttons
1541 $(nav_li).click(function(e) {
1542 go($(nav_li).index($(this)));
1543 })
Scott Main3b90aff2013-08-01 18:09:35 -07001544
Scott Mainf5089842012-08-14 16:31:07 -07001545 //Go to an item
1546 function go(to) {
1547 if(!running) {
1548 curr = to;
1549 running = true;
1550
1551 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1552 function() {
1553 running = false;
1554 }
1555 );
1556
Scott Main3b90aff2013-08-01 18:09:35 -07001557
Scott Mainf5089842012-08-14 16:31:07 -07001558 nav_li.removeClass('active');
1559 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001560
Scott Mainf5089842012-08-14 16:31:07 -07001561
1562 }
1563 return false;
1564 };
1565 });
1566 };
1567
1568 function css(el, prop) {
1569 return parseInt($.css(el[0], prop)) || 0;
1570 };
1571 function width(el) {
1572 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1573 };
1574 function height(el) {
1575 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1576 };
1577
1578 })(jQuery);
1579
1580
1581
1582
1583
1584/* ######################################################## */
1585/* ################ SEARCH SUGGESTIONS ################## */
1586/* ######################################################## */
1587
1588
Scott Main7e447ed2013-02-19 17:22:37 -08001589
Scott Main0e76e7e2013-03-12 10:24:07 -07001590var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1591var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1592
Scott Mainf5089842012-08-14 16:31:07 -07001593var gMatches = new Array();
1594var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001595var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001596var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1597var gListLength = 0;
1598
1599
1600var gGoogleMatches = new Array();
1601var ROW_COUNT_GOOGLE = 15; // max number of results in list
1602var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001603
Scott Main0e76e7e2013-03-12 10:24:07 -07001604var gDocsMatches = new Array();
1605var ROW_COUNT_DOCS = 100; // max number of results in list
1606var gDocsListLength = 0;
1607
Scott Mainde295272013-03-25 15:48:35 -07001608function onSuggestionClick(link) {
1609 // When user clicks a suggested document, track it
1610 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1611 'from: ' + $("#search_autocomplete").val()]);
1612}
1613
Scott Mainf5089842012-08-14 16:31:07 -07001614function set_item_selected($li, selected)
1615{
1616 if (selected) {
1617 $li.attr('class','jd-autocomplete jd-selected');
1618 } else {
1619 $li.attr('class','jd-autocomplete');
1620 }
1621}
1622
1623function set_item_values(toroot, $li, match)
1624{
1625 var $link = $('a',$li);
1626 $link.html(match.__hilabel || match.label);
1627 $link.attr('href',toroot + match.link);
1628}
1629
Scott Main719acb42013-12-05 16:05:09 -08001630function set_item_values_jd(toroot, $li, match)
1631{
1632 var $link = $('a',$li);
1633 $link.html(match.title);
1634 $link.attr('href',toroot + match.url);
1635}
1636
Scott Main0e76e7e2013-03-12 10:24:07 -07001637function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001638 var $li = $("<li class='jd-autocomplete'></li>");
1639 $list.append($li);
1640
1641 $li.mousedown(function() {
1642 window.location = this.firstChild.getAttribute("href");
1643 });
1644 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001645 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001646 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001647 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1648 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001649 });
Scott Mainde295272013-03-25 15:48:35 -07001650 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001651 $li.attr('class','show-item');
1652 return $li;
1653}
1654
Scott Mainf5089842012-08-14 16:31:07 -07001655function sync_selection_table(toroot)
1656{
Scott Mainf5089842012-08-14 16:31:07 -07001657 var $li; //list item jquery object
1658 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001659
Scott Main0e76e7e2013-03-12 10:24:07 -07001660 // if there are NO results at all, hide all columns
1661 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1662 $('.suggest-card').hide(300);
1663 return;
1664 }
1665
1666 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001667 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001668 // reveal suggestion list
1669 $('.suggest-card.dummy').show();
1670 $('.suggest-card.reference').show();
1671 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001672
Scott Main0e76e7e2013-03-12 10:24:07 -07001673 // reset the lists
1674 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001675
Scott Main0e76e7e2013-03-12 10:24:07 -07001676 // ########### ANDROID RESULTS #############
1677 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001678
Scott Main0e76e7e2013-03-12 10:24:07 -07001679 // determine android results to show
1680 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1681 gMatches.length : ROW_COUNT_FRAMEWORK;
1682 for (i=0; i<gListLength; i++) {
1683 var $li = new_suggestion($(".suggest-card.reference ul"));
1684 set_item_values(toroot, $li, gMatches[i]);
1685 set_item_selected($li, i == gSelectedIndex);
1686 }
1687 }
Scott Main7e447ed2013-02-19 17:22:37 -08001688
Scott Main0e76e7e2013-03-12 10:24:07 -07001689 // ########### GOOGLE RESULTS #############
1690 if (gGoogleMatches.length > 0) {
1691 // show header for list
1692 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001693
Scott Main0e76e7e2013-03-12 10:24:07 -07001694 // determine google results to show
1695 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1696 for (i=0; i<gGoogleListLength; i++) {
1697 var $li = new_suggestion($(".suggest-card.reference ul"));
1698 set_item_values(toroot, $li, gGoogleMatches[i]);
1699 set_item_selected($li, i == gSelectedIndex);
1700 }
1701 }
Scott Mainf5089842012-08-14 16:31:07 -07001702 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001703 $('.suggest-card.reference').hide();
1704 $('.suggest-card.dummy').hide();
1705 }
1706
1707 // ########### JD DOC RESULTS #############
1708 if (gDocsMatches.length > 0) {
1709 // reset the lists
1710 $(".search_filtered_wrapper.docs li").remove();
1711
1712 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001713 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1714 // The order must match the reverse order that each section appears as a card in
1715 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001716 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1717 for (i=0; i<gDocsListLength; i++) {
1718 var sugg = gDocsMatches[i];
1719 var $li;
1720 if (sugg.type == "design") {
1721 $li = new_suggestion($(".suggest-card.design ul"));
1722 } else
1723 if (sugg.type == "distribute") {
1724 $li = new_suggestion($(".suggest-card.distribute ul"));
1725 } else
Scott Main719acb42013-12-05 16:05:09 -08001726 if (sugg.type == "samples") {
1727 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1728 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001729 if (sugg.type == "training") {
1730 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1731 } else
Scott Main719acb42013-12-05 16:05:09 -08001732 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001733 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1734 } else {
1735 continue;
1736 }
1737
Scott Main719acb42013-12-05 16:05:09 -08001738 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001739 set_item_selected($li, i == gSelectedIndex);
1740 }
1741
1742 // add heading and show or hide card
1743 if ($(".suggest-card.design li").length > 0) {
1744 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1745 $(".suggest-card.design").show(300);
1746 } else {
1747 $('.suggest-card.design').hide(300);
1748 }
1749 if ($(".suggest-card.distribute li").length > 0) {
1750 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1751 $(".suggest-card.distribute").show(300);
1752 } else {
1753 $('.suggest-card.distribute').hide(300);
1754 }
1755 if ($(".child-card.guides li").length > 0) {
1756 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1757 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1758 }
1759 if ($(".child-card.training li").length > 0) {
1760 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1761 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1762 }
Scott Main719acb42013-12-05 16:05:09 -08001763 if ($(".child-card.samples li").length > 0) {
1764 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1765 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1766 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001767
1768 if ($(".suggest-card.develop li").length > 0) {
1769 $(".suggest-card.develop").show(300);
1770 } else {
1771 $('.suggest-card.develop').hide(300);
1772 }
1773
1774 } else {
1775 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001776 }
1777}
1778
Scott Main0e76e7e2013-03-12 10:24:07 -07001779/** Called by the search input's onkeydown and onkeyup events.
1780 * Handles navigation with keyboard arrows, Enter key to invoke search,
1781 * otherwise invokes search suggestions on key-up event.
1782 * @param e The JS event
1783 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001784 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001785 * @returns True if the event should bubble up
1786 */
Scott Mainf5089842012-08-14 16:31:07 -07001787function search_changed(e, kd, toroot)
1788{
Scott Main719acb42013-12-05 16:05:09 -08001789 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001790 var search = document.getElementById("search_autocomplete");
1791 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001792 // get the ul hosting the currently selected item
1793 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1794 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1795 var $selectedUl = $columns[gSelectedColumn];
1796
Scott Mainf5089842012-08-14 16:31:07 -07001797 // show/hide the close button
1798 if (text != '') {
1799 $(".search .close").removeClass("hide");
1800 } else {
1801 $(".search .close").addClass("hide");
1802 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001803 // 27 = esc
1804 if (e.keyCode == 27) {
1805 // close all search results
1806 if (kd) $('.search .close').trigger('click');
1807 return true;
1808 }
Scott Mainf5089842012-08-14 16:31:07 -07001809 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001810 else if (e.keyCode == 13) {
1811 if (gSelectedIndex < 0) {
1812 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001813 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1814 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001815 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001816 return true;
1817 } else {
1818 // otherwise, results are already showing, so allow ajax to auto refresh the results
1819 // and ignore this Enter press to avoid the reload.
1820 return false;
1821 }
1822 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001823 // click the link corresponding to selected item
1824 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001825 return false;
1826 }
1827 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001828 // Stop here if Google results are showing
1829 else if ($("#searchResults").is(":visible")) {
1830 return true;
1831 }
1832 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001833 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001834 // if the next item is a header, skip it
1835 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001836 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001837 }
1838 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001839 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001840 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001841 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1842 // If user reaches top, reset selected column
1843 if (gSelectedIndex < 0) {
1844 gSelectedColumn = -1;
1845 }
Scott Mainf5089842012-08-14 16:31:07 -07001846 }
1847 return false;
1848 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001849 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001850 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001851 // if the next item is a header, skip it
1852 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001853 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001854 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001855 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1856 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1857 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001858 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001859 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001860 }
1861 return false;
1862 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001863 // Consider left/right arrow navigation
1864 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1865 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1866 // 37 LEFT ARROW
1867 // go left only if current column is not left-most column (last column)
1868 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1869 $('li', $selectedUl).removeClass('jd-selected');
1870 gSelectedColumn++;
1871 $selectedUl = $columns[gSelectedColumn];
1872 // keep or reset the selected item to last item as appropriate
1873 gSelectedIndex = gSelectedIndex >
1874 $("li", $selectedUl).length-1 ?
1875 $("li", $selectedUl).length-1 : gSelectedIndex;
1876 // if the corresponding item is a header, move down
1877 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1878 gSelectedIndex++;
1879 }
Scott Main3b90aff2013-08-01 18:09:35 -07001880 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001881 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1882 return false;
1883 }
1884 // 39 RIGHT ARROW
1885 // go right only if current column is not the right-most column (first column)
1886 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1887 $('li', $selectedUl).removeClass('jd-selected');
1888 gSelectedColumn--;
1889 $selectedUl = $columns[gSelectedColumn];
1890 // keep or reset the selected item to last item as appropriate
1891 gSelectedIndex = gSelectedIndex >
1892 $("li", $selectedUl).length-1 ?
1893 $("li", $selectedUl).length-1 : gSelectedIndex;
1894 // if the corresponding item is a header, move down
1895 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1896 gSelectedIndex++;
1897 }
Scott Main3b90aff2013-08-01 18:09:35 -07001898 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001899 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1900 return false;
1901 }
1902 }
1903
Scott Main719acb42013-12-05 16:05:09 -08001904 // if key-up event and not arrow down/up/left/right,
1905 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001906 else if (!kd && (e.keyCode != 40)
1907 && (e.keyCode != 38)
1908 && (e.keyCode != 37)
1909 && (e.keyCode != 39)) {
1910 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001911 gMatches = new Array();
1912 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001913 gGoogleMatches = new Array();
1914 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001915 gDocsMatches = new Array();
1916 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001917
1918 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001919 for (var i=0; i<DATA.length; i++) {
1920 var s = DATA[i];
1921 if (text.length != 0 &&
1922 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1923 gMatches[matchedCount] = s;
1924 matchedCount++;
1925 }
1926 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001927 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001928 for (var i=0; i<gMatches.length; i++) {
1929 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001930 }
1931
1932
1933 // Search for Google matches
1934 for (var i=0; i<GOOGLE_DATA.length; i++) {
1935 var s = GOOGLE_DATA[i];
1936 if (text.length != 0 &&
1937 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1938 gGoogleMatches[matchedCountGoogle] = s;
1939 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001940 }
1941 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001942 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001943 for (var i=0; i<gGoogleMatches.length; i++) {
1944 var s = gGoogleMatches[i];
1945 }
1946
Scott Mainf5089842012-08-14 16:31:07 -07001947 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001948
1949
1950
Scott Main719acb42013-12-05 16:05:09 -08001951 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07001952 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08001953 // Regex to match only the beginning of a word
1954 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1955
1956
1957 // Search for Training classes
1958 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001959 // current search comparison, with counters for tag and title,
1960 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08001961 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07001962 s.matched_tag = 0;
1963 s.matched_title = 0;
1964 var matched = false;
1965
1966 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08001967 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001968 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08001969 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001970 matched = true;
1971 s.matched_tag = j + 1; // add 1 to index position
1972 }
1973 }
Scott Main719acb42013-12-05 16:05:09 -08001974 // Don't consider doc title for lessons (only for class landing pages),
1975 // unless the lesson has a tag that already matches
1976 if ((s.lang == currentLang) &&
1977 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001978 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08001979 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001980 matched = true;
1981 s.matched_title = 1;
1982 }
1983 }
1984 if (matched) {
1985 gDocsMatches[matchedCountDocs] = s;
1986 matchedCountDocs++;
1987 }
1988 }
Scott Main719acb42013-12-05 16:05:09 -08001989
1990
1991 // Search for API Guides
1992 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
1993 // current search comparison, with counters for tag and title,
1994 // used later to improve ranking
1995 var s = GUIDE_RESOURCES[i];
1996 s.matched_tag = 0;
1997 s.matched_title = 0;
1998 var matched = false;
1999
2000 // Check if query matches any tags; work backwards toward 1 to assist ranking
2001 for (var j = s.keywords.length - 1; j >= 0; j--) {
2002 // it matches a tag
2003 if (s.keywords[j].toLowerCase().match(textRegex)) {
2004 matched = true;
2005 s.matched_tag = j + 1; // add 1 to index position
2006 }
2007 }
2008 // Check if query matches the doc title, but only for current language
2009 if (s.lang == currentLang) {
2010 // if query matches the doc title
2011 if (s.title.toLowerCase().match(textRegex)) {
2012 matched = true;
2013 s.matched_title = 1;
2014 }
2015 }
2016 if (matched) {
2017 gDocsMatches[matchedCountDocs] = s;
2018 matchedCountDocs++;
2019 }
2020 }
2021
2022
2023 // Search for Tools Guides
2024 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2025 // current search comparison, with counters for tag and title,
2026 // used later to improve ranking
2027 var s = TOOLS_RESOURCES[i];
2028 s.matched_tag = 0;
2029 s.matched_title = 0;
2030 var matched = false;
2031
2032 // Check if query matches any tags; work backwards toward 1 to assist ranking
2033 for (var j = s.keywords.length - 1; j >= 0; j--) {
2034 // it matches a tag
2035 if (s.keywords[j].toLowerCase().match(textRegex)) {
2036 matched = true;
2037 s.matched_tag = j + 1; // add 1 to index position
2038 }
2039 }
2040 // Check if query matches the doc title, but only for current language
2041 if (s.lang == currentLang) {
2042 // if query matches the doc title
2043 if (s.title.toLowerCase().match(textRegex)) {
2044 matched = true;
2045 s.matched_title = 1;
2046 }
2047 }
2048 if (matched) {
2049 gDocsMatches[matchedCountDocs] = s;
2050 matchedCountDocs++;
2051 }
2052 }
2053
2054
2055 // Search for About docs
2056 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2057 // current search comparison, with counters for tag and title,
2058 // used later to improve ranking
2059 var s = ABOUT_RESOURCES[i];
2060 s.matched_tag = 0;
2061 s.matched_title = 0;
2062 var matched = false;
2063
2064 // Check if query matches any tags; work backwards toward 1 to assist ranking
2065 for (var j = s.keywords.length - 1; j >= 0; j--) {
2066 // it matches a tag
2067 if (s.keywords[j].toLowerCase().match(textRegex)) {
2068 matched = true;
2069 s.matched_tag = j + 1; // add 1 to index position
2070 }
2071 }
2072 // Check if query matches the doc title, but only for current language
2073 if (s.lang == currentLang) {
2074 // if query matches the doc title
2075 if (s.title.toLowerCase().match(textRegex)) {
2076 matched = true;
2077 s.matched_title = 1;
2078 }
2079 }
2080 if (matched) {
2081 gDocsMatches[matchedCountDocs] = s;
2082 matchedCountDocs++;
2083 }
2084 }
2085
2086
2087 // Search for Design guides
2088 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2089 // current search comparison, with counters for tag and title,
2090 // used later to improve ranking
2091 var s = DESIGN_RESOURCES[i];
2092 s.matched_tag = 0;
2093 s.matched_title = 0;
2094 var matched = false;
2095
2096 // Check if query matches any tags; work backwards toward 1 to assist ranking
2097 for (var j = s.keywords.length - 1; j >= 0; j--) {
2098 // it matches a tag
2099 if (s.keywords[j].toLowerCase().match(textRegex)) {
2100 matched = true;
2101 s.matched_tag = j + 1; // add 1 to index position
2102 }
2103 }
2104 // Check if query matches the doc title, but only for current language
2105 if (s.lang == currentLang) {
2106 // if query matches the doc title
2107 if (s.title.toLowerCase().match(textRegex)) {
2108 matched = true;
2109 s.matched_title = 1;
2110 }
2111 }
2112 if (matched) {
2113 gDocsMatches[matchedCountDocs] = s;
2114 matchedCountDocs++;
2115 }
2116 }
2117
2118
2119 // Search for Distribute guides
2120 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2121 // current search comparison, with counters for tag and title,
2122 // used later to improve ranking
2123 var s = DISTRIBUTE_RESOURCES[i];
2124 s.matched_tag = 0;
2125 s.matched_title = 0;
2126 var matched = false;
2127
2128 // Check if query matches any tags; work backwards toward 1 to assist ranking
2129 for (var j = s.keywords.length - 1; j >= 0; j--) {
2130 // it matches a tag
2131 if (s.keywords[j].toLowerCase().match(textRegex)) {
2132 matched = true;
2133 s.matched_tag = j + 1; // add 1 to index position
2134 }
2135 }
2136 // Check if query matches the doc title, but only for current language
2137 if (s.lang == currentLang) {
2138 // if query matches the doc title
2139 if (s.title.toLowerCase().match(textRegex)) {
2140 matched = true;
2141 s.matched_title = 1;
2142 }
2143 }
2144 if (matched) {
2145 gDocsMatches[matchedCountDocs] = s;
2146 matchedCountDocs++;
2147 }
2148 }
2149
2150
2151 // Search for Google guides
2152 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2153 // current search comparison, with counters for tag and title,
2154 // used later to improve ranking
2155 var s = GOOGLE_RESOURCES[i];
2156 s.matched_tag = 0;
2157 s.matched_title = 0;
2158 var matched = false;
2159
2160 // Check if query matches any tags; work backwards toward 1 to assist ranking
2161 for (var j = s.keywords.length - 1; j >= 0; j--) {
2162 // it matches a tag
2163 if (s.keywords[j].toLowerCase().match(textRegex)) {
2164 matched = true;
2165 s.matched_tag = j + 1; // add 1 to index position
2166 }
2167 }
2168 // Check if query matches the doc title, but only for current language
2169 if (s.lang == currentLang) {
2170 // if query matches the doc title
2171 if (s.title.toLowerCase().match(textRegex)) {
2172 matched = true;
2173 s.matched_title = 1;
2174 }
2175 }
2176 if (matched) {
2177 gDocsMatches[matchedCountDocs] = s;
2178 matchedCountDocs++;
2179 }
2180 }
2181
2182
2183 // Search for Samples
2184 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2185 // current search comparison, with counters for tag and title,
2186 // used later to improve ranking
2187 var s = SAMPLES_RESOURCES[i];
2188 s.matched_tag = 0;
2189 s.matched_title = 0;
2190 var matched = false;
2191 // Check if query matches any tags; work backwards toward 1 to assist ranking
2192 for (var j = s.keywords.length - 1; j >= 0; j--) {
2193 // it matches a tag
2194 if (s.keywords[j].toLowerCase().match(textRegex)) {
2195 matched = true;
2196 s.matched_tag = j + 1; // add 1 to index position
2197 }
2198 }
2199 // Check if query matches the doc title, but only for current language
2200 if (s.lang == currentLang) {
2201 // if query matches the doc title.t
2202 if (s.title.toLowerCase().match(textRegex)) {
2203 matched = true;
2204 s.matched_title = 1;
2205 }
2206 }
2207 if (matched) {
2208 gDocsMatches[matchedCountDocs] = s;
2209 matchedCountDocs++;
2210 }
2211 }
2212
2213 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002214 rank_autocomplete_doc_results(text, gDocsMatches);
2215 }
2216
2217 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002218 sync_selection_table(toroot);
2219 return true; // allow the event to bubble up to the search api
2220 }
2221}
2222
Scott Main0e76e7e2013-03-12 10:24:07 -07002223/* Order the jd doc result list based on match quality */
2224function rank_autocomplete_doc_results(query, matches) {
2225 query = query || '';
2226 if (!matches || !matches.length)
2227 return;
2228
2229 var _resultScoreFn = function(match) {
2230 var score = 1.0;
2231
2232 // if the query matched a tag
2233 if (match.matched_tag > 0) {
2234 // multiply score by factor relative to position in tags list (max of 3)
2235 score *= 3 / match.matched_tag;
2236
2237 // if it also matched the title
2238 if (match.matched_title > 0) {
2239 score *= 2;
2240 }
2241 } else if (match.matched_title > 0) {
2242 score *= 3;
2243 }
2244
2245 return score;
2246 };
2247
2248 for (var i=0; i<matches.length; i++) {
2249 matches[i].__resultScore = _resultScoreFn(matches[i]);
2250 }
2251
2252 matches.sort(function(a,b){
2253 var n = b.__resultScore - a.__resultScore;
2254 if (n == 0) // lexicographical sort if scores are the same
2255 n = (a.label < b.label) ? -1 : 1;
2256 return n;
2257 });
2258}
2259
Scott Main7e447ed2013-02-19 17:22:37 -08002260/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002261function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002262 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002263 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002264 return;
2265
2266 // helper function that gets the last occurence index of the given regex
2267 // in the given string, or -1 if not found
2268 var _lastSearch = function(s, re) {
2269 if (s == '')
2270 return -1;
2271 var l = -1;
2272 var tmp;
2273 while ((tmp = s.search(re)) >= 0) {
2274 if (l < 0) l = 0;
2275 l += tmp;
2276 s = s.substr(tmp + 1);
2277 }
2278 return l;
2279 };
2280
2281 // helper function that counts the occurrences of a given character in
2282 // a given string
2283 var _countChar = function(s, c) {
2284 var n = 0;
2285 for (var i=0; i<s.length; i++)
2286 if (s.charAt(i) == c) ++n;
2287 return n;
2288 };
2289
2290 var queryLower = query.toLowerCase();
2291 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2292 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2293 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2294
2295 var _resultScoreFn = function(result) {
2296 // scores are calculated based on exact and prefix matches,
2297 // and then number of path separators (dots) from the last
2298 // match (i.e. favoring classes and deep package names)
2299 var score = 1.0;
2300 var labelLower = result.label.toLowerCase();
2301 var t;
2302 t = _lastSearch(labelLower, partExactAlnumRE);
2303 if (t >= 0) {
2304 // exact part match
2305 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2306 score *= 200 / (partsAfter + 1);
2307 } else {
2308 t = _lastSearch(labelLower, partPrefixAlnumRE);
2309 if (t >= 0) {
2310 // part prefix match
2311 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2312 score *= 20 / (partsAfter + 1);
2313 }
2314 }
2315
2316 return score;
2317 };
2318
Scott Main7e447ed2013-02-19 17:22:37 -08002319 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002320 // if the API is deprecated, default score is 0; otherwise, perform scoring
2321 if (matches[i].deprecated == "true") {
2322 matches[i].__resultScore = 0;
2323 } else {
2324 matches[i].__resultScore = _resultScoreFn(matches[i]);
2325 }
Scott Mainf5089842012-08-14 16:31:07 -07002326 }
2327
Scott Main7e447ed2013-02-19 17:22:37 -08002328 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002329 var n = b.__resultScore - a.__resultScore;
2330 if (n == 0) // lexicographical sort if scores are the same
2331 n = (a.label < b.label) ? -1 : 1;
2332 return n;
2333 });
2334}
2335
Scott Main7e447ed2013-02-19 17:22:37 -08002336/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002337function highlight_autocomplete_result_labels(query) {
2338 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002339 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002340 return;
2341
2342 var queryLower = query.toLowerCase();
2343 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2344 var queryRE = new RegExp(
2345 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2346 for (var i=0; i<gMatches.length; i++) {
2347 gMatches[i].__hilabel = gMatches[i].label.replace(
2348 queryRE, '<b>$1</b>');
2349 }
Scott Main7e447ed2013-02-19 17:22:37 -08002350 for (var i=0; i<gGoogleMatches.length; i++) {
2351 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2352 queryRE, '<b>$1</b>');
2353 }
Scott Mainf5089842012-08-14 16:31:07 -07002354}
2355
2356function search_focus_changed(obj, focused)
2357{
Scott Main3b90aff2013-08-01 18:09:35 -07002358 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002359 if(obj.value == ""){
2360 $(".search .close").addClass("hide");
2361 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002362 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002363 }
2364}
2365
2366function submit_search() {
2367 var query = document.getElementById('search_autocomplete').value;
2368 location.hash = 'q=' + query;
2369 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002370 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002371 return false;
2372}
2373
2374
2375function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002376 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002377 $(".search .close").addClass("hide");
2378 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002379
Scott Mainf5089842012-08-14 16:31:07 -07002380 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002381
Scott Mainf5089842012-08-14 16:31:07 -07002382 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2383 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002384
2385 // forcefully regain key-up event control (previously jacked by search api)
2386 $("#search_autocomplete").keyup(function(event) {
2387 return search_changed(event, false, toRoot);
2388 });
2389
Scott Mainf5089842012-08-14 16:31:07 -07002390 return false;
2391}
2392
2393
2394
2395/* ########################################################## */
2396/* ################ CUSTOM SEARCH ENGINE ################## */
2397/* ########################################################## */
2398
Scott Mainf5089842012-08-14 16:31:07 -07002399var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002400google.load('search', '1', {"callback" : function() {
2401 searchControl = new google.search.SearchControl();
2402 } });
Scott Mainf5089842012-08-14 16:31:07 -07002403
2404function loadSearchResults() {
2405 document.getElementById("search_autocomplete").style.color = "#000";
2406
Scott Mainf5089842012-08-14 16:31:07 -07002407 searchControl = new google.search.SearchControl();
2408
2409 // use our existing search form and use tabs when multiple searchers are used
2410 drawOptions = new google.search.DrawOptions();
2411 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2412 drawOptions.setInput(document.getElementById("search_autocomplete"));
2413
2414 // configure search result options
2415 searchOptions = new google.search.SearcherOptions();
2416 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2417
2418 // configure each of the searchers, for each tab
2419 devSiteSearcher = new google.search.WebSearch();
2420 devSiteSearcher.setUserDefinedLabel("All");
2421 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2422
2423 designSearcher = new google.search.WebSearch();
2424 designSearcher.setUserDefinedLabel("Design");
2425 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2426
2427 trainingSearcher = new google.search.WebSearch();
2428 trainingSearcher.setUserDefinedLabel("Training");
2429 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2430
2431 guidesSearcher = new google.search.WebSearch();
2432 guidesSearcher.setUserDefinedLabel("Guides");
2433 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2434
2435 referenceSearcher = new google.search.WebSearch();
2436 referenceSearcher.setUserDefinedLabel("Reference");
2437 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2438
Scott Maindf08ada2012-12-03 08:54:37 -08002439 googleSearcher = new google.search.WebSearch();
2440 googleSearcher.setUserDefinedLabel("Google Services");
2441 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2442
Scott Mainf5089842012-08-14 16:31:07 -07002443 blogSearcher = new google.search.WebSearch();
2444 blogSearcher.setUserDefinedLabel("Blog");
2445 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2446
2447 // add each searcher to the search control
2448 searchControl.addSearcher(devSiteSearcher, searchOptions);
2449 searchControl.addSearcher(designSearcher, searchOptions);
2450 searchControl.addSearcher(trainingSearcher, searchOptions);
2451 searchControl.addSearcher(guidesSearcher, searchOptions);
2452 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002453 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002454 searchControl.addSearcher(blogSearcher, searchOptions);
2455
2456 // configure result options
2457 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2458 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2459 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2460 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2461
2462 // upon ajax search, refresh the url and search title
2463 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2464 updateResultTitle(query);
2465 var query = document.getElementById('search_autocomplete').value;
2466 location.hash = 'q=' + query;
2467 });
2468
Scott Mainde295272013-03-25 15:48:35 -07002469 // once search results load, set up click listeners
2470 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2471 addResultClickListeners();
2472 });
2473
Scott Mainf5089842012-08-14 16:31:07 -07002474 // draw the search results box
2475 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2476
2477 // get query and execute the search
2478 searchControl.execute(decodeURI(getQuery(location.hash)));
2479
2480 document.getElementById("search_autocomplete").focus();
2481 addTabListeners();
2482}
2483// End of loadSearchResults
2484
2485
2486google.setOnLoadCallback(function(){
2487 if (location.hash.indexOf("q=") == -1) {
2488 // if there's no query in the url, don't search and make sure results are hidden
2489 $('#searchResults').hide();
2490 return;
2491 } else {
2492 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002493 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002494 $(".search .close").removeClass("hide");
2495 loadSearchResults();
2496 }
2497}, true);
2498
2499// when an event on the browser history occurs (back, forward, load) requery hash and do search
2500$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002501 // If the hash isn't a search query or there's an error in the query,
2502 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002503 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2504 // If the results pane is open, close it.
2505 if (!$("#searchResults").is(":hidden")) {
2506 hideResults();
2507 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07002508 // Adjust the scroll position to account for sticky header
2509 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainf5089842012-08-14 16:31:07 -07002510 return;
2511 }
2512
2513 // Otherwise, we have a search to do
2514 var query = decodeURI(getQuery(location.hash));
2515 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002516 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002517 $("#search_autocomplete").focus();
2518 $(".search .close").removeClass("hide");
2519
2520 updateResultTitle(query);
2521});
2522
2523function updateResultTitle(query) {
2524 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2525}
2526
2527// forcefully regain key-up event control (previously jacked by search api)
2528$("#search_autocomplete").keyup(function(event) {
2529 return search_changed(event, false, toRoot);
2530});
2531
2532// add event listeners to each tab so we can track the browser history
2533function addTabListeners() {
2534 var tabHeaders = $(".gsc-tabHeader");
2535 for (var i = 0; i < tabHeaders.length; i++) {
2536 $(tabHeaders[i]).attr("id",i).click(function() {
2537 /*
2538 // make a copy of the page numbers for the search left pane
2539 setTimeout(function() {
2540 // remove any residual page numbers
2541 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002542 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002543 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002544 // and because we're going to remove it (previous line),
2545 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002546 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2547 .clone().appendTo('#searchResults .gsc-tabsArea');
2548 }, 200);
2549 */
2550 });
2551 }
2552 setTimeout(function(){$(tabHeaders[0]).click()},200);
2553}
2554
Scott Mainde295272013-03-25 15:48:35 -07002555// add analytics tracking events to each result link
2556function addResultClickListeners() {
2557 $("#searchResults a.gs-title").each(function(index, link) {
2558 // When user clicks enter for Google search results, track it
2559 $(link).click(function() {
2560 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2561 'from: ' + $("#search_autocomplete").val()]);
2562 });
2563 });
2564}
2565
Scott Mainf5089842012-08-14 16:31:07 -07002566
2567function getQuery(hash) {
2568 var queryParts = hash.split('=');
2569 return queryParts[1];
2570}
2571
2572/* returns the given string with all HTML brackets converted to entities
2573 TODO: move this to the site's JS library */
2574function escapeHTML(string) {
2575 return string.replace(/</g,"&lt;")
2576 .replace(/>/g,"&gt;");
2577}
2578
2579
2580
2581
2582
2583
2584
2585/* ######################################################## */
2586/* ################# JAVADOC REFERENCE ################### */
2587/* ######################################################## */
2588
Scott Main65511c02012-09-07 15:51:32 -07002589/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002590if (location.pathname.indexOf("/reference") == 0) {
2591 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2592 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2593 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002594 $(document).ready(function() {
2595 // init available apis based on user pref
2596 changeApiLevel();
2597 initSidenavHeightResize()
2598 });
2599 }
Scott Main65511c02012-09-07 15:51:32 -07002600}
Scott Mainf5089842012-08-14 16:31:07 -07002601
2602var API_LEVEL_COOKIE = "api_level";
2603var minLevel = 1;
2604var maxLevel = 1;
2605
2606/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002607
Scott Mainf5089842012-08-14 16:31:07 -07002608 function initSidenavHeightResize() {
2609 // Change the drag bar size to nicely fit the scrollbar positions
2610 var $dragBar = $(".ui-resizable-s");
2611 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002612
2613 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002614 containment: "#nav-panels",
2615 handles: "s",
2616 alsoResize: "#packages-nav",
2617 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2618 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2619 });
Scott Main3b90aff2013-08-01 18:09:35 -07002620
Scott Mainf5089842012-08-14 16:31:07 -07002621 }
Scott Main3b90aff2013-08-01 18:09:35 -07002622
Scott Mainf5089842012-08-14 16:31:07 -07002623function updateSidenavFixedWidth() {
2624 if (!navBarIsFixed) return;
2625 $('#devdoc-nav').css({
2626 'width' : $('#side-nav').css('width'),
2627 'margin' : $('#side-nav').css('margin')
2628 });
2629 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002630
Scott Mainf5089842012-08-14 16:31:07 -07002631 initSidenavHeightResize();
2632}
2633
2634function updateSidenavFullscreenWidth() {
2635 if (!navBarIsFixed) return;
2636 $('#devdoc-nav').css({
2637 'width' : $('#side-nav').css('width'),
2638 'margin' : $('#side-nav').css('margin')
2639 });
2640 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002641
Scott Mainf5089842012-08-14 16:31:07 -07002642 initSidenavHeightResize();
2643}
2644
2645function buildApiLevelSelector() {
2646 maxLevel = SINCE_DATA.length;
2647 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2648 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2649
2650 minLevel = parseInt($("#doc-api-level").attr("class"));
2651 // Handle provisional api levels; the provisional level will always be the highest possible level
2652 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2653 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2654 if (isNaN(minLevel) && minLevel.length) {
2655 minLevel = maxLevel;
2656 }
2657 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2658 for (var i = maxLevel-1; i >= 0; i--) {
2659 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2660 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2661 select.append(option);
2662 }
2663
2664 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2665 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2666 selectedLevelItem.setAttribute('selected',true);
2667}
2668
2669function changeApiLevel() {
2670 maxLevel = SINCE_DATA.length;
2671 var selectedLevel = maxLevel;
2672
2673 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2674 toggleVisisbleApis(selectedLevel, "body");
2675
2676 var date = new Date();
2677 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2678 var expiration = date.toGMTString();
2679 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2680
2681 if (selectedLevel < minLevel) {
2682 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002683 $("#naMessage").show().html("<div><p><strong>This " + thing
2684 + " requires API level " + minLevel + " or higher.</strong></p>"
2685 + "<p>This document is hidden because your selected API level for the documentation is "
2686 + selectedLevel + ". You can change the documentation API level with the selector "
2687 + "above the left navigation.</p>"
2688 + "<p>For more information about specifying the API level your app requires, "
2689 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2690 + ">Supporting Different Platform Versions</a>.</p>"
2691 + "<input type='button' value='OK, make this page visible' "
2692 + "title='Change the API level to " + minLevel + "' "
2693 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2694 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002695 } else {
2696 $("#naMessage").hide();
2697 }
2698}
2699
2700function toggleVisisbleApis(selectedLevel, context) {
2701 var apis = $(".api",context);
2702 apis.each(function(i) {
2703 var obj = $(this);
2704 var className = obj.attr("class");
2705 var apiLevelIndex = className.lastIndexOf("-")+1;
2706 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2707 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2708 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2709 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2710 return;
2711 }
2712 apiLevel = parseInt(apiLevel);
2713
2714 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2715 var selectedLevelNum = parseInt(selectedLevel)
2716 var apiLevelNum = parseInt(apiLevel);
2717 if (isNaN(apiLevelNum)) {
2718 apiLevelNum = maxLevel;
2719 }
2720
2721 // Grey things out that aren't available and give a tooltip title
2722 if (apiLevelNum > selectedLevelNum) {
2723 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002724 + apiLevel + "\" or higher. To reveal, change the target API level "
2725 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002726 }
Scott Mainf5089842012-08-14 16:31:07 -07002727 else obj.removeClass("absent").removeAttr("title");
2728 });
2729}
2730
2731
2732
2733
2734/* ################# SIDENAV TREE VIEW ################### */
2735
2736function new_node(me, mom, text, link, children_data, api_level)
2737{
2738 var node = new Object();
2739 node.children = Array();
2740 node.children_data = children_data;
2741 node.depth = mom.depth + 1;
2742
2743 node.li = document.createElement("li");
2744 mom.get_children_ul().appendChild(node.li);
2745
2746 node.label_div = document.createElement("div");
2747 node.label_div.className = "label";
2748 if (api_level != null) {
2749 $(node.label_div).addClass("api");
2750 $(node.label_div).addClass("api-level-"+api_level);
2751 }
2752 node.li.appendChild(node.label_div);
2753
2754 if (children_data != null) {
2755 node.expand_toggle = document.createElement("a");
2756 node.expand_toggle.href = "javascript:void(0)";
2757 node.expand_toggle.onclick = function() {
2758 if (node.expanded) {
2759 $(node.get_children_ul()).slideUp("fast");
2760 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2761 node.expanded = false;
2762 } else {
2763 expand_node(me, node);
2764 }
2765 };
2766 node.label_div.appendChild(node.expand_toggle);
2767
2768 node.plus_img = document.createElement("img");
2769 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2770 node.plus_img.className = "plus";
2771 node.plus_img.width = "8";
2772 node.plus_img.border = "0";
2773 node.expand_toggle.appendChild(node.plus_img);
2774
2775 node.expanded = false;
2776 }
2777
2778 var a = document.createElement("a");
2779 node.label_div.appendChild(a);
2780 node.label = document.createTextNode(text);
2781 a.appendChild(node.label);
2782 if (link) {
2783 a.href = me.toroot + link;
2784 } else {
2785 if (children_data != null) {
2786 a.className = "nolink";
2787 a.href = "javascript:void(0)";
2788 a.onclick = node.expand_toggle.onclick;
2789 // This next line shouldn't be necessary. I'll buy a beer for the first
2790 // person who figures out how to remove this line and have the link
2791 // toggle shut on the first try. --joeo@android.com
2792 node.expanded = false;
2793 }
2794 }
Scott Main3b90aff2013-08-01 18:09:35 -07002795
Scott Mainf5089842012-08-14 16:31:07 -07002796
2797 node.children_ul = null;
2798 node.get_children_ul = function() {
2799 if (!node.children_ul) {
2800 node.children_ul = document.createElement("ul");
2801 node.children_ul.className = "children_ul";
2802 node.children_ul.style.display = "none";
2803 node.li.appendChild(node.children_ul);
2804 }
2805 return node.children_ul;
2806 };
2807
2808 return node;
2809}
2810
Robert Lyd2dd6e52012-11-29 21:28:48 -08002811
2812
2813
Scott Mainf5089842012-08-14 16:31:07 -07002814function expand_node(me, node)
2815{
2816 if (node.children_data && !node.expanded) {
2817 if (node.children_visited) {
2818 $(node.get_children_ul()).slideDown("fast");
2819 } else {
2820 get_node(me, node);
2821 if ($(node.label_div).hasClass("absent")) {
2822 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002823 }
Scott Mainf5089842012-08-14 16:31:07 -07002824 $(node.get_children_ul()).slideDown("fast");
2825 }
2826 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2827 node.expanded = true;
2828
2829 // perform api level toggling because new nodes are new to the DOM
2830 var selectedLevel = $("#apiLevelSelector option:selected").val();
2831 toggleVisisbleApis(selectedLevel, "#side-nav");
2832 }
2833}
2834
2835function get_node(me, mom)
2836{
2837 mom.children_visited = true;
2838 for (var i in mom.children_data) {
2839 var node_data = mom.children_data[i];
2840 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2841 node_data[2], node_data[3]);
2842 }
2843}
2844
2845function this_page_relative(toroot)
2846{
2847 var full = document.location.pathname;
2848 var file = "";
2849 if (toroot.substr(0, 1) == "/") {
2850 if (full.substr(0, toroot.length) == toroot) {
2851 return full.substr(toroot.length);
2852 } else {
2853 // the file isn't under toroot. Fail.
2854 return null;
2855 }
2856 } else {
2857 if (toroot != "./") {
2858 toroot = "./" + toroot;
2859 }
2860 do {
2861 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2862 var pos = full.lastIndexOf("/");
2863 file = full.substr(pos) + file;
2864 full = full.substr(0, pos);
2865 toroot = toroot.substr(0, toroot.length-3);
2866 }
2867 } while (toroot != "" && toroot != "/");
2868 return file.substr(1);
2869 }
2870}
2871
2872function find_page(url, data)
2873{
2874 var nodes = data;
2875 var result = null;
2876 for (var i in nodes) {
2877 var d = nodes[i];
2878 if (d[1] == url) {
2879 return new Array(i);
2880 }
2881 else if (d[2] != null) {
2882 result = find_page(url, d[2]);
2883 if (result != null) {
2884 return (new Array(i).concat(result));
2885 }
2886 }
2887 }
2888 return null;
2889}
2890
Scott Mainf5089842012-08-14 16:31:07 -07002891function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002892 // load json file for navtree data
2893 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2894 // when the file is loaded, initialize the tree
2895 if(jqxhr.status === 200) {
2896 init_navtree("tree-list", toroot, NAVTREE_DATA);
2897 }
2898 });
Scott Main3b90aff2013-08-01 18:09:35 -07002899
Scott Mainf5089842012-08-14 16:31:07 -07002900 // perform api level toggling because because the whole tree is new to the DOM
2901 var selectedLevel = $("#apiLevelSelector option:selected").val();
2902 toggleVisisbleApis(selectedLevel, "#side-nav");
2903}
2904
2905function init_navtree(navtree_id, toroot, root_nodes)
2906{
2907 var me = new Object();
2908 me.toroot = toroot;
2909 me.node = new Object();
2910
2911 me.node.li = document.getElementById(navtree_id);
2912 me.node.children_data = root_nodes;
2913 me.node.children = new Array();
2914 me.node.children_ul = document.createElement("ul");
2915 me.node.get_children_ul = function() { return me.node.children_ul; };
2916 //me.node.children_ul.className = "children_ul";
2917 me.node.li.appendChild(me.node.children_ul);
2918 me.node.depth = 0;
2919
2920 get_node(me, me.node);
2921
2922 me.this_page = this_page_relative(toroot);
2923 me.breadcrumbs = find_page(me.this_page, root_nodes);
2924 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2925 var mom = me.node;
2926 for (var i in me.breadcrumbs) {
2927 var j = me.breadcrumbs[i];
2928 mom = mom.children[j];
2929 expand_node(me, mom);
2930 }
2931 mom.label_div.className = mom.label_div.className + " selected";
2932 addLoadEvent(function() {
2933 scrollIntoView("nav-tree");
2934 });
2935 }
2936}
2937
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07002938
2939
2940
2941
2942
2943
2944
Robert Lyd2dd6e52012-11-29 21:28:48 -08002945/* TODO: eliminate redundancy with non-google functions */
2946function init_google_navtree(navtree_id, toroot, root_nodes)
2947{
2948 var me = new Object();
2949 me.toroot = toroot;
2950 me.node = new Object();
2951
2952 me.node.li = document.getElementById(navtree_id);
2953 me.node.children_data = root_nodes;
2954 me.node.children = new Array();
2955 me.node.children_ul = document.createElement("ul");
2956 me.node.get_children_ul = function() { return me.node.children_ul; };
2957 //me.node.children_ul.className = "children_ul";
2958 me.node.li.appendChild(me.node.children_ul);
2959 me.node.depth = 0;
2960
2961 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002962}
2963
2964function new_google_node(me, mom, text, link, children_data, api_level)
2965{
2966 var node = new Object();
2967 var child;
2968 node.children = Array();
2969 node.children_data = children_data;
2970 node.depth = mom.depth + 1;
2971 node.get_children_ul = function() {
2972 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07002973 node.children_ul = document.createElement("ul");
2974 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002975 node.li.appendChild(node.children_ul);
2976 }
2977 return node.children_ul;
2978 };
2979 node.li = document.createElement("li");
2980
2981 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07002982
2983
Robert Lyd2dd6e52012-11-29 21:28:48 -08002984 if(link) {
2985 child = document.createElement("a");
2986
2987 }
2988 else {
2989 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08002990 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002991
2992 }
2993 if (children_data != null) {
2994 node.li.className="nav-section";
2995 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07002996 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002997 node.li.appendChild(node.label_div);
2998 get_google_node(me, node);
2999 node.label_div.appendChild(child);
3000 }
3001 else {
3002 node.li.appendChild(child);
3003 }
3004 if(link) {
3005 child.href = me.toroot + link;
3006 }
3007 node.label = document.createTextNode(text);
3008 child.appendChild(node.label);
3009
3010 node.children_ul = null;
3011
3012 return node;
3013}
3014
3015function get_google_node(me, mom)
3016{
3017 mom.children_visited = true;
3018 var linkText;
3019 for (var i in mom.children_data) {
3020 var node_data = mom.children_data[i];
3021 linkText = node_data[0];
3022
3023 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3024 linkText = linkText.substr(19, linkText.length);
3025 }
3026 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3027 node_data[2], node_data[3]);
3028 }
3029}
Scott Mainad08f072013-08-20 16:49:57 -07003030
3031
3032
3033
3034
3035
3036/****** NEW version of script to build google and sample navs dynamically ******/
3037// TODO: update Google reference docs to tolerate this new implementation
3038
Scott Maine624b3f2013-09-12 12:56:41 -07003039var NODE_NAME = 0;
3040var NODE_HREF = 1;
3041var NODE_GROUP = 2;
3042var NODE_TAGS = 3;
3043var NODE_CHILDREN = 4;
3044
Scott Mainad08f072013-08-20 16:49:57 -07003045function init_google_navtree2(navtree_id, data)
3046{
3047 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003048 for (var i in data) {
3049 var node_data = data[i];
3050 $containerUl.append(new_google_node2(node_data));
3051 }
3052
Scott Main70557ee2013-10-30 14:47:40 -07003053 // Make all third-generation list items 'sticky' to prevent them from collapsing
3054 $containerUl.find('li li li.nav-section').addClass('sticky');
3055
Scott Mainad08f072013-08-20 16:49:57 -07003056 initExpandableNavItems("#"+navtree_id);
3057}
3058
3059function new_google_node2(node_data)
3060{
Scott Maine624b3f2013-09-12 12:56:41 -07003061 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003062 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3063 linkText = linkText.substr(19, linkText.length);
3064 }
3065 var $li = $('<li>');
3066 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003067 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003068 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3069 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003070 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003071 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3072 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003073 }
3074 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003075 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003076 $li.addClass("nav-section");
3077 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003078 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003079
Scott Maine624b3f2013-09-12 12:56:41 -07003080 for (var i in node_data[NODE_CHILDREN]) {
3081 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003082 $childUl.append(new_google_node2(child_node_data));
3083 }
3084 $li.append($childUl);
3085 }
3086 $li.prepend($a);
3087
3088 return $li;
3089}
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
Robert Lyd2dd6e52012-11-29 21:28:48 -08003101function showGoogleRefTree() {
3102 init_default_google_navtree(toRoot);
3103 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003104}
3105
3106function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003107 // load json file for navtree data
3108 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3109 // when the file is loaded, initialize the tree
3110 if(jqxhr.status === 200) {
3111 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3112 highlightSidenav();
3113 resizeNav();
3114 }
3115 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003116}
3117
3118function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003119 // load json file for navtree data
3120 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3121 // when the file is loaded, initialize the tree
3122 if(jqxhr.status === 200) {
3123 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3124 highlightSidenav();
3125 resizeNav();
3126 }
3127 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003128}
3129
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003130function showSamplesRefTree() {
3131 init_default_samples_navtree(toRoot);
3132}
3133
3134function init_default_samples_navtree(toroot) {
3135 // load json file for navtree data
3136 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3137 // when the file is loaded, initialize the tree
3138 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003139 // hack to remove the "about the samples" link then put it back in
3140 // after we nuke the list to remove the dummy static list of samples
3141 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3142 $("#nav.samples-nav").empty();
3143 $("#nav.samples-nav").append($firstLi);
3144
Scott Mainad08f072013-08-20 16:49:57 -07003145 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003146 highlightSidenav();
3147 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003148 if ($("#jd-content #samples").length) {
3149 showSamples();
3150 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003151 }
3152 });
3153}
3154
Scott Mainf5089842012-08-14 16:31:07 -07003155/* TOGGLE INHERITED MEMBERS */
3156
3157/* Toggle an inherited class (arrow toggle)
3158 * @param linkObj The link that was clicked.
3159 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3160 * 'null' to simply toggle.
3161 */
3162function toggleInherited(linkObj, expand) {
3163 var base = linkObj.getAttribute("id");
3164 var list = document.getElementById(base + "-list");
3165 var summary = document.getElementById(base + "-summary");
3166 var trigger = document.getElementById(base + "-trigger");
3167 var a = $(linkObj);
3168 if ( (expand == null && a.hasClass("closed")) || expand ) {
3169 list.style.display = "none";
3170 summary.style.display = "block";
3171 trigger.src = toRoot + "assets/images/triangle-opened.png";
3172 a.removeClass("closed");
3173 a.addClass("opened");
3174 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3175 list.style.display = "block";
3176 summary.style.display = "none";
3177 trigger.src = toRoot + "assets/images/triangle-closed.png";
3178 a.removeClass("opened");
3179 a.addClass("closed");
3180 }
3181 return false;
3182}
3183
3184/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3185 * @param linkObj The link that was clicked.
3186 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3187 * 'null' to simply toggle.
3188 */
3189function toggleAllInherited(linkObj, expand) {
3190 var a = $(linkObj);
3191 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3192 var expandos = $(".jd-expando-trigger", table);
3193 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3194 expandos.each(function(i) {
3195 toggleInherited(this, true);
3196 });
3197 a.text("[Collapse]");
3198 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3199 expandos.each(function(i) {
3200 toggleInherited(this, false);
3201 });
3202 a.text("[Expand]");
3203 }
3204 return false;
3205}
3206
3207/* Toggle all inherited members in the class (link in the class title)
3208 */
3209function toggleAllClassInherited() {
3210 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3211 var toggles = $(".toggle-all", $("#body-content"));
3212 if (a.text() == "[Expand All]") {
3213 toggles.each(function(i) {
3214 toggleAllInherited(this, true);
3215 });
3216 a.text("[Collapse All]");
3217 } else {
3218 toggles.each(function(i) {
3219 toggleAllInherited(this, false);
3220 });
3221 a.text("[Expand All]");
3222 }
3223 return false;
3224}
3225
3226/* Expand all inherited members in the class. Used when initiating page search */
3227function ensureAllInheritedExpanded() {
3228 var toggles = $(".toggle-all", $("#body-content"));
3229 toggles.each(function(i) {
3230 toggleAllInherited(this, true);
3231 });
3232 $("#toggleAllClassInherited").text("[Collapse All]");
3233}
3234
3235
3236/* HANDLE KEY EVENTS
3237 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3238 */
3239var agent = navigator['userAgent'].toLowerCase();
3240var mac = agent.indexOf("macintosh") != -1;
3241
3242$(document).keydown( function(e) {
3243var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3244 if (control && e.which == 70) { // 70 is "F"
3245 ensureAllInheritedExpanded();
3246 }
3247});
Scott Main498d7102013-08-21 15:47:38 -07003248
3249
3250
3251
3252
3253
3254/* On-demand functions */
3255
3256/** Move sample code line numbers out of PRE block and into non-copyable column */
3257function initCodeLineNumbers() {
3258 var numbers = $("#codesample-block a.number");
3259 if (numbers.length) {
3260 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3261 }
3262
3263 $(document).ready(function() {
3264 // select entire line when clicked
3265 $("span.code-line").click(function() {
3266 if (!shifted) {
3267 selectText(this);
3268 }
3269 });
3270 // invoke line link on double click
3271 $(".code-line").dblclick(function() {
3272 document.location.hash = $(this).attr('id');
3273 });
3274 // highlight the line when hovering on the number
3275 $("#codesample-line-numbers a.number").mouseover(function() {
3276 var id = $(this).attr('href');
3277 $(id).css('background','#e7e7e7');
3278 });
3279 $("#codesample-line-numbers a.number").mouseout(function() {
3280 var id = $(this).attr('href');
3281 $(id).css('background','none');
3282 });
3283 });
3284}
3285
3286// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3287var shifted = false;
3288$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3289
3290// courtesy of jasonedelman.com
3291function selectText(element) {
3292 var doc = document
3293 , range, selection
3294 ;
3295 if (doc.body.createTextRange) { //ms
3296 range = doc.body.createTextRange();
3297 range.moveToElementText(element);
3298 range.select();
3299 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003300 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003301 range = doc.createRange();
3302 range.selectNodeContents(element);
3303 selection.removeAllRanges();
3304 selection.addRange(range);
3305 }
Scott Main285f0772013-08-22 23:22:09 +00003306}
Scott Main03aca9a2013-10-31 07:20:55 -07003307
3308
3309
3310
3311/** Display links and other information about samples that match the
3312 group specified by the URL */
3313function showSamples() {
3314 var group = $("#samples").attr('class');
3315 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3316
3317 var $ul = $("<ul>");
3318 $selectedLi = $("#nav li.selected");
3319
3320 $selectedLi.children("ul").children("li").each(function() {
3321 var $li = $("<li>").append($(this).find("a").first().clone());
3322 $ul.append($li);
3323 });
3324
3325 $("#samples").append($ul);
3326
3327}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003328
3329
3330
3331/* ########################################################## */
3332/* ################### RESOURCE CARDS ##################### */
3333/* ########################################################## */
3334
3335/** Handle resource queries, collections, and grids (sections). Requires
3336 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3337
3338(function() {
3339 // Prevent the same resource from being loaded more than once per page.
3340 var addedPageResources = {};
3341
3342 $(document).ready(function() {
3343 $('.resource-widget').each(function() {
3344 initResourceWidget(this);
3345 });
3346
3347 /* Pass the line height to ellipsisfade() to adjust the height of the
3348 text container to show the max number of lines possible, without
3349 showing lines that are cut off. This works with the css ellipsis
3350 classes to fade last text line and apply an ellipsis char. */
3351
3352 //card text currently uses 15px line height.
3353 var lineHeight = 15;
3354 $('.card-info .text').ellipsisfade(lineHeight);
3355 });
3356
3357 /*
3358 Three types of resource layouts:
3359 Flow - Uses a fixed row-height flow using float left style.
3360 Carousel - Single card slideshow all same dimension absolute.
3361 Stack - Uses fixed columns and flexible element height.
3362 */
3363 function initResourceWidget(widget) {
3364 var $widget = $(widget);
3365 var isFlow = $widget.hasClass('resource-flow-layout'),
3366 isCarousel = $widget.hasClass('resource-carousel-layout'),
3367 isStack = $widget.hasClass('resource-stack-layout');
3368
3369 // find size of widget by pulling out its class name
3370 var sizeCols = 1;
3371 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3372 if (m) {
3373 sizeCols = parseInt(m[1], 10);
3374 }
3375
3376 var opts = {
3377 cardSizes: ($widget.data('cardsizes') || '').split(','),
3378 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3379 itemsPerPage: $widget.data('itemsperpage'),
3380 sortOrder: $widget.data('sortorder'),
3381 query: $widget.data('query'),
3382 section: $widget.data('section'),
3383 sizeCols: sizeCols
3384 };
3385
3386 // run the search for the set of resources to show
3387
3388 var resources = buildResourceList(opts);
3389
3390 if (isFlow) {
3391 drawResourcesFlowWidget($widget, opts, resources);
3392 } else if (isCarousel) {
3393 drawResourcesCarouselWidget($widget, opts, resources);
3394 } else if (isStack) {
3395 var sections = buildSectionList(opts);
3396 opts['numStacks'] = $widget.data('numstacks');
3397 drawResourcesStackWidget($widget, opts, resources, sections);
3398 }
3399 }
3400
3401 /* Initializes a Resource Carousel Widget */
3402 function drawResourcesCarouselWidget($widget, opts, resources) {
3403 $widget.empty();
3404 var plusone = true; //always show plusone on carousel
3405
3406 $widget.addClass('resource-card slideshow-container')
3407 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3408 .append($('<a>').addClass('slideshow-next').text('Next'));
3409
3410 var css = { 'width': $widget.width() + 'px',
3411 'height': $widget.height() + 'px' };
3412
3413 var $ul = $('<ul>');
3414
3415 for (var i = 0; i < resources.length; ++i) {
3416 //keep url clean for matching and offline mode handling
3417 var urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3418 var $card = $('<a>')
3419 .attr('href', urlPrefix + resources[i].url)
3420 .decorateResourceCard(resources[i],plusone);
3421
3422 $('<li>').css(css)
3423 .append($card)
3424 .appendTo($ul);
3425 }
3426
3427 $('<div>').addClass('frame')
3428 .append($ul)
3429 .appendTo($widget);
3430
3431 $widget.dacSlideshow({
3432 auto: true,
3433 btnPrev: '.slideshow-prev',
3434 btnNext: '.slideshow-next'
3435 });
3436 };
3437
3438 /* Initializes a Resource Card Stack Widget (column-based layout) */
3439 function drawResourcesStackWidget($widget, opts, resources, sections) {
3440 // Don't empty widget, grab all items inside since they will be the first
3441 // items stacked, followed by the resource query
3442 var plusone = true; //by default show plusone on section cards
3443 var cards = $widget.find('.resource-card').detach().toArray();
3444 var numStacks = opts.numStacks || 1;
3445 var $stacks = [];
3446 var urlString;
3447
3448 for (var i = 0; i < numStacks; ++i) {
3449 $stacks[i] = $('<div>').addClass('resource-card-stack')
3450 .appendTo($widget);
3451 }
3452
3453 var sectionResources = [];
3454
3455 // Extract any subsections that are actually resource cards
3456 for (var i = 0; i < sections.length; ++i) {
3457 if (!sections[i].sections || !sections[i].sections.length) {
3458 //keep url clean for matching and offline mode handling
3459 urlPrefix = sections[i].url.indexOf("//") > -1 ? "" : toRoot;
3460 // Render it as a resource card
3461
3462 sectionResources.push(
3463 $('<a>')
3464 .addClass('resource-card section-card')
3465 .attr('href', urlPrefix + sections[i].resource.url)
3466 .decorateResourceCard(sections[i].resource,plusone)[0]
3467 );
3468
3469 } else {
3470 cards.push(
3471 $('<div>')
3472 .addClass('resource-card section-card-menu')
3473 .decorateResourceSection(sections[i],plusone)[0]
3474 );
3475 }
3476 }
3477
3478 cards = cards.concat(sectionResources);
3479
3480 for (var i = 0; i < resources.length; ++i) {
3481 //keep url clean for matching and offline mode handling
3482 urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3483 var $card = $('<a>')
3484 .addClass('resource-card related-card')
3485 .attr('href', urlPrefix + resources[i].url)
3486 .decorateResourceCard(resources[i],plusone);
3487
3488 cards.push($card[0]);
3489 }
3490
3491 for (var i = 0; i < cards.length; ++i) {
3492 // Find the stack with the shortest height, but give preference to
3493 // left to right order.
3494 var minHeight = $stacks[0].height();
3495 var minIndex = 0;
3496
3497 for (var j = 1; j < numStacks; ++j) {
3498 var height = $stacks[j].height();
3499 if (height < minHeight - 45) {
3500 minHeight = height;
3501 minIndex = j;
3502 }
3503 }
3504
3505 $stacks[minIndex].append($(cards[i]));
3506 }
3507
3508 };
3509
3510 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3511 function drawResourcesFlowWidget($widget, opts, resources) {
3512 $widget.empty();
3513 var cardSizes = opts.cardSizes || ['6x6'];
3514 var i = 0, j = 0;
3515 var plusone = true; // by default show plusone on resource cards
3516
3517 while (i < resources.length) {
3518 var cardSize = cardSizes[j++ % cardSizes.length];
3519 cardSize = cardSize.replace(/^\s+|\s+$/,'');
3520 console.log("cardsize is " + cardSize);
3521 // Some card sizes do not get a plusone button, such as where space is constrained
3522 // or for cards commonly embedded in docs (to improve overall page speed).
3523 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3524 (cardSize == "9x2") || (cardSize == "9x3") ||
3525 (cardSize == "12x2") || (cardSize == "12x3"));
3526
3527 // A stack has a third dimension which is the number of stacked items
3528 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3529 var stackCount = 0;
3530 var $stackDiv = null;
3531
3532 if (isStack) {
3533 // Create a stack container which should have the dimensions defined
3534 // by the product of the items inside.
3535 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3536 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3537 }
3538
3539 // Build each stack item or just a single item
3540 do {
3541 var resource = resources[i];
3542 //keep url clean for matching and offline mode handling
3543 urlPrefix = resource.url.indexOf("//") > -1 ? "" : toRoot;
3544 var $card = $('<a>')
3545 .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
3546 .attr('href', urlPrefix + resource.url);
3547
3548 if (isStack) {
3549 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3550 if (++stackCount == parseInt(isStack[3])) {
3551 $card.addClass('resource-card-row-stack-last');
3552 stackCount = 0;
3553 }
3554 } else {
3555 stackCount = 0;
3556 }
3557
3558 $card.decorateResourceCard(resource,plusone)
3559 .appendTo($stackDiv || $widget);
3560
3561 } while (++i < resources.length && stackCount > 0);
3562 }
3563 }
3564
3565 /* Build a site map of resources using a section as a root. */
3566 function buildSectionList(opts) {
3567 if (opts.section && SECTION_BY_ID[opts.section]) {
3568 return SECTION_BY_ID[opts.section].sections || [];
3569 }
3570 return [];
3571 }
3572
3573 function buildResourceList(opts) {
3574 var maxResults = opts.maxResults || 100;
3575
3576 var query = opts.query || '';
3577 var expressions = parseResourceQuery(query);
3578 var addedResourceIndices = {};
3579 var results = [];
3580
3581 for (var i = 0; i < expressions.length; i++) {
3582 var clauses = expressions[i];
3583
3584 // build initial set of resources from first clause
3585 var firstClause = clauses[0];
3586 var resources = [];
3587 switch (firstClause.attr) {
3588 case 'type':
3589 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3590 break;
3591 case 'lang':
3592 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3593 break;
3594 case 'tag':
3595 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3596 break;
3597 case 'collection':
3598 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3599 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3600 break;
3601 case 'section':
3602 var urls = SITE_MAP[firstClause.value].sections || [];
3603 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3604 break;
3605 }
3606 // console.log(firstClause.attr + ':' + firstClause.value);
3607 resources = resources || [];
3608
3609 // use additional clauses to filter corpus
3610 if (clauses.length > 1) {
3611 var otherClauses = clauses.slice(1);
3612 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3613 }
3614
3615 // filter out resources already added
3616 if (i > 1) {
3617 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3618 }
3619
3620 // add to list of already added indices
3621 for (var j = 0; j < resources.length; j++) {
3622 // console.log(resources[j].title);
3623 addedResourceIndices[resources[j].index] = 1;
3624 }
3625
3626 // concat to final results list
3627 results = results.concat(resources);
3628 }
3629
3630 if (opts.sortOrder && results.length) {
3631 var attr = opts.sortOrder;
3632
3633 if (opts.sortOrder == 'random') {
3634 var i = results.length, j, temp;
3635 while (--i) {
3636 j = Math.floor(Math.random() * (i + 1));
3637 temp = results[i];
3638 results[i] = results[j];
3639 results[j] = temp;
3640 }
3641 } else {
3642 var desc = attr.charAt(0) == '-';
3643 if (desc) {
3644 attr = attr.substring(1);
3645 }
3646 results = results.sort(function(x,y) {
3647 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3648 });
3649 }
3650 }
3651
3652 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3653 results = results.slice(0, maxResults);
3654
3655 for (var j = 0; j < results.length; ++j) {
3656 addedPageResources[results[j].index] = 1;
3657 }
3658
3659 return results;
3660 }
3661
3662
3663 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3664 return function(resource) {
3665 return !addedResourceIndices[resource.index];
3666 };
3667 }
3668
3669
3670 function getResourceMatchesClausesFilter(clauses) {
3671 return function(resource) {
3672 return doesResourceMatchClauses(resource, clauses);
3673 };
3674 }
3675
3676
3677 function doesResourceMatchClauses(resource, clauses) {
3678 for (var i = 0; i < clauses.length; i++) {
3679 var map;
3680 switch (clauses[i].attr) {
3681 case 'type':
3682 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3683 break;
3684 case 'lang':
3685 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3686 break;
3687 case 'tag':
3688 map = IS_RESOURCE_TAGGED[clauses[i].value];
3689 break;
3690 }
3691
3692 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3693 return clauses[i].negative;
3694 }
3695 }
3696 return true;
3697 }
3698
3699
3700 function parseResourceQuery(query) {
3701 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3702 var expressions = [];
3703 var expressionStrs = query.split(',') || [];
3704 for (var i = 0; i < expressionStrs.length; i++) {
3705 var expr = expressionStrs[i] || '';
3706
3707 // Break expression into clauses (clause e.g. 'tag:foo')
3708 var clauses = [];
3709 var clauseStrs = expr.split(/(?=[\+\-])/);
3710 for (var j = 0; j < clauseStrs.length; j++) {
3711 var clauseStr = clauseStrs[j] || '';
3712
3713 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3714 var parts = clauseStr.split(':');
3715 var clause = {};
3716
3717 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3718 if (clause.attr) {
3719 if (clause.attr.charAt(0) == '+') {
3720 clause.attr = clause.attr.substring(1);
3721 } else if (clause.attr.charAt(0) == '-') {
3722 clause.negative = true;
3723 clause.attr = clause.attr.substring(1);
3724 }
3725 }
3726
3727 if (parts.length > 1) {
3728 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3729 }
3730
3731 clauses.push(clause);
3732 }
3733
3734 if (!clauses.length) {
3735 continue;
3736 }
3737
3738 expressions.push(clauses);
3739 }
3740
3741 return expressions;
3742 }
3743})();
3744
3745(function($) {
3746 /* Simple jquery function to create dom for a standard resource card */
3747 $.fn.decorateResourceCard = function(resource,plusone) {
3748 var section = resource.group || resource.type;
3749 var imgUrl;
3750 if (resource.image) {
3751 //keep url clean for matching and offline mode handling
3752 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3753 imgUrl = urlPrefix + resource.image;
3754 }
3755 //add linkout logic here. check url or type, assign a class, map to css :after
3756 $('<div>')
3757 .addClass('card-bg')
3758 .css('background-image', 'url(' + (imgUrl || toRoot + 'assets/images/resource-card-default-android.jpg') + ')')
3759 .appendTo(this);
3760 if (!plusone) {
3761 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3762 .append($('<div>').addClass('section').text(section))
3763 .append($('<div>').addClass('title').html(resource.title))
3764 .append($('<div>').addClass('description ellipsis')
3765 .append($('<div>').addClass('text').html(resource.summary))
3766 .append($('<div>').addClass('util')))
3767 .appendTo(this);
3768 } else {
3769 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3770 .append($('<div>').addClass('section').text(section))
3771 .append($('<div>').addClass('title').html(resource.title))
3772 .append($('<div>').addClass('description ellipsis')
3773 .append($('<div>').addClass('text').html(resource.summary))
3774 .append($('<div>').addClass('util')
3775 .append($('<div>').addClass('g-plusone')
3776 .attr('data-size', 'small')
3777 .attr('data-align', 'right')
3778 .attr('data-href', resource.url))))
3779 .appendTo(this);
3780 }
3781
3782 return this;
3783 };
3784
3785 /* Simple jquery function to create dom for a resource section card (menu) */
3786 $.fn.decorateResourceSection = function(section,plusone) {
3787 var resource = section.resource;
3788 //keep url clean for matching and offline mode handling
3789 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3790 var $base = $('<a>')
3791 .addClass('card-bg')
3792 .attr('href', resource.url)
3793 .append($('<div>').addClass('card-section-icon')
3794 .append($('<div>').addClass('icon'))
3795 .append($('<div>').addClass('section').html(resource.title)))
3796 .appendTo(this);
3797
3798 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3799
3800 if (section.sections && section.sections.length) {
3801 // Recurse the section sub-tree to find a resource image.
3802 var stack = [section];
3803
3804 while (stack.length) {
3805 if (stack[0].resource.image) {
3806 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3807 break;
3808 }
3809
3810 if (stack[0].sections) {
3811 stack = stack.concat(stack[0].sections);
3812 }
3813
3814 stack.shift();
3815 }
3816
3817 var $ul = $('<ul>')
3818 .appendTo($cardInfo);
3819
3820 var max = section.sections.length > 3 ? 3 : section.sections.length;
3821
3822 for (var i = 0; i < max; ++i) {
3823
3824 var subResource = section.sections[i];
3825 if (!plusone) {
3826 $('<li>')
3827 .append($('<a>').attr('href', subResource.url)
3828 .append($('<div>').addClass('title').html(subResource.title))
3829 .append($('<div>').addClass('description ellipsis')
3830 .append($('<div>').addClass('text').html(subResource.summary))
3831 .append($('<div>').addClass('util'))))
3832 .appendTo($ul);
3833 } else {
3834 $('<li>')
3835 .append($('<a>').attr('href', subResource.url)
3836 .append($('<div>').addClass('title').html(subResource.title))
3837 .append($('<div>').addClass('description ellipsis')
3838 .append($('<div>').addClass('text').html(subResource.summary))
3839 .append($('<div>').addClass('util')
3840 .append($('<div>').addClass('g-plusone')
3841 .attr('data-size', 'small')
3842 .attr('data-align', 'right')
3843 .attr('data-href', resource.url)))))
3844 .appendTo($ul);
3845 }
3846 }
3847
3848 // Add a more row
3849 if (max < section.sections.length) {
3850 $('<li>')
3851 .append($('<a>').attr('href', resource.url)
3852 .append($('<div>')
3853 .addClass('title')
3854 .text('More')))
3855 .appendTo($ul);
3856 }
3857 } else {
3858 // No sub-resources, just render description?
3859 }
3860
3861 return this;
3862 };
3863})(jQuery);
3864/* Calculate the vertical area remaining */
3865(function($) {
3866 $.fn.ellipsisfade= function(lineHeight) {
3867 this.each(function() {
3868 // get element text
3869 var $this = $(this);
3870 var remainingHeight = $this.parent().parent().height();
3871 $this.parent().siblings().each(function ()
3872 {
3873 var h = $(this).height();
3874 remainingHeight = remainingHeight - h;
3875 });
3876
3877 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
3878 $this.parent().css({'height': adjustedRemainingHeight});
3879 $this.css({'height': "auto"});
3880 });
3881
3882 return this;
3883 };
3884}) (jQuery);