blob: d8d5732000a1c267334ed3cca752c3ba6eb6f250 [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
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700887var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -0700888var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700889var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -0700890/* 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
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700896/*
Dirk Doughertyc3921652014-05-13 16:55:26 -0700897 * Displays sticky nav bar on pages when dac header scrolls out of view
898 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700899$(window).scroll(function(event) {
900
901 setStickyTop();
902 var hiding = false;
903 var $stickyEl = $('#sticky-header');
904 var $menuEl = $('.menu-container');
905 // Exit if there's no sidenav
906 if ($('#side-nav').length == 0) return;
907 // Exit if the mouse target is a DIV, because that means the event is coming
908 // from a scrollable div and so there's no need to make adjustments to our layout
909 if ($(event.target).nodeName == "DIV") {
910 return;
911 }
912
913 var top = $(window).scrollTop();
914 // we set the navbar fixed when the scroll position is beyond the height of the site header...
915 var shouldBeSticky = top >= stickyTop;
916 // ... except if the document content is shorter than the sidenav height.
917 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
918 if ($("#doc-col").height() < $("#side-nav").height()) {
919 shouldBeSticky = false;
920 }
921
922 // Don't continue if the header is sufficently far away
923 // (to avoid intensive resizing that slows scrolling)
924 if (sticky == shouldBeSticky) {
925 return;
926 }
927 // Account for horizontal scroll
928 var scrollLeft = $(window).scrollLeft();
929 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
930 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
931 updateSideNavPosition();
932 prevScrollLeft = scrollLeft;
933 }
934
935 // If sticky header visible and position is now near top, hide sticky
936 if (sticky && !shouldBeSticky) {
937 sticky = false;
938 hiding = true;
939 // make the sidenav static again
940 $('#devdoc-nav')
941 .removeClass('fixed')
942 .css({'width':'auto','margin':''})
943 .prependTo('#side-nav');
944 // delay hide the sticky
945 $menuEl.removeClass('sticky-menu');
946 $stickyEl.fadeOut(250);
947 hiding = false;
948
949 // update the sidenaav position for side scrolling
950 updateSideNavPosition();
951 } else if (!sticky && shouldBeSticky) {
952 sticky = true;
953 $stickyEl.fadeIn(10);
954 $menuEl.addClass('sticky-menu');
955
956 // make the sidenav fixed
957 var width = $('#devdoc-nav').width();
958 $('#devdoc-nav')
959 .addClass('fixed')
960 .css({'width':width+'px'})
961 .prependTo('#body-content');
962
963 // update the sidenaav position for side scrolling
964 updateSideNavPosition();
965
966 } else if (hiding && top < 15) {
967 $menuEl.removeClass('sticky-menu');
968 $stickyEl.hide();
969 hiding = false;
970 }
971 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
972});
973
974/*
975 * Manages secion card states and nav resize to conclude loading
976 */
Dirk Doughertyc3921652014-05-13 16:55:26 -0700977(function() {
978 $(document).ready(function() {
979
Dirk Doughertyc3921652014-05-13 16:55:26 -0700980 // Stack hover states
981 $('.section-card-menu').each(function(index, el) {
982 var height = $(el).height();
983 $(el).css({height:height+'px', position:'relative'});
984 var $cardInfo = $(el).find('.card-info');
985
986 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
987 });
988
989 resizeNav(); // must resize once loading is finished
990 });
991
992})();
993
Scott Maine4d8f1b2012-06-21 18:03:05 -0700994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
Scott Maind7026f72013-06-17 15:08:49 -07001007/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001008
1009
1010
1011
1012
1013function toggle(obj, slide) {
1014 var ul = $("ul:first", obj);
1015 var li = ul.parent();
1016 if (li.hasClass("closed")) {
1017 if (slide) {
1018 ul.slideDown("fast");
1019 } else {
1020 ul.show();
1021 }
1022 li.removeClass("closed");
1023 li.addClass("open");
1024 $(".toggle-img", li).attr("title", "hide pages");
1025 } else {
1026 ul.slideUp("fast");
1027 li.removeClass("open");
1028 li.addClass("closed");
1029 $(".toggle-img", li).attr("title", "show pages");
1030 }
1031}
1032
1033
Scott Maine4d8f1b2012-06-21 18:03:05 -07001034function buildToggleLists() {
1035 $(".toggle-list").each(
1036 function(i) {
1037 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1038 $(this).addClass("closed");
1039 });
1040}
1041
1042
1043
Scott Maind7026f72013-06-17 15:08:49 -07001044function hideNestedItems(list, toggle) {
1045 $list = $(list);
1046 // hide nested lists
1047 if($list.hasClass('showing')) {
1048 $("li ol", $list).hide('fast');
1049 $list.removeClass('showing');
1050 // show nested lists
1051 } else {
1052 $("li ol", $list).show('fast');
1053 $list.addClass('showing');
1054 }
1055 $(".more,.less",$(toggle)).toggle();
1056}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085/* REFERENCE NAV SWAP */
1086
1087
1088function getNavPref() {
1089 var v = readCookie('reference_nav');
1090 if (v != NAV_PREF_TREE) {
1091 v = NAV_PREF_PANELS;
1092 }
1093 return v;
1094}
1095
1096function chooseDefaultNav() {
1097 nav_pref = getNavPref();
1098 if (nav_pref == NAV_PREF_TREE) {
1099 $("#nav-panels").toggle();
1100 $("#panel-link").toggle();
1101 $("#nav-tree").toggle();
1102 $("#tree-link").toggle();
1103 }
1104}
1105
1106function swapNav() {
1107 if (nav_pref == NAV_PREF_TREE) {
1108 nav_pref = NAV_PREF_PANELS;
1109 } else {
1110 nav_pref = NAV_PREF_TREE;
1111 init_default_navtree(toRoot);
1112 }
1113 var date = new Date();
1114 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1115 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1116
1117 $("#nav-panels").toggle();
1118 $("#panel-link").toggle();
1119 $("#nav-tree").toggle();
1120 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001121
Scott Maine4d8f1b2012-06-21 18:03:05 -07001122 resizeNav();
1123
1124 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1125 $("#nav-tree .jspContainer:visible")
1126 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1127 // Another nasty hack to make the scrollbar appear now that we have height
1128 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001129
Scott Maine4d8f1b2012-06-21 18:03:05 -07001130 if ($("#nav-tree").is(':visible')) {
1131 scrollIntoView("nav-tree");
1132 } else {
1133 scrollIntoView("packages-nav");
1134 scrollIntoView("classes-nav");
1135 }
1136}
1137
1138
1139
Scott Mainf5089842012-08-14 16:31:07 -07001140/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001141/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001142/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001143
1144function getBaseUri(uri) {
1145 var intlUrl = (uri.substring(0,6) == "/intl/");
1146 if (intlUrl) {
1147 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1148 base = base.substring(base.indexOf('/')+1, base.length);
1149 //alert("intl, returning base url: /" + base);
1150 return ("/" + base);
1151 } else {
1152 //alert("not intl, returning uri as found.");
1153 return uri;
1154 }
1155}
1156
1157function requestAppendHL(uri) {
1158//append "?hl=<lang> to an outgoing request (such as to blog)
1159 var lang = getLangPref();
1160 if (lang) {
1161 var q = 'hl=' + lang;
1162 uri += '?' + q;
1163 window.location = uri;
1164 return false;
1165 } else {
1166 return true;
1167 }
1168}
1169
1170
Scott Maine4d8f1b2012-06-21 18:03:05 -07001171function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001172 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1173 $links.each(function(i){ // for each link with a translation
1174 var $link = $(this);
1175 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1176 // put the desired language from the attribute as the text
1177 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001178 }
Scott Main6eb95f12012-10-02 17:12:23 -07001179 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001180}
1181
Scott Main015d6162013-01-29 09:01:52 -08001182function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001183 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001184 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001185 // keep this for 50 years
1186 //alert("expires: " + expires)
1187 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001188
1189 // ####### TODO: Remove this condition once we're stable on devsite #######
1190 // This condition is only needed if we still need to support legacy GAE server
1191 if (devsite) {
1192 // Switch language when on Devsite server
1193 if (submit) {
1194 $("#setlang").submit();
1195 }
1196 } else {
1197 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001198 if (submit) {
1199 window.location = getBaseUri(location.pathname);
1200 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001201 }
1202}
1203
1204function loadLangPref() {
1205 var lang = readCookie("pref_lang");
1206 if (lang != 0) {
1207 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1208 }
1209}
1210
1211function getLangPref() {
1212 var lang = $("#language").find(":selected").attr("value");
1213 if (!lang) {
1214 lang = readCookie("pref_lang");
1215 }
1216 return (lang != 0) ? lang : 'en';
1217}
1218
1219/* ########## END LOCALIZATION ############ */
1220
1221
1222
1223
1224
1225
1226/* Used to hide and reveal supplemental content, such as long code samples.
1227 See the companion CSS in android-developer-docs.css */
1228function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001229 var div = $(obj).closest(".toggle-content");
1230 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001231 if (div.hasClass("closed")) { // if it's closed, open it
1232 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001233 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001234 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001235 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001236 + "assets/images/triangle-opened.png");
1237 } else { // if it's open, close it
1238 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001239 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001240 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001241 div.find(".toggle-content").removeClass("open").addClass("closed")
1242 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001243 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001244 + "assets/images/triangle-closed.png");
1245 });
1246 }
1247 return false;
1248}
Scott Mainf5089842012-08-14 16:31:07 -07001249
1250
Scott Maindb3678b2012-10-23 14:13:41 -07001251/* New version of expandable content */
1252function toggleExpandable(link,id) {
1253 if($(id).is(':visible')) {
1254 $(id).slideUp();
1255 $(link).removeClass('expanded');
1256 } else {
1257 $(id).slideDown();
1258 $(link).addClass('expanded');
1259 }
1260}
1261
1262function hideExpandable(ids) {
1263 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001264 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001265}
1266
Scott Mainf5089842012-08-14 16:31:07 -07001267
1268
1269
1270
Scott Main3b90aff2013-08-01 18:09:35 -07001271/*
Scott Mainf5089842012-08-14 16:31:07 -07001272 * Slideshow 1.0
1273 * Used on /index.html and /develop/index.html for carousel
1274 *
1275 * Sample usage:
1276 * HTML -
1277 * <div class="slideshow-container">
1278 * <a href="" class="slideshow-prev">Prev</a>
1279 * <a href="" class="slideshow-next">Next</a>
1280 * <ul>
1281 * <li class="item"><img src="images/marquee1.jpg"></li>
1282 * <li class="item"><img src="images/marquee2.jpg"></li>
1283 * <li class="item"><img src="images/marquee3.jpg"></li>
1284 * <li class="item"><img src="images/marquee4.jpg"></li>
1285 * </ul>
1286 * </div>
1287 *
1288 * <script type="text/javascript">
1289 * $('.slideshow-container').dacSlideshow({
1290 * auto: true,
1291 * btnPrev: '.slideshow-prev',
1292 * btnNext: '.slideshow-next'
1293 * });
1294 * </script>
1295 *
1296 * Options:
1297 * btnPrev: optional identifier for previous button
1298 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001299 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001300 * auto: whether or not to auto-proceed
1301 * speed: animation speed
1302 * autoTime: time between auto-rotation
1303 * easing: easing function for transition
1304 * start: item to select by default
1305 * scroll: direction to scroll in
1306 * pagination: whether or not to include dotted pagination
1307 *
1308 */
1309
1310 (function($) {
1311 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001312
Scott Mainf5089842012-08-14 16:31:07 -07001313 //Options - see above
1314 o = $.extend({
1315 btnPrev: null,
1316 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001317 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001318 auto: true,
1319 speed: 500,
1320 autoTime: 12000,
1321 easing: null,
1322 start: 0,
1323 scroll: 1,
1324 pagination: true
1325
1326 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001327
1328 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001329 return this.each(function() {
1330
1331 var running = false;
1332 var animCss = o.vertical ? "top" : "left";
1333 var sizeCss = o.vertical ? "height" : "width";
1334 var div = $(this);
1335 var ul = $("ul", div);
1336 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001337 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001338 var timer = null;
1339
1340 var li = $("li", ul);
1341 var itemLength = li.size();
1342 var curr = o.start;
1343
1344 li.css({float: o.vertical ? "none" : "left"});
1345 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1346 div.css({position: "relative", "z-index": "2", left: "0px"});
1347
1348 var liSize = o.vertical ? height(li) : width(li);
1349 var ulSize = liSize * itemLength;
1350 var divSize = liSize;
1351
1352 li.css({width: li.width(), height: li.height()});
1353 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1354
1355 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001356
Scott Mainf5089842012-08-14 16:31:07 -07001357 //Pagination
1358 if (o.pagination) {
1359 var pagination = $("<div class='pagination'></div>");
1360 var pag_ul = $("<ul></ul>");
1361 if (tl > 1) {
1362 for (var i=0;i<tl;i++) {
1363 var li = $("<li>"+i+"</li>");
1364 pag_ul.append(li);
1365 if (i==o.start) li.addClass('active');
1366 li.click(function() {
1367 go(parseInt($(this).text()));
1368 })
1369 }
1370 pagination.append(pag_ul);
1371 div.append(pagination);
1372 }
1373 }
Scott Main3b90aff2013-08-01 18:09:35 -07001374
Scott Mainf5089842012-08-14 16:31:07 -07001375 //Previous button
1376 if(o.btnPrev)
1377 $(o.btnPrev).click(function(e) {
1378 e.preventDefault();
1379 return go(curr-o.scroll);
1380 });
1381
1382 //Next button
1383 if(o.btnNext)
1384 $(o.btnNext).click(function(e) {
1385 e.preventDefault();
1386 return go(curr+o.scroll);
1387 });
Scott Maineb410352013-01-14 19:03:40 -08001388
1389 //Pause button
1390 if(o.btnPause)
1391 $(o.btnPause).click(function(e) {
1392 e.preventDefault();
1393 if ($(this).hasClass('paused')) {
1394 startRotateTimer();
1395 } else {
1396 pauseRotateTimer();
1397 }
1398 });
Scott Main3b90aff2013-08-01 18:09:35 -07001399
Scott Mainf5089842012-08-14 16:31:07 -07001400 //Auto rotation
1401 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001402
Scott Mainf5089842012-08-14 16:31:07 -07001403 function startRotateTimer() {
1404 clearInterval(timer);
1405 timer = setInterval(function() {
1406 if (curr == tl-1) {
1407 go(0);
1408 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001409 go(curr+o.scroll);
1410 }
Scott Mainf5089842012-08-14 16:31:07 -07001411 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001412 $(o.btnPause).removeClass('paused');
1413 }
1414
1415 function pauseRotateTimer() {
1416 clearInterval(timer);
1417 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001418 }
1419
1420 //Go to an item
1421 function go(to) {
1422 if(!running) {
1423
1424 if(to<0) {
1425 to = itemLength-1;
1426 } else if (to>itemLength-1) {
1427 to = 0;
1428 }
1429 curr = to;
1430
1431 running = true;
1432
1433 ul.animate(
1434 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1435 function() {
1436 running = false;
1437 }
1438 );
1439
1440 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1441 $( (curr-o.scroll<0 && o.btnPrev)
1442 ||
1443 (curr+o.scroll > itemLength && o.btnNext)
1444 ||
1445 []
1446 ).addClass("disabled");
1447
Scott Main3b90aff2013-08-01 18:09:35 -07001448
Scott Mainf5089842012-08-14 16:31:07 -07001449 var nav_items = $('li', pagination);
1450 nav_items.removeClass('active');
1451 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001452
Scott Mainf5089842012-08-14 16:31:07 -07001453
1454 }
1455 if(o.auto) startRotateTimer();
1456 return false;
1457 };
1458 });
1459 };
1460
1461 function css(el, prop) {
1462 return parseInt($.css(el[0], prop)) || 0;
1463 };
1464 function width(el) {
1465 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1466 };
1467 function height(el) {
1468 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1469 };
1470
1471 })(jQuery);
1472
1473
Scott Main3b90aff2013-08-01 18:09:35 -07001474/*
Scott Mainf5089842012-08-14 16:31:07 -07001475 * dacSlideshow 1.0
1476 * Used on develop/index.html for side-sliding tabs
1477 *
1478 * Sample usage:
1479 * HTML -
1480 * <div class="slideshow-container">
1481 * <a href="" class="slideshow-prev">Prev</a>
1482 * <a href="" class="slideshow-next">Next</a>
1483 * <ul>
1484 * <li class="item"><img src="images/marquee1.jpg"></li>
1485 * <li class="item"><img src="images/marquee2.jpg"></li>
1486 * <li class="item"><img src="images/marquee3.jpg"></li>
1487 * <li class="item"><img src="images/marquee4.jpg"></li>
1488 * </ul>
1489 * </div>
1490 *
1491 * <script type="text/javascript">
1492 * $('.slideshow-container').dacSlideshow({
1493 * auto: true,
1494 * btnPrev: '.slideshow-prev',
1495 * btnNext: '.slideshow-next'
1496 * });
1497 * </script>
1498 *
1499 * Options:
1500 * btnPrev: optional identifier for previous button
1501 * btnNext: optional identifier for next button
1502 * auto: whether or not to auto-proceed
1503 * speed: animation speed
1504 * autoTime: time between auto-rotation
1505 * easing: easing function for transition
1506 * start: item to select by default
1507 * scroll: direction to scroll in
1508 * pagination: whether or not to include dotted pagination
1509 *
1510 */
1511 (function($) {
1512 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001513
Scott Mainf5089842012-08-14 16:31:07 -07001514 //Options - see above
1515 o = $.extend({
1516 speed : 250,
1517 easing: null,
1518 nav_id: null,
1519 frame_id: null
1520 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001521
1522 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001523 return this.each(function() {
1524
1525 var curr = 0;
1526 var running = false;
1527 var animCss = "margin-left";
1528 var sizeCss = "width";
1529 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001530
Scott Mainf5089842012-08-14 16:31:07 -07001531 var nav = $(o.nav_id, div);
1532 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001533 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001534 var frame = div.find(o.frame_id);
1535 var content_width = $(frame).find('ul').width();
1536 //Buttons
1537 $(nav_li).click(function(e) {
1538 go($(nav_li).index($(this)));
1539 })
Scott Main3b90aff2013-08-01 18:09:35 -07001540
Scott Mainf5089842012-08-14 16:31:07 -07001541 //Go to an item
1542 function go(to) {
1543 if(!running) {
1544 curr = to;
1545 running = true;
1546
1547 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1548 function() {
1549 running = false;
1550 }
1551 );
1552
Scott Main3b90aff2013-08-01 18:09:35 -07001553
Scott Mainf5089842012-08-14 16:31:07 -07001554 nav_li.removeClass('active');
1555 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001556
Scott Mainf5089842012-08-14 16:31:07 -07001557
1558 }
1559 return false;
1560 };
1561 });
1562 };
1563
1564 function css(el, prop) {
1565 return parseInt($.css(el[0], prop)) || 0;
1566 };
1567 function width(el) {
1568 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1569 };
1570 function height(el) {
1571 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1572 };
1573
1574 })(jQuery);
1575
1576
1577
1578
1579
1580/* ######################################################## */
1581/* ################ SEARCH SUGGESTIONS ################## */
1582/* ######################################################## */
1583
1584
Scott Main7e447ed2013-02-19 17:22:37 -08001585
Scott Main0e76e7e2013-03-12 10:24:07 -07001586var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1587var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1588
Scott Mainf5089842012-08-14 16:31:07 -07001589var gMatches = new Array();
1590var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001591var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001592var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1593var gListLength = 0;
1594
1595
1596var gGoogleMatches = new Array();
1597var ROW_COUNT_GOOGLE = 15; // max number of results in list
1598var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001599
Scott Main0e76e7e2013-03-12 10:24:07 -07001600var gDocsMatches = new Array();
1601var ROW_COUNT_DOCS = 100; // max number of results in list
1602var gDocsListLength = 0;
1603
Scott Mainde295272013-03-25 15:48:35 -07001604function onSuggestionClick(link) {
1605 // When user clicks a suggested document, track it
1606 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1607 'from: ' + $("#search_autocomplete").val()]);
1608}
1609
Scott Mainf5089842012-08-14 16:31:07 -07001610function set_item_selected($li, selected)
1611{
1612 if (selected) {
1613 $li.attr('class','jd-autocomplete jd-selected');
1614 } else {
1615 $li.attr('class','jd-autocomplete');
1616 }
1617}
1618
1619function set_item_values(toroot, $li, match)
1620{
1621 var $link = $('a',$li);
1622 $link.html(match.__hilabel || match.label);
1623 $link.attr('href',toroot + match.link);
1624}
1625
Scott Main719acb42013-12-05 16:05:09 -08001626function set_item_values_jd(toroot, $li, match)
1627{
1628 var $link = $('a',$li);
1629 $link.html(match.title);
1630 $link.attr('href',toroot + match.url);
1631}
1632
Scott Main0e76e7e2013-03-12 10:24:07 -07001633function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001634 var $li = $("<li class='jd-autocomplete'></li>");
1635 $list.append($li);
1636
1637 $li.mousedown(function() {
1638 window.location = this.firstChild.getAttribute("href");
1639 });
1640 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001641 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001642 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001643 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1644 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001645 });
Scott Mainde295272013-03-25 15:48:35 -07001646 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001647 $li.attr('class','show-item');
1648 return $li;
1649}
1650
Scott Mainf5089842012-08-14 16:31:07 -07001651function sync_selection_table(toroot)
1652{
Scott Mainf5089842012-08-14 16:31:07 -07001653 var $li; //list item jquery object
1654 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001655
Scott Main0e76e7e2013-03-12 10:24:07 -07001656 // if there are NO results at all, hide all columns
1657 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1658 $('.suggest-card').hide(300);
1659 return;
1660 }
1661
1662 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001663 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001664 // reveal suggestion list
1665 $('.suggest-card.dummy').show();
1666 $('.suggest-card.reference').show();
1667 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001668
Scott Main0e76e7e2013-03-12 10:24:07 -07001669 // reset the lists
1670 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001671
Scott Main0e76e7e2013-03-12 10:24:07 -07001672 // ########### ANDROID RESULTS #############
1673 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001674
Scott Main0e76e7e2013-03-12 10:24:07 -07001675 // determine android results to show
1676 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1677 gMatches.length : ROW_COUNT_FRAMEWORK;
1678 for (i=0; i<gListLength; i++) {
1679 var $li = new_suggestion($(".suggest-card.reference ul"));
1680 set_item_values(toroot, $li, gMatches[i]);
1681 set_item_selected($li, i == gSelectedIndex);
1682 }
1683 }
Scott Main7e447ed2013-02-19 17:22:37 -08001684
Scott Main0e76e7e2013-03-12 10:24:07 -07001685 // ########### GOOGLE RESULTS #############
1686 if (gGoogleMatches.length > 0) {
1687 // show header for list
1688 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001689
Scott Main0e76e7e2013-03-12 10:24:07 -07001690 // determine google results to show
1691 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1692 for (i=0; i<gGoogleListLength; i++) {
1693 var $li = new_suggestion($(".suggest-card.reference ul"));
1694 set_item_values(toroot, $li, gGoogleMatches[i]);
1695 set_item_selected($li, i == gSelectedIndex);
1696 }
1697 }
Scott Mainf5089842012-08-14 16:31:07 -07001698 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001699 $('.suggest-card.reference').hide();
1700 $('.suggest-card.dummy').hide();
1701 }
1702
1703 // ########### JD DOC RESULTS #############
1704 if (gDocsMatches.length > 0) {
1705 // reset the lists
1706 $(".search_filtered_wrapper.docs li").remove();
1707
1708 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001709 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1710 // The order must match the reverse order that each section appears as a card in
1711 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001712 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1713 for (i=0; i<gDocsListLength; i++) {
1714 var sugg = gDocsMatches[i];
1715 var $li;
1716 if (sugg.type == "design") {
1717 $li = new_suggestion($(".suggest-card.design ul"));
1718 } else
1719 if (sugg.type == "distribute") {
1720 $li = new_suggestion($(".suggest-card.distribute ul"));
1721 } else
Scott Main719acb42013-12-05 16:05:09 -08001722 if (sugg.type == "samples") {
1723 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1724 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001725 if (sugg.type == "training") {
1726 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1727 } else
Scott Main719acb42013-12-05 16:05:09 -08001728 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001729 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1730 } else {
1731 continue;
1732 }
1733
Scott Main719acb42013-12-05 16:05:09 -08001734 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001735 set_item_selected($li, i == gSelectedIndex);
1736 }
1737
1738 // add heading and show or hide card
1739 if ($(".suggest-card.design li").length > 0) {
1740 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1741 $(".suggest-card.design").show(300);
1742 } else {
1743 $('.suggest-card.design').hide(300);
1744 }
1745 if ($(".suggest-card.distribute li").length > 0) {
1746 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1747 $(".suggest-card.distribute").show(300);
1748 } else {
1749 $('.suggest-card.distribute').hide(300);
1750 }
1751 if ($(".child-card.guides li").length > 0) {
1752 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1753 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1754 }
1755 if ($(".child-card.training li").length > 0) {
1756 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1757 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1758 }
Scott Main719acb42013-12-05 16:05:09 -08001759 if ($(".child-card.samples li").length > 0) {
1760 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1761 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1762 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001763
1764 if ($(".suggest-card.develop li").length > 0) {
1765 $(".suggest-card.develop").show(300);
1766 } else {
1767 $('.suggest-card.develop').hide(300);
1768 }
1769
1770 } else {
1771 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001772 }
1773}
1774
Scott Main0e76e7e2013-03-12 10:24:07 -07001775/** Called by the search input's onkeydown and onkeyup events.
1776 * Handles navigation with keyboard arrows, Enter key to invoke search,
1777 * otherwise invokes search suggestions on key-up event.
1778 * @param e The JS event
1779 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001780 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001781 * @returns True if the event should bubble up
1782 */
Scott Mainf5089842012-08-14 16:31:07 -07001783function search_changed(e, kd, toroot)
1784{
Scott Main719acb42013-12-05 16:05:09 -08001785 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001786 var search = document.getElementById("search_autocomplete");
1787 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001788 // get the ul hosting the currently selected item
1789 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1790 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1791 var $selectedUl = $columns[gSelectedColumn];
1792
Scott Mainf5089842012-08-14 16:31:07 -07001793 // show/hide the close button
1794 if (text != '') {
1795 $(".search .close").removeClass("hide");
1796 } else {
1797 $(".search .close").addClass("hide");
1798 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001799 // 27 = esc
1800 if (e.keyCode == 27) {
1801 // close all search results
1802 if (kd) $('.search .close').trigger('click');
1803 return true;
1804 }
Scott Mainf5089842012-08-14 16:31:07 -07001805 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001806 else if (e.keyCode == 13) {
1807 if (gSelectedIndex < 0) {
1808 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001809 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1810 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001811 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001812 return true;
1813 } else {
1814 // otherwise, results are already showing, so allow ajax to auto refresh the results
1815 // and ignore this Enter press to avoid the reload.
1816 return false;
1817 }
1818 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001819 // click the link corresponding to selected item
1820 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001821 return false;
1822 }
1823 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001824 // Stop here if Google results are showing
1825 else if ($("#searchResults").is(":visible")) {
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001826 //If search_results is scrolled out of view, scroll to top on input
1827 if ((sticky ) && (search.value != "")) {
1828 $('body,html').animate({scrollTop:0}, '500', 'swing');
1829 }
1830 // if results aren't showing (and text not empty), return true to allow search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001831 return true;
1832 }
1833 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001834 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001835 // if the next item is a header, skip it
1836 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001837 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001838 }
1839 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001840 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001841 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001842 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1843 // If user reaches top, reset selected column
1844 if (gSelectedIndex < 0) {
1845 gSelectedColumn = -1;
1846 }
Scott Mainf5089842012-08-14 16:31:07 -07001847 }
1848 return false;
1849 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001850 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001851 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001852 // if the next item is a header, skip it
1853 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001854 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001855 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001856 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1857 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1858 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001859 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001860 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001861 }
1862 return false;
1863 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001864 // Consider left/right arrow navigation
1865 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1866 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1867 // 37 LEFT ARROW
1868 // go left only if current column is not left-most column (last column)
1869 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1870 $('li', $selectedUl).removeClass('jd-selected');
1871 gSelectedColumn++;
1872 $selectedUl = $columns[gSelectedColumn];
1873 // keep or reset the selected item to last item as appropriate
1874 gSelectedIndex = gSelectedIndex >
1875 $("li", $selectedUl).length-1 ?
1876 $("li", $selectedUl).length-1 : gSelectedIndex;
1877 // if the corresponding item is a header, move down
1878 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1879 gSelectedIndex++;
1880 }
Scott Main3b90aff2013-08-01 18:09:35 -07001881 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001882 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1883 return false;
1884 }
1885 // 39 RIGHT ARROW
1886 // go right only if current column is not the right-most column (first column)
1887 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1888 $('li', $selectedUl).removeClass('jd-selected');
1889 gSelectedColumn--;
1890 $selectedUl = $columns[gSelectedColumn];
1891 // keep or reset the selected item to last item as appropriate
1892 gSelectedIndex = gSelectedIndex >
1893 $("li", $selectedUl).length-1 ?
1894 $("li", $selectedUl).length-1 : gSelectedIndex;
1895 // if the corresponding item is a header, move down
1896 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1897 gSelectedIndex++;
1898 }
Scott Main3b90aff2013-08-01 18:09:35 -07001899 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001900 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1901 return false;
1902 }
1903 }
1904
Scott Main719acb42013-12-05 16:05:09 -08001905 // if key-up event and not arrow down/up/left/right,
1906 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001907 else if (!kd && (e.keyCode != 40)
1908 && (e.keyCode != 38)
1909 && (e.keyCode != 37)
1910 && (e.keyCode != 39)) {
1911 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001912 gMatches = new Array();
1913 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001914 gGoogleMatches = new Array();
1915 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001916 gDocsMatches = new Array();
1917 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001918
1919 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001920 for (var i=0; i<DATA.length; i++) {
1921 var s = DATA[i];
1922 if (text.length != 0 &&
1923 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1924 gMatches[matchedCount] = s;
1925 matchedCount++;
1926 }
1927 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001928 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001929 for (var i=0; i<gMatches.length; i++) {
1930 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001931 }
1932
1933
1934 // Search for Google matches
1935 for (var i=0; i<GOOGLE_DATA.length; i++) {
1936 var s = GOOGLE_DATA[i];
1937 if (text.length != 0 &&
1938 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1939 gGoogleMatches[matchedCountGoogle] = s;
1940 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001941 }
1942 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001943 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001944 for (var i=0; i<gGoogleMatches.length; i++) {
1945 var s = gGoogleMatches[i];
1946 }
1947
Scott Mainf5089842012-08-14 16:31:07 -07001948 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001949
1950
1951
Scott Main719acb42013-12-05 16:05:09 -08001952 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07001953 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08001954 // Regex to match only the beginning of a word
1955 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1956
1957
1958 // Search for Training classes
1959 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001960 // current search comparison, with counters for tag and title,
1961 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08001962 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07001963 s.matched_tag = 0;
1964 s.matched_title = 0;
1965 var matched = false;
1966
1967 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08001968 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001969 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08001970 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001971 matched = true;
1972 s.matched_tag = j + 1; // add 1 to index position
1973 }
1974 }
Scott Main719acb42013-12-05 16:05:09 -08001975 // Don't consider doc title for lessons (only for class landing pages),
1976 // unless the lesson has a tag that already matches
1977 if ((s.lang == currentLang) &&
1978 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001979 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08001980 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001981 matched = true;
1982 s.matched_title = 1;
1983 }
1984 }
1985 if (matched) {
1986 gDocsMatches[matchedCountDocs] = s;
1987 matchedCountDocs++;
1988 }
1989 }
Scott Main719acb42013-12-05 16:05:09 -08001990
1991
1992 // Search for API Guides
1993 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
1994 // current search comparison, with counters for tag and title,
1995 // used later to improve ranking
1996 var s = GUIDE_RESOURCES[i];
1997 s.matched_tag = 0;
1998 s.matched_title = 0;
1999 var matched = false;
2000
2001 // Check if query matches any tags; work backwards toward 1 to assist ranking
2002 for (var j = s.keywords.length - 1; j >= 0; j--) {
2003 // it matches a tag
2004 if (s.keywords[j].toLowerCase().match(textRegex)) {
2005 matched = true;
2006 s.matched_tag = j + 1; // add 1 to index position
2007 }
2008 }
2009 // Check if query matches the doc title, but only for current language
2010 if (s.lang == currentLang) {
2011 // if query matches the doc title
2012 if (s.title.toLowerCase().match(textRegex)) {
2013 matched = true;
2014 s.matched_title = 1;
2015 }
2016 }
2017 if (matched) {
2018 gDocsMatches[matchedCountDocs] = s;
2019 matchedCountDocs++;
2020 }
2021 }
2022
2023
2024 // Search for Tools Guides
2025 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2026 // current search comparison, with counters for tag and title,
2027 // used later to improve ranking
2028 var s = TOOLS_RESOURCES[i];
2029 s.matched_tag = 0;
2030 s.matched_title = 0;
2031 var matched = false;
2032
2033 // Check if query matches any tags; work backwards toward 1 to assist ranking
2034 for (var j = s.keywords.length - 1; j >= 0; j--) {
2035 // it matches a tag
2036 if (s.keywords[j].toLowerCase().match(textRegex)) {
2037 matched = true;
2038 s.matched_tag = j + 1; // add 1 to index position
2039 }
2040 }
2041 // Check if query matches the doc title, but only for current language
2042 if (s.lang == currentLang) {
2043 // if query matches the doc title
2044 if (s.title.toLowerCase().match(textRegex)) {
2045 matched = true;
2046 s.matched_title = 1;
2047 }
2048 }
2049 if (matched) {
2050 gDocsMatches[matchedCountDocs] = s;
2051 matchedCountDocs++;
2052 }
2053 }
2054
2055
2056 // Search for About docs
2057 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2058 // current search comparison, with counters for tag and title,
2059 // used later to improve ranking
2060 var s = ABOUT_RESOURCES[i];
2061 s.matched_tag = 0;
2062 s.matched_title = 0;
2063 var matched = false;
2064
2065 // Check if query matches any tags; work backwards toward 1 to assist ranking
2066 for (var j = s.keywords.length - 1; j >= 0; j--) {
2067 // it matches a tag
2068 if (s.keywords[j].toLowerCase().match(textRegex)) {
2069 matched = true;
2070 s.matched_tag = j + 1; // add 1 to index position
2071 }
2072 }
2073 // Check if query matches the doc title, but only for current language
2074 if (s.lang == currentLang) {
2075 // if query matches the doc title
2076 if (s.title.toLowerCase().match(textRegex)) {
2077 matched = true;
2078 s.matched_title = 1;
2079 }
2080 }
2081 if (matched) {
2082 gDocsMatches[matchedCountDocs] = s;
2083 matchedCountDocs++;
2084 }
2085 }
2086
2087
2088 // Search for Design guides
2089 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2090 // current search comparison, with counters for tag and title,
2091 // used later to improve ranking
2092 var s = DESIGN_RESOURCES[i];
2093 s.matched_tag = 0;
2094 s.matched_title = 0;
2095 var matched = false;
2096
2097 // Check if query matches any tags; work backwards toward 1 to assist ranking
2098 for (var j = s.keywords.length - 1; j >= 0; j--) {
2099 // it matches a tag
2100 if (s.keywords[j].toLowerCase().match(textRegex)) {
2101 matched = true;
2102 s.matched_tag = j + 1; // add 1 to index position
2103 }
2104 }
2105 // Check if query matches the doc title, but only for current language
2106 if (s.lang == currentLang) {
2107 // if query matches the doc title
2108 if (s.title.toLowerCase().match(textRegex)) {
2109 matched = true;
2110 s.matched_title = 1;
2111 }
2112 }
2113 if (matched) {
2114 gDocsMatches[matchedCountDocs] = s;
2115 matchedCountDocs++;
2116 }
2117 }
2118
2119
2120 // Search for Distribute guides
2121 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2122 // current search comparison, with counters for tag and title,
2123 // used later to improve ranking
2124 var s = DISTRIBUTE_RESOURCES[i];
2125 s.matched_tag = 0;
2126 s.matched_title = 0;
2127 var matched = false;
2128
2129 // Check if query matches any tags; work backwards toward 1 to assist ranking
2130 for (var j = s.keywords.length - 1; j >= 0; j--) {
2131 // it matches a tag
2132 if (s.keywords[j].toLowerCase().match(textRegex)) {
2133 matched = true;
2134 s.matched_tag = j + 1; // add 1 to index position
2135 }
2136 }
2137 // Check if query matches the doc title, but only for current language
2138 if (s.lang == currentLang) {
2139 // if query matches the doc title
2140 if (s.title.toLowerCase().match(textRegex)) {
2141 matched = true;
2142 s.matched_title = 1;
2143 }
2144 }
2145 if (matched) {
2146 gDocsMatches[matchedCountDocs] = s;
2147 matchedCountDocs++;
2148 }
2149 }
2150
2151
2152 // Search for Google guides
2153 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2154 // current search comparison, with counters for tag and title,
2155 // used later to improve ranking
2156 var s = GOOGLE_RESOURCES[i];
2157 s.matched_tag = 0;
2158 s.matched_title = 0;
2159 var matched = false;
2160
2161 // Check if query matches any tags; work backwards toward 1 to assist ranking
2162 for (var j = s.keywords.length - 1; j >= 0; j--) {
2163 // it matches a tag
2164 if (s.keywords[j].toLowerCase().match(textRegex)) {
2165 matched = true;
2166 s.matched_tag = j + 1; // add 1 to index position
2167 }
2168 }
2169 // Check if query matches the doc title, but only for current language
2170 if (s.lang == currentLang) {
2171 // if query matches the doc title
2172 if (s.title.toLowerCase().match(textRegex)) {
2173 matched = true;
2174 s.matched_title = 1;
2175 }
2176 }
2177 if (matched) {
2178 gDocsMatches[matchedCountDocs] = s;
2179 matchedCountDocs++;
2180 }
2181 }
2182
2183
2184 // Search for Samples
2185 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2186 // current search comparison, with counters for tag and title,
2187 // used later to improve ranking
2188 var s = SAMPLES_RESOURCES[i];
2189 s.matched_tag = 0;
2190 s.matched_title = 0;
2191 var matched = false;
2192 // Check if query matches any tags; work backwards toward 1 to assist ranking
2193 for (var j = s.keywords.length - 1; j >= 0; j--) {
2194 // it matches a tag
2195 if (s.keywords[j].toLowerCase().match(textRegex)) {
2196 matched = true;
2197 s.matched_tag = j + 1; // add 1 to index position
2198 }
2199 }
2200 // Check if query matches the doc title, but only for current language
2201 if (s.lang == currentLang) {
2202 // if query matches the doc title.t
2203 if (s.title.toLowerCase().match(textRegex)) {
2204 matched = true;
2205 s.matched_title = 1;
2206 }
2207 }
2208 if (matched) {
2209 gDocsMatches[matchedCountDocs] = s;
2210 matchedCountDocs++;
2211 }
2212 }
2213
2214 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002215 rank_autocomplete_doc_results(text, gDocsMatches);
2216 }
2217
2218 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002219 sync_selection_table(toroot);
2220 return true; // allow the event to bubble up to the search api
2221 }
2222}
2223
Scott Main0e76e7e2013-03-12 10:24:07 -07002224/* Order the jd doc result list based on match quality */
2225function rank_autocomplete_doc_results(query, matches) {
2226 query = query || '';
2227 if (!matches || !matches.length)
2228 return;
2229
2230 var _resultScoreFn = function(match) {
2231 var score = 1.0;
2232
2233 // if the query matched a tag
2234 if (match.matched_tag > 0) {
2235 // multiply score by factor relative to position in tags list (max of 3)
2236 score *= 3 / match.matched_tag;
2237
2238 // if it also matched the title
2239 if (match.matched_title > 0) {
2240 score *= 2;
2241 }
2242 } else if (match.matched_title > 0) {
2243 score *= 3;
2244 }
2245
2246 return score;
2247 };
2248
2249 for (var i=0; i<matches.length; i++) {
2250 matches[i].__resultScore = _resultScoreFn(matches[i]);
2251 }
2252
2253 matches.sort(function(a,b){
2254 var n = b.__resultScore - a.__resultScore;
2255 if (n == 0) // lexicographical sort if scores are the same
2256 n = (a.label < b.label) ? -1 : 1;
2257 return n;
2258 });
2259}
2260
Scott Main7e447ed2013-02-19 17:22:37 -08002261/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002262function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002263 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002264 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002265 return;
2266
2267 // helper function that gets the last occurence index of the given regex
2268 // in the given string, or -1 if not found
2269 var _lastSearch = function(s, re) {
2270 if (s == '')
2271 return -1;
2272 var l = -1;
2273 var tmp;
2274 while ((tmp = s.search(re)) >= 0) {
2275 if (l < 0) l = 0;
2276 l += tmp;
2277 s = s.substr(tmp + 1);
2278 }
2279 return l;
2280 };
2281
2282 // helper function that counts the occurrences of a given character in
2283 // a given string
2284 var _countChar = function(s, c) {
2285 var n = 0;
2286 for (var i=0; i<s.length; i++)
2287 if (s.charAt(i) == c) ++n;
2288 return n;
2289 };
2290
2291 var queryLower = query.toLowerCase();
2292 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2293 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2294 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2295
2296 var _resultScoreFn = function(result) {
2297 // scores are calculated based on exact and prefix matches,
2298 // and then number of path separators (dots) from the last
2299 // match (i.e. favoring classes and deep package names)
2300 var score = 1.0;
2301 var labelLower = result.label.toLowerCase();
2302 var t;
2303 t = _lastSearch(labelLower, partExactAlnumRE);
2304 if (t >= 0) {
2305 // exact part match
2306 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2307 score *= 200 / (partsAfter + 1);
2308 } else {
2309 t = _lastSearch(labelLower, partPrefixAlnumRE);
2310 if (t >= 0) {
2311 // part prefix match
2312 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2313 score *= 20 / (partsAfter + 1);
2314 }
2315 }
2316
2317 return score;
2318 };
2319
Scott Main7e447ed2013-02-19 17:22:37 -08002320 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002321 // if the API is deprecated, default score is 0; otherwise, perform scoring
2322 if (matches[i].deprecated == "true") {
2323 matches[i].__resultScore = 0;
2324 } else {
2325 matches[i].__resultScore = _resultScoreFn(matches[i]);
2326 }
Scott Mainf5089842012-08-14 16:31:07 -07002327 }
2328
Scott Main7e447ed2013-02-19 17:22:37 -08002329 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002330 var n = b.__resultScore - a.__resultScore;
2331 if (n == 0) // lexicographical sort if scores are the same
2332 n = (a.label < b.label) ? -1 : 1;
2333 return n;
2334 });
2335}
2336
Scott Main7e447ed2013-02-19 17:22:37 -08002337/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002338function highlight_autocomplete_result_labels(query) {
2339 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002340 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002341 return;
2342
2343 var queryLower = query.toLowerCase();
2344 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2345 var queryRE = new RegExp(
2346 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2347 for (var i=0; i<gMatches.length; i++) {
2348 gMatches[i].__hilabel = gMatches[i].label.replace(
2349 queryRE, '<b>$1</b>');
2350 }
Scott Main7e447ed2013-02-19 17:22:37 -08002351 for (var i=0; i<gGoogleMatches.length; i++) {
2352 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2353 queryRE, '<b>$1</b>');
2354 }
Scott Mainf5089842012-08-14 16:31:07 -07002355}
2356
2357function search_focus_changed(obj, focused)
2358{
Scott Main3b90aff2013-08-01 18:09:35 -07002359 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002360 if(obj.value == ""){
2361 $(".search .close").addClass("hide");
2362 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002363 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002364 }
2365}
2366
2367function submit_search() {
2368 var query = document.getElementById('search_autocomplete').value;
2369 location.hash = 'q=' + query;
2370 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002371 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002372 return false;
2373}
2374
2375
2376function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002377 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002378 $(".search .close").addClass("hide");
2379 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002380
Scott Mainf5089842012-08-14 16:31:07 -07002381 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002382
Scott Mainf5089842012-08-14 16:31:07 -07002383 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2384 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002385
2386 // forcefully regain key-up event control (previously jacked by search api)
2387 $("#search_autocomplete").keyup(function(event) {
2388 return search_changed(event, false, toRoot);
2389 });
2390
Scott Mainf5089842012-08-14 16:31:07 -07002391 return false;
2392}
2393
2394
2395
2396/* ########################################################## */
2397/* ################ CUSTOM SEARCH ENGINE ################## */
2398/* ########################################################## */
2399
Scott Mainf5089842012-08-14 16:31:07 -07002400var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002401google.load('search', '1', {"callback" : function() {
2402 searchControl = new google.search.SearchControl();
2403 } });
Scott Mainf5089842012-08-14 16:31:07 -07002404
2405function loadSearchResults() {
2406 document.getElementById("search_autocomplete").style.color = "#000";
2407
Scott Mainf5089842012-08-14 16:31:07 -07002408 searchControl = new google.search.SearchControl();
2409
2410 // use our existing search form and use tabs when multiple searchers are used
2411 drawOptions = new google.search.DrawOptions();
2412 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2413 drawOptions.setInput(document.getElementById("search_autocomplete"));
2414
2415 // configure search result options
2416 searchOptions = new google.search.SearcherOptions();
2417 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2418
2419 // configure each of the searchers, for each tab
2420 devSiteSearcher = new google.search.WebSearch();
2421 devSiteSearcher.setUserDefinedLabel("All");
2422 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2423
2424 designSearcher = new google.search.WebSearch();
2425 designSearcher.setUserDefinedLabel("Design");
2426 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2427
2428 trainingSearcher = new google.search.WebSearch();
2429 trainingSearcher.setUserDefinedLabel("Training");
2430 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2431
2432 guidesSearcher = new google.search.WebSearch();
2433 guidesSearcher.setUserDefinedLabel("Guides");
2434 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2435
2436 referenceSearcher = new google.search.WebSearch();
2437 referenceSearcher.setUserDefinedLabel("Reference");
2438 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2439
Scott Maindf08ada2012-12-03 08:54:37 -08002440 googleSearcher = new google.search.WebSearch();
2441 googleSearcher.setUserDefinedLabel("Google Services");
2442 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2443
Scott Mainf5089842012-08-14 16:31:07 -07002444 blogSearcher = new google.search.WebSearch();
2445 blogSearcher.setUserDefinedLabel("Blog");
2446 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2447
2448 // add each searcher to the search control
2449 searchControl.addSearcher(devSiteSearcher, searchOptions);
2450 searchControl.addSearcher(designSearcher, searchOptions);
2451 searchControl.addSearcher(trainingSearcher, searchOptions);
2452 searchControl.addSearcher(guidesSearcher, searchOptions);
2453 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002454 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002455 searchControl.addSearcher(blogSearcher, searchOptions);
2456
2457 // configure result options
2458 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2459 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2460 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2461 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2462
2463 // upon ajax search, refresh the url and search title
2464 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2465 updateResultTitle(query);
2466 var query = document.getElementById('search_autocomplete').value;
2467 location.hash = 'q=' + query;
2468 });
2469
Scott Mainde295272013-03-25 15:48:35 -07002470 // once search results load, set up click listeners
2471 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2472 addResultClickListeners();
2473 });
2474
Scott Mainf5089842012-08-14 16:31:07 -07002475 // draw the search results box
2476 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2477
2478 // get query and execute the search
2479 searchControl.execute(decodeURI(getQuery(location.hash)));
2480
2481 document.getElementById("search_autocomplete").focus();
2482 addTabListeners();
2483}
2484// End of loadSearchResults
2485
2486
2487google.setOnLoadCallback(function(){
2488 if (location.hash.indexOf("q=") == -1) {
2489 // if there's no query in the url, don't search and make sure results are hidden
2490 $('#searchResults').hide();
2491 return;
2492 } else {
2493 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002494 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002495 $(".search .close").removeClass("hide");
2496 loadSearchResults();
2497 }
2498}, true);
2499
2500// when an event on the browser history occurs (back, forward, load) requery hash and do search
2501$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002502 // If the hash isn't a search query or there's an error in the query,
2503 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002504 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2505 // If the results pane is open, close it.
2506 if (!$("#searchResults").is(":hidden")) {
2507 hideResults();
2508 }
2509 return;
2510 }
2511
2512 // Otherwise, we have a search to do
2513 var query = decodeURI(getQuery(location.hash));
2514 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002515 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002516 $("#search_autocomplete").focus();
2517 $(".search .close").removeClass("hide");
2518
2519 updateResultTitle(query);
2520});
2521
2522function updateResultTitle(query) {
2523 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2524}
2525
2526// forcefully regain key-up event control (previously jacked by search api)
2527$("#search_autocomplete").keyup(function(event) {
2528 return search_changed(event, false, toRoot);
2529});
2530
2531// add event listeners to each tab so we can track the browser history
2532function addTabListeners() {
2533 var tabHeaders = $(".gsc-tabHeader");
2534 for (var i = 0; i < tabHeaders.length; i++) {
2535 $(tabHeaders[i]).attr("id",i).click(function() {
2536 /*
2537 // make a copy of the page numbers for the search left pane
2538 setTimeout(function() {
2539 // remove any residual page numbers
2540 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002541 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002542 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002543 // and because we're going to remove it (previous line),
2544 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002545 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2546 .clone().appendTo('#searchResults .gsc-tabsArea');
2547 }, 200);
2548 */
2549 });
2550 }
2551 setTimeout(function(){$(tabHeaders[0]).click()},200);
2552}
2553
Scott Mainde295272013-03-25 15:48:35 -07002554// add analytics tracking events to each result link
2555function addResultClickListeners() {
2556 $("#searchResults a.gs-title").each(function(index, link) {
2557 // When user clicks enter for Google search results, track it
2558 $(link).click(function() {
2559 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2560 'from: ' + $("#search_autocomplete").val()]);
2561 });
2562 });
2563}
2564
Scott Mainf5089842012-08-14 16:31:07 -07002565
2566function getQuery(hash) {
2567 var queryParts = hash.split('=');
2568 return queryParts[1];
2569}
2570
2571/* returns the given string with all HTML brackets converted to entities
2572 TODO: move this to the site's JS library */
2573function escapeHTML(string) {
2574 return string.replace(/</g,"&lt;")
2575 .replace(/>/g,"&gt;");
2576}
2577
2578
2579
2580
2581
2582
2583
2584/* ######################################################## */
2585/* ################# JAVADOC REFERENCE ################### */
2586/* ######################################################## */
2587
Scott Main65511c02012-09-07 15:51:32 -07002588/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002589if (location.pathname.indexOf("/reference") == 0) {
2590 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2591 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2592 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002593 $(document).ready(function() {
2594 // init available apis based on user pref
2595 changeApiLevel();
2596 initSidenavHeightResize()
2597 });
2598 }
Scott Main65511c02012-09-07 15:51:32 -07002599}
Scott Mainf5089842012-08-14 16:31:07 -07002600
2601var API_LEVEL_COOKIE = "api_level";
2602var minLevel = 1;
2603var maxLevel = 1;
2604
2605/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002606
Scott Mainf5089842012-08-14 16:31:07 -07002607 function initSidenavHeightResize() {
2608 // Change the drag bar size to nicely fit the scrollbar positions
2609 var $dragBar = $(".ui-resizable-s");
2610 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002611
2612 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002613 containment: "#nav-panels",
2614 handles: "s",
2615 alsoResize: "#packages-nav",
2616 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2617 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2618 });
Scott Main3b90aff2013-08-01 18:09:35 -07002619
Scott Mainf5089842012-08-14 16:31:07 -07002620 }
Scott Main3b90aff2013-08-01 18:09:35 -07002621
Scott Mainf5089842012-08-14 16:31:07 -07002622function updateSidenavFixedWidth() {
2623 if (!navBarIsFixed) return;
2624 $('#devdoc-nav').css({
2625 'width' : $('#side-nav').css('width'),
2626 'margin' : $('#side-nav').css('margin')
2627 });
2628 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002629
Scott Mainf5089842012-08-14 16:31:07 -07002630 initSidenavHeightResize();
2631}
2632
2633function updateSidenavFullscreenWidth() {
2634 if (!navBarIsFixed) return;
2635 $('#devdoc-nav').css({
2636 'width' : $('#side-nav').css('width'),
2637 'margin' : $('#side-nav').css('margin')
2638 });
2639 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002640
Scott Mainf5089842012-08-14 16:31:07 -07002641 initSidenavHeightResize();
2642}
2643
2644function buildApiLevelSelector() {
2645 maxLevel = SINCE_DATA.length;
2646 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2647 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2648
2649 minLevel = parseInt($("#doc-api-level").attr("class"));
2650 // Handle provisional api levels; the provisional level will always be the highest possible level
2651 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2652 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2653 if (isNaN(minLevel) && minLevel.length) {
2654 minLevel = maxLevel;
2655 }
2656 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2657 for (var i = maxLevel-1; i >= 0; i--) {
2658 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2659 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2660 select.append(option);
2661 }
2662
2663 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2664 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2665 selectedLevelItem.setAttribute('selected',true);
2666}
2667
2668function changeApiLevel() {
2669 maxLevel = SINCE_DATA.length;
2670 var selectedLevel = maxLevel;
2671
2672 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2673 toggleVisisbleApis(selectedLevel, "body");
2674
2675 var date = new Date();
2676 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2677 var expiration = date.toGMTString();
2678 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2679
2680 if (selectedLevel < minLevel) {
2681 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002682 $("#naMessage").show().html("<div><p><strong>This " + thing
2683 + " requires API level " + minLevel + " or higher.</strong></p>"
2684 + "<p>This document is hidden because your selected API level for the documentation is "
2685 + selectedLevel + ". You can change the documentation API level with the selector "
2686 + "above the left navigation.</p>"
2687 + "<p>For more information about specifying the API level your app requires, "
2688 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2689 + ">Supporting Different Platform Versions</a>.</p>"
2690 + "<input type='button' value='OK, make this page visible' "
2691 + "title='Change the API level to " + minLevel + "' "
2692 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2693 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002694 } else {
2695 $("#naMessage").hide();
2696 }
2697}
2698
2699function toggleVisisbleApis(selectedLevel, context) {
2700 var apis = $(".api",context);
2701 apis.each(function(i) {
2702 var obj = $(this);
2703 var className = obj.attr("class");
2704 var apiLevelIndex = className.lastIndexOf("-")+1;
2705 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2706 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2707 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2708 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2709 return;
2710 }
2711 apiLevel = parseInt(apiLevel);
2712
2713 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2714 var selectedLevelNum = parseInt(selectedLevel)
2715 var apiLevelNum = parseInt(apiLevel);
2716 if (isNaN(apiLevelNum)) {
2717 apiLevelNum = maxLevel;
2718 }
2719
2720 // Grey things out that aren't available and give a tooltip title
2721 if (apiLevelNum > selectedLevelNum) {
2722 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002723 + apiLevel + "\" or higher. To reveal, change the target API level "
2724 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002725 }
Scott Mainf5089842012-08-14 16:31:07 -07002726 else obj.removeClass("absent").removeAttr("title");
2727 });
2728}
2729
2730
2731
2732
2733/* ################# SIDENAV TREE VIEW ################### */
2734
2735function new_node(me, mom, text, link, children_data, api_level)
2736{
2737 var node = new Object();
2738 node.children = Array();
2739 node.children_data = children_data;
2740 node.depth = mom.depth + 1;
2741
2742 node.li = document.createElement("li");
2743 mom.get_children_ul().appendChild(node.li);
2744
2745 node.label_div = document.createElement("div");
2746 node.label_div.className = "label";
2747 if (api_level != null) {
2748 $(node.label_div).addClass("api");
2749 $(node.label_div).addClass("api-level-"+api_level);
2750 }
2751 node.li.appendChild(node.label_div);
2752
2753 if (children_data != null) {
2754 node.expand_toggle = document.createElement("a");
2755 node.expand_toggle.href = "javascript:void(0)";
2756 node.expand_toggle.onclick = function() {
2757 if (node.expanded) {
2758 $(node.get_children_ul()).slideUp("fast");
2759 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2760 node.expanded = false;
2761 } else {
2762 expand_node(me, node);
2763 }
2764 };
2765 node.label_div.appendChild(node.expand_toggle);
2766
2767 node.plus_img = document.createElement("img");
2768 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2769 node.plus_img.className = "plus";
2770 node.plus_img.width = "8";
2771 node.plus_img.border = "0";
2772 node.expand_toggle.appendChild(node.plus_img);
2773
2774 node.expanded = false;
2775 }
2776
2777 var a = document.createElement("a");
2778 node.label_div.appendChild(a);
2779 node.label = document.createTextNode(text);
2780 a.appendChild(node.label);
2781 if (link) {
2782 a.href = me.toroot + link;
2783 } else {
2784 if (children_data != null) {
2785 a.className = "nolink";
2786 a.href = "javascript:void(0)";
2787 a.onclick = node.expand_toggle.onclick;
2788 // This next line shouldn't be necessary. I'll buy a beer for the first
2789 // person who figures out how to remove this line and have the link
2790 // toggle shut on the first try. --joeo@android.com
2791 node.expanded = false;
2792 }
2793 }
Scott Main3b90aff2013-08-01 18:09:35 -07002794
Scott Mainf5089842012-08-14 16:31:07 -07002795
2796 node.children_ul = null;
2797 node.get_children_ul = function() {
2798 if (!node.children_ul) {
2799 node.children_ul = document.createElement("ul");
2800 node.children_ul.className = "children_ul";
2801 node.children_ul.style.display = "none";
2802 node.li.appendChild(node.children_ul);
2803 }
2804 return node.children_ul;
2805 };
2806
2807 return node;
2808}
2809
Robert Lyd2dd6e52012-11-29 21:28:48 -08002810
2811
2812
Scott Mainf5089842012-08-14 16:31:07 -07002813function expand_node(me, node)
2814{
2815 if (node.children_data && !node.expanded) {
2816 if (node.children_visited) {
2817 $(node.get_children_ul()).slideDown("fast");
2818 } else {
2819 get_node(me, node);
2820 if ($(node.label_div).hasClass("absent")) {
2821 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002822 }
Scott Mainf5089842012-08-14 16:31:07 -07002823 $(node.get_children_ul()).slideDown("fast");
2824 }
2825 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2826 node.expanded = true;
2827
2828 // perform api level toggling because new nodes are new to the DOM
2829 var selectedLevel = $("#apiLevelSelector option:selected").val();
2830 toggleVisisbleApis(selectedLevel, "#side-nav");
2831 }
2832}
2833
2834function get_node(me, mom)
2835{
2836 mom.children_visited = true;
2837 for (var i in mom.children_data) {
2838 var node_data = mom.children_data[i];
2839 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2840 node_data[2], node_data[3]);
2841 }
2842}
2843
2844function this_page_relative(toroot)
2845{
2846 var full = document.location.pathname;
2847 var file = "";
2848 if (toroot.substr(0, 1) == "/") {
2849 if (full.substr(0, toroot.length) == toroot) {
2850 return full.substr(toroot.length);
2851 } else {
2852 // the file isn't under toroot. Fail.
2853 return null;
2854 }
2855 } else {
2856 if (toroot != "./") {
2857 toroot = "./" + toroot;
2858 }
2859 do {
2860 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2861 var pos = full.lastIndexOf("/");
2862 file = full.substr(pos) + file;
2863 full = full.substr(0, pos);
2864 toroot = toroot.substr(0, toroot.length-3);
2865 }
2866 } while (toroot != "" && toroot != "/");
2867 return file.substr(1);
2868 }
2869}
2870
2871function find_page(url, data)
2872{
2873 var nodes = data;
2874 var result = null;
2875 for (var i in nodes) {
2876 var d = nodes[i];
2877 if (d[1] == url) {
2878 return new Array(i);
2879 }
2880 else if (d[2] != null) {
2881 result = find_page(url, d[2]);
2882 if (result != null) {
2883 return (new Array(i).concat(result));
2884 }
2885 }
2886 }
2887 return null;
2888}
2889
Scott Mainf5089842012-08-14 16:31:07 -07002890function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002891 // load json file for navtree data
2892 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2893 // when the file is loaded, initialize the tree
2894 if(jqxhr.status === 200) {
2895 init_navtree("tree-list", toroot, NAVTREE_DATA);
2896 }
2897 });
Scott Main3b90aff2013-08-01 18:09:35 -07002898
Scott Mainf5089842012-08-14 16:31:07 -07002899 // perform api level toggling because because the whole tree is new to the DOM
2900 var selectedLevel = $("#apiLevelSelector option:selected").val();
2901 toggleVisisbleApis(selectedLevel, "#side-nav");
2902}
2903
2904function init_navtree(navtree_id, toroot, root_nodes)
2905{
2906 var me = new Object();
2907 me.toroot = toroot;
2908 me.node = new Object();
2909
2910 me.node.li = document.getElementById(navtree_id);
2911 me.node.children_data = root_nodes;
2912 me.node.children = new Array();
2913 me.node.children_ul = document.createElement("ul");
2914 me.node.get_children_ul = function() { return me.node.children_ul; };
2915 //me.node.children_ul.className = "children_ul";
2916 me.node.li.appendChild(me.node.children_ul);
2917 me.node.depth = 0;
2918
2919 get_node(me, me.node);
2920
2921 me.this_page = this_page_relative(toroot);
2922 me.breadcrumbs = find_page(me.this_page, root_nodes);
2923 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2924 var mom = me.node;
2925 for (var i in me.breadcrumbs) {
2926 var j = me.breadcrumbs[i];
2927 mom = mom.children[j];
2928 expand_node(me, mom);
2929 }
2930 mom.label_div.className = mom.label_div.className + " selected";
2931 addLoadEvent(function() {
2932 scrollIntoView("nav-tree");
2933 });
2934 }
2935}
2936
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07002937
2938
2939
2940
2941
2942
2943
Robert Lyd2dd6e52012-11-29 21:28:48 -08002944/* TODO: eliminate redundancy with non-google functions */
2945function init_google_navtree(navtree_id, toroot, root_nodes)
2946{
2947 var me = new Object();
2948 me.toroot = toroot;
2949 me.node = new Object();
2950
2951 me.node.li = document.getElementById(navtree_id);
2952 me.node.children_data = root_nodes;
2953 me.node.children = new Array();
2954 me.node.children_ul = document.createElement("ul");
2955 me.node.get_children_ul = function() { return me.node.children_ul; };
2956 //me.node.children_ul.className = "children_ul";
2957 me.node.li.appendChild(me.node.children_ul);
2958 me.node.depth = 0;
2959
2960 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002961}
2962
2963function new_google_node(me, mom, text, link, children_data, api_level)
2964{
2965 var node = new Object();
2966 var child;
2967 node.children = Array();
2968 node.children_data = children_data;
2969 node.depth = mom.depth + 1;
2970 node.get_children_ul = function() {
2971 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07002972 node.children_ul = document.createElement("ul");
2973 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002974 node.li.appendChild(node.children_ul);
2975 }
2976 return node.children_ul;
2977 };
2978 node.li = document.createElement("li");
2979
2980 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07002981
2982
Robert Lyd2dd6e52012-11-29 21:28:48 -08002983 if(link) {
2984 child = document.createElement("a");
2985
2986 }
2987 else {
2988 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08002989 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002990
2991 }
2992 if (children_data != null) {
2993 node.li.className="nav-section";
2994 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07002995 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002996 node.li.appendChild(node.label_div);
2997 get_google_node(me, node);
2998 node.label_div.appendChild(child);
2999 }
3000 else {
3001 node.li.appendChild(child);
3002 }
3003 if(link) {
3004 child.href = me.toroot + link;
3005 }
3006 node.label = document.createTextNode(text);
3007 child.appendChild(node.label);
3008
3009 node.children_ul = null;
3010
3011 return node;
3012}
3013
3014function get_google_node(me, mom)
3015{
3016 mom.children_visited = true;
3017 var linkText;
3018 for (var i in mom.children_data) {
3019 var node_data = mom.children_data[i];
3020 linkText = node_data[0];
3021
3022 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3023 linkText = linkText.substr(19, linkText.length);
3024 }
3025 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3026 node_data[2], node_data[3]);
3027 }
3028}
Scott Mainad08f072013-08-20 16:49:57 -07003029
3030
3031
3032
3033
3034
3035/****** NEW version of script to build google and sample navs dynamically ******/
3036// TODO: update Google reference docs to tolerate this new implementation
3037
Scott Maine624b3f2013-09-12 12:56:41 -07003038var NODE_NAME = 0;
3039var NODE_HREF = 1;
3040var NODE_GROUP = 2;
3041var NODE_TAGS = 3;
3042var NODE_CHILDREN = 4;
3043
Scott Mainad08f072013-08-20 16:49:57 -07003044function init_google_navtree2(navtree_id, data)
3045{
3046 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003047 for (var i in data) {
3048 var node_data = data[i];
3049 $containerUl.append(new_google_node2(node_data));
3050 }
3051
Scott Main70557ee2013-10-30 14:47:40 -07003052 // Make all third-generation list items 'sticky' to prevent them from collapsing
3053 $containerUl.find('li li li.nav-section').addClass('sticky');
3054
Scott Mainad08f072013-08-20 16:49:57 -07003055 initExpandableNavItems("#"+navtree_id);
3056}
3057
3058function new_google_node2(node_data)
3059{
Scott Maine624b3f2013-09-12 12:56:41 -07003060 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003061 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3062 linkText = linkText.substr(19, linkText.length);
3063 }
3064 var $li = $('<li>');
3065 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003066 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003067 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3068 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003069 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003070 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3071 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003072 }
3073 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003074 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003075 $li.addClass("nav-section");
3076 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003077 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003078
Scott Maine624b3f2013-09-12 12:56:41 -07003079 for (var i in node_data[NODE_CHILDREN]) {
3080 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003081 $childUl.append(new_google_node2(child_node_data));
3082 }
3083 $li.append($childUl);
3084 }
3085 $li.prepend($a);
3086
3087 return $li;
3088}
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
Robert Lyd2dd6e52012-11-29 21:28:48 -08003100function showGoogleRefTree() {
3101 init_default_google_navtree(toRoot);
3102 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003103}
3104
3105function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003106 // load json file for navtree data
3107 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3108 // when the file is loaded, initialize the tree
3109 if(jqxhr.status === 200) {
3110 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3111 highlightSidenav();
3112 resizeNav();
3113 }
3114 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003115}
3116
3117function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003118 // load json file for navtree data
3119 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3120 // when the file is loaded, initialize the tree
3121 if(jqxhr.status === 200) {
3122 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3123 highlightSidenav();
3124 resizeNav();
3125 }
3126 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003127}
3128
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003129function showSamplesRefTree() {
3130 init_default_samples_navtree(toRoot);
3131}
3132
3133function init_default_samples_navtree(toroot) {
3134 // load json file for navtree data
3135 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3136 // when the file is loaded, initialize the tree
3137 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003138 // hack to remove the "about the samples" link then put it back in
3139 // after we nuke the list to remove the dummy static list of samples
3140 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3141 $("#nav.samples-nav").empty();
3142 $("#nav.samples-nav").append($firstLi);
3143
Scott Mainad08f072013-08-20 16:49:57 -07003144 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003145 highlightSidenav();
3146 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003147 if ($("#jd-content #samples").length) {
3148 showSamples();
3149 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003150 }
3151 });
3152}
3153
Scott Mainf5089842012-08-14 16:31:07 -07003154/* TOGGLE INHERITED MEMBERS */
3155
3156/* Toggle an inherited class (arrow toggle)
3157 * @param linkObj The link that was clicked.
3158 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3159 * 'null' to simply toggle.
3160 */
3161function toggleInherited(linkObj, expand) {
3162 var base = linkObj.getAttribute("id");
3163 var list = document.getElementById(base + "-list");
3164 var summary = document.getElementById(base + "-summary");
3165 var trigger = document.getElementById(base + "-trigger");
3166 var a = $(linkObj);
3167 if ( (expand == null && a.hasClass("closed")) || expand ) {
3168 list.style.display = "none";
3169 summary.style.display = "block";
3170 trigger.src = toRoot + "assets/images/triangle-opened.png";
3171 a.removeClass("closed");
3172 a.addClass("opened");
3173 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3174 list.style.display = "block";
3175 summary.style.display = "none";
3176 trigger.src = toRoot + "assets/images/triangle-closed.png";
3177 a.removeClass("opened");
3178 a.addClass("closed");
3179 }
3180 return false;
3181}
3182
3183/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3184 * @param linkObj The link that was clicked.
3185 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3186 * 'null' to simply toggle.
3187 */
3188function toggleAllInherited(linkObj, expand) {
3189 var a = $(linkObj);
3190 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3191 var expandos = $(".jd-expando-trigger", table);
3192 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3193 expandos.each(function(i) {
3194 toggleInherited(this, true);
3195 });
3196 a.text("[Collapse]");
3197 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3198 expandos.each(function(i) {
3199 toggleInherited(this, false);
3200 });
3201 a.text("[Expand]");
3202 }
3203 return false;
3204}
3205
3206/* Toggle all inherited members in the class (link in the class title)
3207 */
3208function toggleAllClassInherited() {
3209 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3210 var toggles = $(".toggle-all", $("#body-content"));
3211 if (a.text() == "[Expand All]") {
3212 toggles.each(function(i) {
3213 toggleAllInherited(this, true);
3214 });
3215 a.text("[Collapse All]");
3216 } else {
3217 toggles.each(function(i) {
3218 toggleAllInherited(this, false);
3219 });
3220 a.text("[Expand All]");
3221 }
3222 return false;
3223}
3224
3225/* Expand all inherited members in the class. Used when initiating page search */
3226function ensureAllInheritedExpanded() {
3227 var toggles = $(".toggle-all", $("#body-content"));
3228 toggles.each(function(i) {
3229 toggleAllInherited(this, true);
3230 });
3231 $("#toggleAllClassInherited").text("[Collapse All]");
3232}
3233
3234
3235/* HANDLE KEY EVENTS
3236 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3237 */
3238var agent = navigator['userAgent'].toLowerCase();
3239var mac = agent.indexOf("macintosh") != -1;
3240
3241$(document).keydown( function(e) {
3242var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3243 if (control && e.which == 70) { // 70 is "F"
3244 ensureAllInheritedExpanded();
3245 }
3246});
Scott Main498d7102013-08-21 15:47:38 -07003247
3248
3249
3250
3251
3252
3253/* On-demand functions */
3254
3255/** Move sample code line numbers out of PRE block and into non-copyable column */
3256function initCodeLineNumbers() {
3257 var numbers = $("#codesample-block a.number");
3258 if (numbers.length) {
3259 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3260 }
3261
3262 $(document).ready(function() {
3263 // select entire line when clicked
3264 $("span.code-line").click(function() {
3265 if (!shifted) {
3266 selectText(this);
3267 }
3268 });
3269 // invoke line link on double click
3270 $(".code-line").dblclick(function() {
3271 document.location.hash = $(this).attr('id');
3272 });
3273 // highlight the line when hovering on the number
3274 $("#codesample-line-numbers a.number").mouseover(function() {
3275 var id = $(this).attr('href');
3276 $(id).css('background','#e7e7e7');
3277 });
3278 $("#codesample-line-numbers a.number").mouseout(function() {
3279 var id = $(this).attr('href');
3280 $(id).css('background','none');
3281 });
3282 });
3283}
3284
3285// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3286var shifted = false;
3287$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3288
3289// courtesy of jasonedelman.com
3290function selectText(element) {
3291 var doc = document
3292 , range, selection
3293 ;
3294 if (doc.body.createTextRange) { //ms
3295 range = doc.body.createTextRange();
3296 range.moveToElementText(element);
3297 range.select();
3298 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003299 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003300 range = doc.createRange();
3301 range.selectNodeContents(element);
3302 selection.removeAllRanges();
3303 selection.addRange(range);
3304 }
Scott Main285f0772013-08-22 23:22:09 +00003305}
Scott Main03aca9a2013-10-31 07:20:55 -07003306
3307
3308
3309
3310/** Display links and other information about samples that match the
3311 group specified by the URL */
3312function showSamples() {
3313 var group = $("#samples").attr('class');
3314 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3315
3316 var $ul = $("<ul>");
3317 $selectedLi = $("#nav li.selected");
3318
3319 $selectedLi.children("ul").children("li").each(function() {
3320 var $li = $("<li>").append($(this).find("a").first().clone());
3321 $ul.append($li);
3322 });
3323
3324 $("#samples").append($ul);
3325
3326}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003327
3328
3329
3330/* ########################################################## */
3331/* ################### RESOURCE CARDS ##################### */
3332/* ########################################################## */
3333
3334/** Handle resource queries, collections, and grids (sections). Requires
3335 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3336
3337(function() {
3338 // Prevent the same resource from being loaded more than once per page.
3339 var addedPageResources = {};
3340
3341 $(document).ready(function() {
3342 $('.resource-widget').each(function() {
3343 initResourceWidget(this);
3344 });
3345
3346 /* Pass the line height to ellipsisfade() to adjust the height of the
3347 text container to show the max number of lines possible, without
3348 showing lines that are cut off. This works with the css ellipsis
3349 classes to fade last text line and apply an ellipsis char. */
3350
3351 //card text currently uses 15px line height.
3352 var lineHeight = 15;
3353 $('.card-info .text').ellipsisfade(lineHeight);
3354 });
3355
3356 /*
3357 Three types of resource layouts:
3358 Flow - Uses a fixed row-height flow using float left style.
3359 Carousel - Single card slideshow all same dimension absolute.
3360 Stack - Uses fixed columns and flexible element height.
3361 */
3362 function initResourceWidget(widget) {
3363 var $widget = $(widget);
3364 var isFlow = $widget.hasClass('resource-flow-layout'),
3365 isCarousel = $widget.hasClass('resource-carousel-layout'),
3366 isStack = $widget.hasClass('resource-stack-layout');
3367
3368 // find size of widget by pulling out its class name
3369 var sizeCols = 1;
3370 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3371 if (m) {
3372 sizeCols = parseInt(m[1], 10);
3373 }
3374
3375 var opts = {
3376 cardSizes: ($widget.data('cardsizes') || '').split(','),
3377 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3378 itemsPerPage: $widget.data('itemsperpage'),
3379 sortOrder: $widget.data('sortorder'),
3380 query: $widget.data('query'),
3381 section: $widget.data('section'),
3382 sizeCols: sizeCols
3383 };
3384
3385 // run the search for the set of resources to show
3386
3387 var resources = buildResourceList(opts);
3388
3389 if (isFlow) {
3390 drawResourcesFlowWidget($widget, opts, resources);
3391 } else if (isCarousel) {
3392 drawResourcesCarouselWidget($widget, opts, resources);
3393 } else if (isStack) {
3394 var sections = buildSectionList(opts);
3395 opts['numStacks'] = $widget.data('numstacks');
3396 drawResourcesStackWidget($widget, opts, resources, sections);
3397 }
3398 }
3399
3400 /* Initializes a Resource Carousel Widget */
3401 function drawResourcesCarouselWidget($widget, opts, resources) {
3402 $widget.empty();
3403 var plusone = true; //always show plusone on carousel
3404
3405 $widget.addClass('resource-card slideshow-container')
3406 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3407 .append($('<a>').addClass('slideshow-next').text('Next'));
3408
3409 var css = { 'width': $widget.width() + 'px',
3410 'height': $widget.height() + 'px' };
3411
3412 var $ul = $('<ul>');
3413
3414 for (var i = 0; i < resources.length; ++i) {
3415 //keep url clean for matching and offline mode handling
3416 var urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3417 var $card = $('<a>')
3418 .attr('href', urlPrefix + resources[i].url)
3419 .decorateResourceCard(resources[i],plusone);
3420
3421 $('<li>').css(css)
3422 .append($card)
3423 .appendTo($ul);
3424 }
3425
3426 $('<div>').addClass('frame')
3427 .append($ul)
3428 .appendTo($widget);
3429
3430 $widget.dacSlideshow({
3431 auto: true,
3432 btnPrev: '.slideshow-prev',
3433 btnNext: '.slideshow-next'
3434 });
3435 };
3436
3437 /* Initializes a Resource Card Stack Widget (column-based layout) */
3438 function drawResourcesStackWidget($widget, opts, resources, sections) {
3439 // Don't empty widget, grab all items inside since they will be the first
3440 // items stacked, followed by the resource query
3441 var plusone = true; //by default show plusone on section cards
3442 var cards = $widget.find('.resource-card').detach().toArray();
3443 var numStacks = opts.numStacks || 1;
3444 var $stacks = [];
3445 var urlString;
3446
3447 for (var i = 0; i < numStacks; ++i) {
3448 $stacks[i] = $('<div>').addClass('resource-card-stack')
3449 .appendTo($widget);
3450 }
3451
3452 var sectionResources = [];
3453
3454 // Extract any subsections that are actually resource cards
3455 for (var i = 0; i < sections.length; ++i) {
3456 if (!sections[i].sections || !sections[i].sections.length) {
3457 //keep url clean for matching and offline mode handling
3458 urlPrefix = sections[i].url.indexOf("//") > -1 ? "" : toRoot;
3459 // Render it as a resource card
3460
3461 sectionResources.push(
3462 $('<a>')
3463 .addClass('resource-card section-card')
3464 .attr('href', urlPrefix + sections[i].resource.url)
3465 .decorateResourceCard(sections[i].resource,plusone)[0]
3466 );
3467
3468 } else {
3469 cards.push(
3470 $('<div>')
3471 .addClass('resource-card section-card-menu')
3472 .decorateResourceSection(sections[i],plusone)[0]
3473 );
3474 }
3475 }
3476
3477 cards = cards.concat(sectionResources);
3478
3479 for (var i = 0; i < resources.length; ++i) {
3480 //keep url clean for matching and offline mode handling
3481 urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3482 var $card = $('<a>')
3483 .addClass('resource-card related-card')
3484 .attr('href', urlPrefix + resources[i].url)
3485 .decorateResourceCard(resources[i],plusone);
3486
3487 cards.push($card[0]);
3488 }
3489
3490 for (var i = 0; i < cards.length; ++i) {
3491 // Find the stack with the shortest height, but give preference to
3492 // left to right order.
3493 var minHeight = $stacks[0].height();
3494 var minIndex = 0;
3495
3496 for (var j = 1; j < numStacks; ++j) {
3497 var height = $stacks[j].height();
3498 if (height < minHeight - 45) {
3499 minHeight = height;
3500 minIndex = j;
3501 }
3502 }
3503
3504 $stacks[minIndex].append($(cards[i]));
3505 }
3506
3507 };
3508
3509 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3510 function drawResourcesFlowWidget($widget, opts, resources) {
3511 $widget.empty();
3512 var cardSizes = opts.cardSizes || ['6x6'];
3513 var i = 0, j = 0;
3514 var plusone = true; // by default show plusone on resource cards
3515
3516 while (i < resources.length) {
3517 var cardSize = cardSizes[j++ % cardSizes.length];
3518 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003519 // Some card sizes do not get a plusone button, such as where space is constrained
3520 // or for cards commonly embedded in docs (to improve overall page speed).
3521 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3522 (cardSize == "9x2") || (cardSize == "9x3") ||
3523 (cardSize == "12x2") || (cardSize == "12x3"));
3524
3525 // A stack has a third dimension which is the number of stacked items
3526 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3527 var stackCount = 0;
3528 var $stackDiv = null;
3529
3530 if (isStack) {
3531 // Create a stack container which should have the dimensions defined
3532 // by the product of the items inside.
3533 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3534 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3535 }
3536
3537 // Build each stack item or just a single item
3538 do {
3539 var resource = resources[i];
3540 //keep url clean for matching and offline mode handling
3541 urlPrefix = resource.url.indexOf("//") > -1 ? "" : toRoot;
3542 var $card = $('<a>')
3543 .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
3544 .attr('href', urlPrefix + resource.url);
3545
3546 if (isStack) {
3547 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3548 if (++stackCount == parseInt(isStack[3])) {
3549 $card.addClass('resource-card-row-stack-last');
3550 stackCount = 0;
3551 }
3552 } else {
3553 stackCount = 0;
3554 }
3555
3556 $card.decorateResourceCard(resource,plusone)
3557 .appendTo($stackDiv || $widget);
3558
3559 } while (++i < resources.length && stackCount > 0);
3560 }
3561 }
3562
3563 /* Build a site map of resources using a section as a root. */
3564 function buildSectionList(opts) {
3565 if (opts.section && SECTION_BY_ID[opts.section]) {
3566 return SECTION_BY_ID[opts.section].sections || [];
3567 }
3568 return [];
3569 }
3570
3571 function buildResourceList(opts) {
3572 var maxResults = opts.maxResults || 100;
3573
3574 var query = opts.query || '';
3575 var expressions = parseResourceQuery(query);
3576 var addedResourceIndices = {};
3577 var results = [];
3578
3579 for (var i = 0; i < expressions.length; i++) {
3580 var clauses = expressions[i];
3581
3582 // build initial set of resources from first clause
3583 var firstClause = clauses[0];
3584 var resources = [];
3585 switch (firstClause.attr) {
3586 case 'type':
3587 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3588 break;
3589 case 'lang':
3590 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3591 break;
3592 case 'tag':
3593 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3594 break;
3595 case 'collection':
3596 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3597 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3598 break;
3599 case 'section':
3600 var urls = SITE_MAP[firstClause.value].sections || [];
3601 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3602 break;
3603 }
3604 // console.log(firstClause.attr + ':' + firstClause.value);
3605 resources = resources || [];
3606
3607 // use additional clauses to filter corpus
3608 if (clauses.length > 1) {
3609 var otherClauses = clauses.slice(1);
3610 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3611 }
3612
3613 // filter out resources already added
3614 if (i > 1) {
3615 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3616 }
3617
3618 // add to list of already added indices
3619 for (var j = 0; j < resources.length; j++) {
3620 // console.log(resources[j].title);
3621 addedResourceIndices[resources[j].index] = 1;
3622 }
3623
3624 // concat to final results list
3625 results = results.concat(resources);
3626 }
3627
3628 if (opts.sortOrder && results.length) {
3629 var attr = opts.sortOrder;
3630
3631 if (opts.sortOrder == 'random') {
3632 var i = results.length, j, temp;
3633 while (--i) {
3634 j = Math.floor(Math.random() * (i + 1));
3635 temp = results[i];
3636 results[i] = results[j];
3637 results[j] = temp;
3638 }
3639 } else {
3640 var desc = attr.charAt(0) == '-';
3641 if (desc) {
3642 attr = attr.substring(1);
3643 }
3644 results = results.sort(function(x,y) {
3645 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3646 });
3647 }
3648 }
3649
3650 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3651 results = results.slice(0, maxResults);
3652
3653 for (var j = 0; j < results.length; ++j) {
3654 addedPageResources[results[j].index] = 1;
3655 }
3656
3657 return results;
3658 }
3659
3660
3661 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3662 return function(resource) {
3663 return !addedResourceIndices[resource.index];
3664 };
3665 }
3666
3667
3668 function getResourceMatchesClausesFilter(clauses) {
3669 return function(resource) {
3670 return doesResourceMatchClauses(resource, clauses);
3671 };
3672 }
3673
3674
3675 function doesResourceMatchClauses(resource, clauses) {
3676 for (var i = 0; i < clauses.length; i++) {
3677 var map;
3678 switch (clauses[i].attr) {
3679 case 'type':
3680 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3681 break;
3682 case 'lang':
3683 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3684 break;
3685 case 'tag':
3686 map = IS_RESOURCE_TAGGED[clauses[i].value];
3687 break;
3688 }
3689
3690 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3691 return clauses[i].negative;
3692 }
3693 }
3694 return true;
3695 }
3696
3697
3698 function parseResourceQuery(query) {
3699 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3700 var expressions = [];
3701 var expressionStrs = query.split(',') || [];
3702 for (var i = 0; i < expressionStrs.length; i++) {
3703 var expr = expressionStrs[i] || '';
3704
3705 // Break expression into clauses (clause e.g. 'tag:foo')
3706 var clauses = [];
3707 var clauseStrs = expr.split(/(?=[\+\-])/);
3708 for (var j = 0; j < clauseStrs.length; j++) {
3709 var clauseStr = clauseStrs[j] || '';
3710
3711 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3712 var parts = clauseStr.split(':');
3713 var clause = {};
3714
3715 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3716 if (clause.attr) {
3717 if (clause.attr.charAt(0) == '+') {
3718 clause.attr = clause.attr.substring(1);
3719 } else if (clause.attr.charAt(0) == '-') {
3720 clause.negative = true;
3721 clause.attr = clause.attr.substring(1);
3722 }
3723 }
3724
3725 if (parts.length > 1) {
3726 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3727 }
3728
3729 clauses.push(clause);
3730 }
3731
3732 if (!clauses.length) {
3733 continue;
3734 }
3735
3736 expressions.push(clauses);
3737 }
3738
3739 return expressions;
3740 }
3741})();
3742
3743(function($) {
3744 /* Simple jquery function to create dom for a standard resource card */
3745 $.fn.decorateResourceCard = function(resource,plusone) {
3746 var section = resource.group || resource.type;
3747 var imgUrl;
3748 if (resource.image) {
3749 //keep url clean for matching and offline mode handling
3750 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3751 imgUrl = urlPrefix + resource.image;
3752 }
3753 //add linkout logic here. check url or type, assign a class, map to css :after
3754 $('<div>')
3755 .addClass('card-bg')
3756 .css('background-image', 'url(' + (imgUrl || toRoot + 'assets/images/resource-card-default-android.jpg') + ')')
3757 .appendTo(this);
3758 if (!plusone) {
3759 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3760 .append($('<div>').addClass('section').text(section))
3761 .append($('<div>').addClass('title').html(resource.title))
3762 .append($('<div>').addClass('description ellipsis')
3763 .append($('<div>').addClass('text').html(resource.summary))
3764 .append($('<div>').addClass('util')))
3765 .appendTo(this);
3766 } else {
3767 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3768 .append($('<div>').addClass('section').text(section))
3769 .append($('<div>').addClass('title').html(resource.title))
3770 .append($('<div>').addClass('description ellipsis')
3771 .append($('<div>').addClass('text').html(resource.summary))
3772 .append($('<div>').addClass('util')
3773 .append($('<div>').addClass('g-plusone')
3774 .attr('data-size', 'small')
3775 .attr('data-align', 'right')
3776 .attr('data-href', resource.url))))
3777 .appendTo(this);
3778 }
3779
3780 return this;
3781 };
3782
3783 /* Simple jquery function to create dom for a resource section card (menu) */
3784 $.fn.decorateResourceSection = function(section,plusone) {
3785 var resource = section.resource;
3786 //keep url clean for matching and offline mode handling
3787 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3788 var $base = $('<a>')
3789 .addClass('card-bg')
3790 .attr('href', resource.url)
3791 .append($('<div>').addClass('card-section-icon')
3792 .append($('<div>').addClass('icon'))
3793 .append($('<div>').addClass('section').html(resource.title)))
3794 .appendTo(this);
3795
3796 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3797
3798 if (section.sections && section.sections.length) {
3799 // Recurse the section sub-tree to find a resource image.
3800 var stack = [section];
3801
3802 while (stack.length) {
3803 if (stack[0].resource.image) {
3804 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3805 break;
3806 }
3807
3808 if (stack[0].sections) {
3809 stack = stack.concat(stack[0].sections);
3810 }
3811
3812 stack.shift();
3813 }
3814
3815 var $ul = $('<ul>')
3816 .appendTo($cardInfo);
3817
3818 var max = section.sections.length > 3 ? 3 : section.sections.length;
3819
3820 for (var i = 0; i < max; ++i) {
3821
3822 var subResource = section.sections[i];
3823 if (!plusone) {
3824 $('<li>')
3825 .append($('<a>').attr('href', subResource.url)
3826 .append($('<div>').addClass('title').html(subResource.title))
3827 .append($('<div>').addClass('description ellipsis')
3828 .append($('<div>').addClass('text').html(subResource.summary))
3829 .append($('<div>').addClass('util'))))
3830 .appendTo($ul);
3831 } else {
3832 $('<li>')
3833 .append($('<a>').attr('href', subResource.url)
3834 .append($('<div>').addClass('title').html(subResource.title))
3835 .append($('<div>').addClass('description ellipsis')
3836 .append($('<div>').addClass('text').html(subResource.summary))
3837 .append($('<div>').addClass('util')
3838 .append($('<div>').addClass('g-plusone')
3839 .attr('data-size', 'small')
3840 .attr('data-align', 'right')
3841 .attr('data-href', resource.url)))))
3842 .appendTo($ul);
3843 }
3844 }
3845
3846 // Add a more row
3847 if (max < section.sections.length) {
3848 $('<li>')
3849 .append($('<a>').attr('href', resource.url)
3850 .append($('<div>')
3851 .addClass('title')
3852 .text('More')))
3853 .appendTo($ul);
3854 }
3855 } else {
3856 // No sub-resources, just render description?
3857 }
3858
3859 return this;
3860 };
3861})(jQuery);
3862/* Calculate the vertical area remaining */
3863(function($) {
3864 $.fn.ellipsisfade= function(lineHeight) {
3865 this.each(function() {
3866 // get element text
3867 var $this = $(this);
3868 var remainingHeight = $this.parent().parent().height();
3869 $this.parent().siblings().each(function ()
3870 {
3871 var h = $(this).height();
3872 remainingHeight = remainingHeight - h;
3873 });
3874
3875 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
3876 $this.parent().css({'height': adjustedRemainingHeight});
3877 $this.css({'height': "auto"});
3878 });
3879
3880 return this;
3881 };
3882}) (jQuery);