blob: 42cd29e1013735d3feec374cccd894a71a7cb7a9 [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
Scott Maine4d8f1b2012-06-21 18:03:05 -070022$(document).ready(function() {
Scott Main7e447ed2013-02-19 17:22:37 -080023
Scott Main0e76e7e2013-03-12 10:24:07 -070024 // load json file for JD doc search suggestions
Scott Main719acb42013-12-05 16:05:09 -080025 $.getScript(toRoot + 'jd_lists_unified.js');
Scott Main7e447ed2013-02-19 17:22:37 -080026 // load json file for Android API search suggestions
27 $.getScript(toRoot + 'reference/lists.js');
28 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080029 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080030 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
31 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080032 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080033 if(jqxhr.status === 200) {
34 // combine GCM and GMS data
35 GOOGLE_DATA = GMS_DATA;
36 var start = GOOGLE_DATA.length;
37 for (var i=0; i<GCM_DATA.length; i++) {
38 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
39 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
40 }
41 }
42 });
43 }
44 });
45
Scott Main0e76e7e2013-03-12 10:24:07 -070046 // setup keyboard listener for search shortcut
47 $('body').keyup(function(event) {
48 if (event.which == 191) {
49 $('#search_autocomplete').focus();
50 }
51 });
Scott Main015d6162013-01-29 09:01:52 -080052
Scott Maine4d8f1b2012-06-21 18:03:05 -070053 // init the fullscreen toggle click event
54 $('#nav-swap .fullscreen').click(function(){
55 if ($(this).hasClass('disabled')) {
56 toggleFullscreen(true);
57 } else {
58 toggleFullscreen(false);
59 }
60 });
Scott Main3b90aff2013-08-01 18:09:35 -070061
Scott Maine4d8f1b2012-06-21 18:03:05 -070062 // initialize the divs with custom scrollbars
63 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070064
Scott Maine4d8f1b2012-06-21 18:03:05 -070065 // add HRs below all H2s (except for a few other h2 variants)
Scott Mainc29b3f52014-05-30 21:18:30 -070066 $('h2').not('#qv h2')
67 .not('#tb h2')
68 .not('.sidebox h2')
69 .not('#devdoc-nav h2')
70 .not('h2.norule').css({marginBottom:0})
71 .after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070072
73 // set up the search close button
74 $('.search .close').click(function() {
75 $searchInput = $('#search_autocomplete');
76 $searchInput.attr('value', '');
77 $(this).addClass("hide");
78 $("#search-container").removeClass('active');
79 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070080 search_focus_changed($searchInput.get(), false);
81 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070082 });
83
84 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070085 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070086 $("#btn-quicknav").click(function() {
87 if (quicknav_open) {
88 $(this).removeClass('active');
89 quicknav_open = false;
90 collapse();
91 } else {
92 $(this).addClass('active');
93 quicknav_open = true;
94 expand();
95 }
96 })
Scott Main3b90aff2013-08-01 18:09:35 -070097
Scott Maine4d8f1b2012-06-21 18:03:05 -070098 var expand = function() {
99 $('#header-wrap').addClass('quicknav');
100 $('#quicknav').stop().show().animate({opacity:'1'});
101 }
Scott Main3b90aff2013-08-01 18:09:35 -0700102
Scott Maine4d8f1b2012-06-21 18:03:05 -0700103 var collapse = function() {
104 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
105 $(this).hide();
106 $('#header-wrap').removeClass('quicknav');
107 });
108 }
Scott Main3b90aff2013-08-01 18:09:35 -0700109
110
Scott Maine4d8f1b2012-06-21 18:03:05 -0700111 //Set up search
112 $("#search_autocomplete").focus(function() {
113 $("#search-container").addClass('active');
114 })
115 $("#search-container").mouseover(function() {
116 $("#search-container").addClass('active');
117 $("#search_autocomplete").focus();
118 })
119 $("#search-container").mouseout(function() {
120 if ($("#search_autocomplete").is(":focus")) return;
121 if ($("#search_autocomplete").val() == '') {
122 setTimeout(function(){
123 $("#search-container").removeClass('active');
124 $("#search_autocomplete").blur();
125 },250);
126 }
127 })
128 $("#search_autocomplete").blur(function() {
129 if ($("#search_autocomplete").val() == '') {
130 $("#search-container").removeClass('active');
131 }
132 })
133
Scott Main3b90aff2013-08-01 18:09:35 -0700134
Scott Maine4d8f1b2012-06-21 18:03:05 -0700135 // prep nav expandos
136 var pagePath = document.location.pathname;
137 // account for intl docs by removing the intl/*/ path
138 if (pagePath.indexOf("/intl/") == 0) {
139 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
140 }
Scott Mainac2aef52013-02-12 14:15:23 -0800141
Scott Maine4d8f1b2012-06-21 18:03:05 -0700142 if (pagePath.indexOf(SITE_ROOT) == 0) {
143 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
144 pagePath += 'index.html';
145 }
146 }
147
Scott Main01a25452013-02-12 17:32:27 -0800148 // Need a copy of the pagePath before it gets changed in the next block;
149 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
150 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700151 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
152 // If running locally, SITE_ROOT will be a relative path, so account for that by
153 // finding the relative URL to this page. This will allow us to find links on the page
154 // leading back to this page.
155 var pathParts = pagePath.split('/');
156 var relativePagePathParts = [];
157 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
158 for (var i = 0; i < upDirs; i++) {
159 relativePagePathParts.push('..');
160 }
161 for (var i = 0; i < upDirs; i++) {
162 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
163 }
164 relativePagePathParts.push(pathParts[pathParts.length - 1]);
165 pagePath = relativePagePathParts.join('/');
166 } else {
167 // Otherwise the page path is already an absolute URL
168 }
169
Scott Mainac2aef52013-02-12 14:15:23 -0800170 // Highlight the header tabs...
171 // highlight Design tab
172 if ($("body").hasClass("design")) {
173 $("#header li.design a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700174 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800175
smain@google.com6040ffa2014-06-13 15:06:23 -0700176 // highlight About tabs
177 } else if ($("body").hasClass("about")) {
178 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
179 if (rootDir == "about") {
180 $("#nav-x li.about a").addClass("selected");
181 } else if (rootDir == "wear") {
182 $("#nav-x li.wear a").addClass("selected");
183 } else if (rootDir == "tv") {
184 $("#nav-x li.tv a").addClass("selected");
185 } else if (rootDir == "auto") {
186 $("#nav-x li.auto a").addClass("selected");
187 }
Scott Mainac2aef52013-02-12 14:15:23 -0800188 // highlight Develop tab
189 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
190 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700191 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800192 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800193 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800194 if (rootDir == "training") {
195 $("#nav-x li.training a").addClass("selected");
196 } else if (rootDir == "guide") {
197 $("#nav-x li.guide a").addClass("selected");
198 } else if (rootDir == "reference") {
199 // If the root is reference, but page is also part of Google Services, select Google
200 if ($("body").hasClass("google")) {
201 $("#nav-x li.google a").addClass("selected");
202 } else {
203 $("#nav-x li.reference a").addClass("selected");
204 }
205 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
206 $("#nav-x li.tools a").addClass("selected");
207 } else if ($("body").hasClass("google")) {
208 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700209 } else if ($("body").hasClass("samples")) {
210 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800211 }
212
213 // highlight Distribute tab
214 } else if ($("body").hasClass("distribute")) {
215 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700216 $("#sticky-header").addClass("distribute");
217
218 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
219 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
220 if (secondFrag == "users") {
221 $("#nav-x li.users a").addClass("selected");
222 } else if (secondFrag == "engage") {
223 $("#nav-x li.engage a").addClass("selected");
224 } else if (secondFrag == "monetize") {
225 $("#nav-x li.monetize a").addClass("selected");
226 } else if (secondFrag == "tools") {
227 $("#nav-x li.disttools a").addClass("selected");
228 } else if (secondFrag == "stories") {
229 $("#nav-x li.stories a").addClass("selected");
230 } else if (secondFrag == "essentials") {
231 $("#nav-x li.essentials a").addClass("selected");
232 } else if (secondFrag == "googleplay") {
233 $("#nav-x li.googleplay a").addClass("selected");
234 }
235 } else if ($("body").hasClass("about")) {
236 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700237 }
Scott Mainac2aef52013-02-12 14:15:23 -0800238
Scott Mainf6145542013-04-01 16:38:11 -0700239 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
240 // and highlight the sidenav
241 mPagePath = pagePath;
242 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700243 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800244
Scott Mainf6145542013-04-01 16:38:11 -0700245 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700246 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700247 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700248 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800249 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700250
251 // set up prev links
252 var $prevLink = [];
253 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700254
Scott Maine4d8f1b2012-06-21 18:03:05 -0700255 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
256false; // navigate across topic boundaries only in design docs
257 if ($prevListItem.length) {
258 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700259 // jump to last topic of previous section
260 $prevLink = $prevListItem.find('a:last');
261 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700262 // jump to previous topic in this section
263 $prevLink = $prevListItem.find('a:eq(0)');
264 }
265 } else {
266 // jump to this section's index page (if it exists)
267 var $parentListItem = $selListItem.parents('li');
268 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700269
Scott Maine4d8f1b2012-06-21 18:03:05 -0700270 // except if cross boundaries aren't allowed, and we're at the top of a section already
271 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700272 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700273 && $selListItem.hasClass('nav-section')) {
274 $prevLink = [];
275 }
276 }
277
Scott Maine4d8f1b2012-06-21 18:03:05 -0700278 // set up next links
279 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700280 var startClass = false;
281 var training = $(".next-class-link").length; // decides whether to provide "next class" link
282 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700283
Scott Main1a00f7f2013-10-29 11:11:19 -0700284 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700285 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700286 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700287
288 // if there aren't any children, go to the next section (required for About pages)
289 if($nextLink.length == 0) {
290 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700291 } else if ($('.topic-start-link').length) {
292 // as long as there's a child link and there is a "topic start link" (we're on a landing)
293 // then set the landing page "start link" text to be the first doc title
294 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700295 }
Scott Main3b90aff2013-08-01 18:09:35 -0700296
Scott Main5a1123e2012-09-26 12:51:28 -0700297 // If the selected page has a description, then it's a class or article homepage
298 if ($selListItem.find('a[description]').length) {
299 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700300 startClass = true;
301 }
302 } else {
303 // jump to the next topic in this section (if it exists)
304 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700305 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700306 isCrossingBoundary = true;
307 // no more topics in this section, jump to the first topic in the next section
308 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
309 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
310 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700311 if ($nextLink.length == 0) {
312 // if that doesn't work, we're at the end of the list, so disable NEXT link
313 $('.next-page-link').attr('href','').addClass("disabled")
314 .click(function() { return false; });
315 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700316 }
317 }
318 }
Scott Main5a1123e2012-09-26 12:51:28 -0700319
320 if (startClass) {
321 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
322
Scott Main3b90aff2013-08-01 18:09:35 -0700323 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700324 // then we need to add a bottom border to button
325 if (!$("#tb").length) {
326 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700327 }
Scott Main5a1123e2012-09-26 12:51:28 -0700328 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
329 $('.content-footer.next-class').show();
330 $('.next-page-link').attr('href','')
331 .removeClass("hide").addClass("disabled")
332 .click(function() { return false; });
Scott Main1a00f7f2013-10-29 11:11:19 -0700333 if ($nextLink.length) {
334 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700335 .removeClass("hide")
336 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700337 $('.next-class-link').find('.new').empty();
338 }
Scott Main5a1123e2012-09-26 12:51:28 -0700339 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700340 $('.next-page-link').attr('href', $nextLink.attr('href'))
341 .removeClass("hide");
342 // for the footer link, also add the next page title
343 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700344 }
345
346 if (!startClass && $prevLink.length) {
347 var prevHref = $prevLink.attr('href');
348 if (prevHref == SITE_ROOT + 'index.html') {
349 // Don't show Previous when it leads to the homepage
350 } else {
351 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
352 }
Scott Main3b90aff2013-08-01 18:09:35 -0700353 }
Scott Main5a1123e2012-09-26 12:51:28 -0700354
355 // If this is a training 'article', there should be no prev/next nav
356 // ... if the grandparent is the "nav" ... and it has no child list items...
357 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
358 !$selListItem.find('li').length) {
359 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
360 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700361 }
Scott Main3b90aff2013-08-01 18:09:35 -0700362
Scott Maine4d8f1b2012-06-21 18:03:05 -0700363 }
Scott Main3b90aff2013-08-01 18:09:35 -0700364
365
366
Scott Main5a1123e2012-09-26 12:51:28 -0700367 // Set up the course landing pages for Training with class names and descriptions
368 if ($('body.trainingcourse').length) {
369 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700370
371 // create an array for all the class descriptions
372 var $classDescriptions = new Array($classLinks.length);
373 var lang = getLangPref();
374 $classLinks.each(function(index) {
375 var langDescr = $(this).attr(lang + "-description");
376 if (typeof langDescr !== 'undefined' && langDescr !== false) {
377 // if there's a class description in the selected language, use that
378 $classDescriptions[index] = langDescr;
379 } else {
380 // otherwise, use the default english description
381 $classDescriptions[index] = $(this).attr("description");
382 }
383 });
Scott Main3b90aff2013-08-01 18:09:35 -0700384
Scott Main5a1123e2012-09-26 12:51:28 -0700385 var $olClasses = $('<ol class="class-list"></ol>');
386 var $liClass;
387 var $imgIcon;
388 var $h2Title;
389 var $pSummary;
390 var $olLessons;
391 var $liLesson;
392 $classLinks.each(function(index) {
393 $liClass = $('<li></li>');
394 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700395 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700396
Scott Main5a1123e2012-09-26 12:51:28 -0700397 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700398
Scott Main5a1123e2012-09-26 12:51:28 -0700399 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700400
Scott Main5a1123e2012-09-26 12:51:28 -0700401 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700402 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
403 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700404 $lessons.each(function(index) {
405 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
406 });
407 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700408 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
409 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700410 $pSummary.addClass('article');
411 }
412
413 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
414 $olClasses.append($liClass);
415 });
416 $('.jd-descr').append($olClasses);
417 }
418
Scott Maine4d8f1b2012-06-21 18:03:05 -0700419 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700420 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700421
Scott Main3b90aff2013-08-01 18:09:35 -0700422
Scott Maine4d8f1b2012-06-21 18:03:05 -0700423 $(".scroll-pane").scroll(function(event) {
424 event.preventDefault();
425 return false;
426 });
427
428 /* Resize nav height when window height changes */
429 $(window).resize(function() {
430 if ($('#side-nav').length == 0) return;
431 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
432 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
433 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700434 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700435 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
436 updateSideNavPosition();
437 } else {
438 updateSidenavFullscreenWidth();
439 }
440 }
441 resizeNav();
442 });
443
444
Scott Maine4d8f1b2012-06-21 18:03:05 -0700445 var navBarLeftPos;
446 if ($('#devdoc-nav').length) {
447 setNavBarLeftPos();
448 }
449
450
Scott Maine4d8f1b2012-06-21 18:03:05 -0700451 // Set up play-on-hover <video> tags.
452 $('video.play-on-hover').bind('click', function(){
453 $(this).get(0).load(); // in case the video isn't seekable
454 $(this).get(0).play();
455 });
456
457 // Set up tooltips
458 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700459 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700460 var $target = $(this);
461 var $tooltip = $('<div>')
462 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700463 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700464 .hide()
465 .appendTo('body');
466 $target.removeAttr('title');
467
468 $target.hover(function() {
469 // in
470 var targetRect = $target.offset();
471 targetRect.width = $target.width();
472 targetRect.height = $target.height();
473
474 $tooltip.css({
475 left: targetRect.left,
476 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
477 });
478 $tooltip.addClass('below');
479 $tooltip.show();
480 }, function() {
481 // out
482 $tooltip.hide();
483 });
484 });
485
486 // Set up <h2> deeplinks
487 $('h2').click(function() {
488 var id = $(this).attr('id');
489 if (id) {
490 document.location.hash = id;
491 }
492 });
493
494 //Loads the +1 button
495 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
496 po.src = 'https://apis.google.com/js/plusone.js';
497 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
498
499
Scott Main3b90aff2013-08-01 18:09:35 -0700500 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700501 // which avoids the visible width from changing each time the bar appears
502 var $sidenav = $("#side-nav");
503 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700504
Scott Maine4d8f1b2012-06-21 18:03:05 -0700505 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
506
507
508 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700509
Scott Maine4d8f1b2012-06-21 18:03:05 -0700510 if ($(".scroll-pane").length > 1) {
511 // Check if there's a user preference for the panel heights
512 var cookieHeight = readCookie("reference_height");
513 if (cookieHeight) {
514 restoreHeight(cookieHeight);
515 }
516 }
Scott Main3b90aff2013-08-01 18:09:35 -0700517
Scott Main06f3f2c2014-05-30 11:23:00 -0700518 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700519 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700520 // Check if there's an anchor that we need to scroll into view.
521 // A delay is needed, because some browsers do not immediately scroll down to the anchor
522 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700523
Scott Main015d6162013-01-29 09:01:52 -0800524 /* init the language selector based on user cookie for lang */
525 loadLangPref();
526 changeNavLang(getLangPref());
527
528 /* setup event handlers to ensure the overflow menu is visible while picking lang */
529 $("#language select")
530 .mousedown(function() {
531 $("div.morehover").addClass("hover"); })
532 .blur(function() {
533 $("div.morehover").removeClass("hover"); });
534
535 /* some global variable setup */
536 resizePackagesNav = $("#resize-packages-nav");
537 classesNav = $("#classes-nav");
538 devdocNav = $("#devdoc-nav");
539
540 var cookiePath = "";
541 if (location.href.indexOf("/reference/") != -1) {
542 cookiePath = "reference_";
543 } else if (location.href.indexOf("/guide/") != -1) {
544 cookiePath = "guide_";
545 } else if (location.href.indexOf("/tools/") != -1) {
546 cookiePath = "tools_";
547 } else if (location.href.indexOf("/training/") != -1) {
548 cookiePath = "training_";
549 } else if (location.href.indexOf("/design/") != -1) {
550 cookiePath = "design_";
551 } else if (location.href.indexOf("/distribute/") != -1) {
552 cookiePath = "distribute_";
553 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700554
555});
Scott Main7e447ed2013-02-19 17:22:37 -0800556// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700557
558
Scott Mainad08f072013-08-20 16:49:57 -0700559function initExpandableNavItems(rootTag) {
560 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
561 var section = $(this).closest('li.nav-section');
562 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700563 /* hide me and descendants */
564 section.find('ul').slideUp(250, function() {
565 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700566 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700567 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700568 resizeNav();
569 });
570 } else {
571 /* show me */
572 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700573 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700574 $others.removeClass('expanded').children('ul').slideUp(250);
575
576 // now expand me
577 section.closest('li').addClass('expanded');
578 section.children('ul').slideDown(250, function() {
579 resizeNav();
580 });
581 }
582 });
Scott Mainf0093852013-08-22 11:37:11 -0700583
584 // Stop expand/collapse behavior when clicking on nav section links
585 // (since we're navigating away from the page)
586 // This selector captures the first instance of <a>, but not those with "#" as the href.
587 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
588 window.location.href = $(this).attr('href');
589 return false;
590 });
Scott Mainad08f072013-08-20 16:49:57 -0700591}
592
Dirk Doughertyc3921652014-05-13 16:55:26 -0700593
594/** Create the list of breadcrumb links in the sticky header */
595function buildBreadcrumbs() {
596 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
597 // Add the secondary horizontal nav item, if provided
598 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
599 if ($selectedSecondNav.length) {
600 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
601 }
602 // Add the primary horizontal nav
603 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
604 // If there's no header nav item, use the logo link and title from alt text
605 if ($selectedFirstNav.length < 1) {
606 $selectedFirstNav = $("<a>")
607 .attr('href', $("div#header .logo a").attr('href'))
608 .text($("div#header .logo img").attr('alt'));
609 }
610 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
611}
612
613
614
Scott Maine624b3f2013-09-12 12:56:41 -0700615/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700616function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700617 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
618 if ($("ul#nav li.selected").length) {
619 unHighlightSidenav();
620 }
621 // look for URL in sidenav, including the hash
622 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
623
624 // If the selNavLink is still empty, look for it without the hash
625 if ($selNavLink.length == 0) {
626 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
627 }
628
Scott Mainf6145542013-04-01 16:38:11 -0700629 var $selListItem;
630 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700631 // Find this page's <li> in sidenav and set selected
632 $selListItem = $selNavLink.closest('li');
633 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700634
Scott Mainf6145542013-04-01 16:38:11 -0700635 // Traverse up the tree and expand all parent nav-sections
636 $selNavLink.parents('li.nav-section').each(function() {
637 $(this).addClass('expanded');
638 $(this).children('ul').show();
639 });
640 }
641}
642
Scott Maine624b3f2013-09-12 12:56:41 -0700643function unHighlightSidenav() {
644 $("ul#nav li.selected").removeClass("selected");
645 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
646}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700647
648function toggleFullscreen(enable) {
649 var delay = 20;
650 var enabled = true;
651 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
652 if (enable) {
653 // Currently NOT USING fullscreen; enable fullscreen
654 stylesheet.removeAttr('disabled');
655 $('#nav-swap .fullscreen').removeClass('disabled');
656 $('#devdoc-nav').css({left:''});
657 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
658 enabled = true;
659 } else {
660 // Currently USING fullscreen; disable fullscreen
661 stylesheet.attr('disabled', 'disabled');
662 $('#nav-swap .fullscreen').addClass('disabled');
663 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
664 enabled = false;
665 }
666 writeCookie("fullscreen", enabled, null, null);
667 setNavBarLeftPos();
668 resizeNav(delay);
669 updateSideNavPosition();
670 setTimeout(initSidenavHeightResize,delay);
671}
672
673
674function setNavBarLeftPos() {
675 navBarLeftPos = $('#body-content').offset().left;
676}
677
678
679function updateSideNavPosition() {
680 var newLeft = $(window).scrollLeft() - navBarLeftPos;
681 $('#devdoc-nav').css({left: -newLeft});
682 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
683}
Scott Main3b90aff2013-08-01 18:09:35 -0700684
Scott Maine4d8f1b2012-06-21 18:03:05 -0700685// TODO: use $(document).ready instead
686function addLoadEvent(newfun) {
687 var current = window.onload;
688 if (typeof window.onload != 'function') {
689 window.onload = newfun;
690 } else {
691 window.onload = function() {
692 current();
693 newfun();
694 }
695 }
696}
697
698var agent = navigator['userAgent'].toLowerCase();
699// If a mobile phone, set flag and do mobile setup
700if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
701 (agent.indexOf("blackberry") != -1) ||
702 (agent.indexOf("webos") != -1) ||
703 (agent.indexOf("mini") != -1)) { // opera mini browsers
704 isMobile = true;
705}
706
707
Scott Main498d7102013-08-21 15:47:38 -0700708$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700709 $("pre:not(.no-pretty-print)").addClass("prettyprint");
710 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700711});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700712
Scott Maine4d8f1b2012-06-21 18:03:05 -0700713
714
715
716/* ######### RESIZE THE SIDENAV HEIGHT ########## */
717
718function resizeNav(delay) {
719 var $nav = $("#devdoc-nav");
720 var $window = $(window);
721 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700722
Scott Maine4d8f1b2012-06-21 18:03:05 -0700723 // Get the height of entire window and the total header height.
724 // Then figure out based on scroll position whether the header is visible
725 var windowHeight = $window.height();
726 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700727 var headerHeight = $('#header-wrapper').outerHeight();
728 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700729
730 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700731 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700732 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700733 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700734
Scott Maine4d8f1b2012-06-21 18:03:05 -0700735 // Depending on whether the header is visible, set the side nav's height.
736 if (headerVisible) {
737 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700738 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700739 } else {
740 // Once header is off screen, the nav height is almost full window height
741 navHeight = windowHeight - topMargin;
742 }
Scott Main3b90aff2013-08-01 18:09:35 -0700743
744
745
Scott Maine4d8f1b2012-06-21 18:03:05 -0700746 $scrollPanes = $(".scroll-pane");
747 if ($scrollPanes.length > 1) {
748 // subtract the height of the api level widget and nav swapper from the available nav height
749 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700750
Scott Maine4d8f1b2012-06-21 18:03:05 -0700751 $("#swapper").css({height:navHeight + "px"});
752 if ($("#nav-tree").is(":visible")) {
753 $("#nav-tree").css({height:navHeight});
754 }
Scott Main3b90aff2013-08-01 18:09:35 -0700755
756 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700757 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700758
759 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700760 // then the package panel should begin to shrink
761 if (parseInt(classesHeight) <= 0) {
762 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
763 $("#packages-nav").css({height:navHeight - 10});
764 }
Scott Main3b90aff2013-08-01 18:09:35 -0700765
Scott Maine4d8f1b2012-06-21 18:03:05 -0700766 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
767 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700768
769
Scott Maine4d8f1b2012-06-21 18:03:05 -0700770 } else {
771 $nav.height(navHeight);
772 }
Scott Main3b90aff2013-08-01 18:09:35 -0700773
Scott Maine4d8f1b2012-06-21 18:03:05 -0700774 if (delay) {
775 updateFromResize = true;
776 delayedReInitScrollbars(delay);
777 } else {
778 reInitScrollbars();
779 }
Scott Main3b90aff2013-08-01 18:09:35 -0700780
Scott Maine4d8f1b2012-06-21 18:03:05 -0700781}
782
783var updateScrollbars = false;
784var updateFromResize = false;
785
786/* Re-initialize the scrollbars to account for changed nav size.
787 * This method postpones the actual update by a 1/4 second in order to optimize the
788 * scroll performance while the header is still visible, because re-initializing the
789 * scroll panes is an intensive process.
790 */
791function delayedReInitScrollbars(delay) {
792 // If we're scheduled for an update, but have received another resize request
793 // before the scheduled resize has occured, just ignore the new request
794 // (and wait for the scheduled one).
795 if (updateScrollbars && updateFromResize) {
796 updateFromResize = false;
797 return;
798 }
Scott Main3b90aff2013-08-01 18:09:35 -0700799
Scott Maine4d8f1b2012-06-21 18:03:05 -0700800 // We're scheduled for an update and the update request came from this method's setTimeout
801 if (updateScrollbars && !updateFromResize) {
802 reInitScrollbars();
803 updateScrollbars = false;
804 } else {
805 updateScrollbars = true;
806 updateFromResize = false;
807 setTimeout('delayedReInitScrollbars()',delay);
808 }
809}
810
811/* Re-initialize the scrollbars to account for changed nav size. */
812function reInitScrollbars() {
813 var pane = $(".scroll-pane").each(function(){
814 var api = $(this).data('jsp');
815 if (!api) { setTimeout(reInitScrollbars,300); return;}
816 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700817 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700818 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
819}
820
821
822/* Resize the height of the nav panels in the reference,
823 * and save the new size to a cookie */
824function saveNavPanels() {
825 var basePath = getBaseUri(location.pathname);
826 var section = basePath.substring(1,basePath.indexOf("/",1));
827 writeCookie("height", resizePackagesNav.css("height"), section, null);
828}
829
830
831
832function restoreHeight(packageHeight) {
833 $("#resize-packages-nav").height(packageHeight);
834 $("#packages-nav").height(packageHeight);
835 // var classesHeight = navHeight - packageHeight;
836 // $("#classes-nav").css({height:classesHeight});
837 // $("#classes-nav .jspContainer").css({height:classesHeight});
838}
839
840
841
842/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
843
844
845
846
847
Scott Main3b90aff2013-08-01 18:09:35 -0700848/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700849 This is called when the page finished loading. */
850function scrollIntoView(nav) {
851 var $nav = $("#"+nav);
852 var element = $nav.jScrollPane({/* ...settings... */});
853 var api = element.data('jsp');
854
855 if ($nav.is(':visible')) {
856 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700857 if ($selected.length == 0) {
858 // If no selected item found, exit
859 return;
860 }
Scott Main52dd2062013-08-15 12:22:28 -0700861 // get the selected item's offset from its container nav by measuring the item's offset
862 // relative to the document then subtract the container nav's offset relative to the document
863 var selectedOffset = $selected.offset().top - $nav.offset().top;
864 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
865 // if it's more than 80% down the nav
866 // scroll the item up by an amount equal to 80% the container nav's height
867 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700868 }
869 }
870}
871
872
873
874
875
876
877/* Show popup dialogs */
878function showDialog(id) {
879 $dialog = $("#"+id);
880 $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>');
881 $dialog.wrapInner('<div/>');
882 $dialog.removeClass("hide");
883}
884
885
886
887
888
889/* ######### COOKIES! ########## */
890
891function readCookie(cookie) {
892 var myCookie = cookie_namespace+"_"+cookie+"=";
893 if (document.cookie) {
894 var index = document.cookie.indexOf(myCookie);
895 if (index != -1) {
896 var valStart = index + myCookie.length;
897 var valEnd = document.cookie.indexOf(";", valStart);
898 if (valEnd == -1) {
899 valEnd = document.cookie.length;
900 }
901 var val = document.cookie.substring(valStart, valEnd);
902 return val;
903 }
904 }
905 return 0;
906}
907
908function writeCookie(cookie, val, section, expiration) {
909 if (val==undefined) return;
910 section = section == null ? "_" : "_"+section+"_";
911 if (expiration == null) {
912 var date = new Date();
913 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
914 expiration = date.toGMTString();
915 }
Scott Main3b90aff2013-08-01 18:09:35 -0700916 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700917 + "; expires=" + expiration+"; path=/";
918 document.cookie = cookieValue;
919}
920
921/* ######### END COOKIES! ########## */
922
923
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700924var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -0700925var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700926var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -0700927/* Sets the vertical scoll position at which the sticky bar should appear.
928 This method is called to reset the position when search results appear or hide */
929function setStickyTop() {
930 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
931}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700932
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700933/*
Scott Mainb16376f2014-05-21 20:35:47 -0700934 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -0700935 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700936$(window).scroll(function(event) {
937
938 setStickyTop();
939 var hiding = false;
940 var $stickyEl = $('#sticky-header');
941 var $menuEl = $('.menu-container');
942 // Exit if there's no sidenav
943 if ($('#side-nav').length == 0) return;
944 // Exit if the mouse target is a DIV, because that means the event is coming
945 // from a scrollable div and so there's no need to make adjustments to our layout
946 if ($(event.target).nodeName == "DIV") {
947 return;
948 }
949
950 var top = $(window).scrollTop();
951 // we set the navbar fixed when the scroll position is beyond the height of the site header...
952 var shouldBeSticky = top >= stickyTop;
953 // ... except if the document content is shorter than the sidenav height.
954 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
955 if ($("#doc-col").height() < $("#side-nav").height()) {
956 shouldBeSticky = false;
957 }
Scott Mainf5257812014-05-22 17:26:38 -0700958 // Account for horizontal scroll
959 var scrollLeft = $(window).scrollLeft();
960 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
961 if (sticky && (scrollLeft != prevScrollLeft)) {
962 updateSideNavPosition();
963 prevScrollLeft = scrollLeft;
964 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700965
966 // Don't continue if the header is sufficently far away
967 // (to avoid intensive resizing that slows scrolling)
968 if (sticky == shouldBeSticky) {
969 return;
970 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700971
972 // If sticky header visible and position is now near top, hide sticky
973 if (sticky && !shouldBeSticky) {
974 sticky = false;
975 hiding = true;
976 // make the sidenav static again
977 $('#devdoc-nav')
978 .removeClass('fixed')
979 .css({'width':'auto','margin':''})
980 .prependTo('#side-nav');
981 // delay hide the sticky
982 $menuEl.removeClass('sticky-menu');
983 $stickyEl.fadeOut(250);
984 hiding = false;
985
986 // update the sidenaav position for side scrolling
987 updateSideNavPosition();
988 } else if (!sticky && shouldBeSticky) {
989 sticky = true;
990 $stickyEl.fadeIn(10);
991 $menuEl.addClass('sticky-menu');
992
993 // make the sidenav fixed
994 var width = $('#devdoc-nav').width();
995 $('#devdoc-nav')
996 .addClass('fixed')
997 .css({'width':width+'px'})
998 .prependTo('#body-content');
999
1000 // update the sidenaav position for side scrolling
1001 updateSideNavPosition();
1002
1003 } else if (hiding && top < 15) {
1004 $menuEl.removeClass('sticky-menu');
1005 $stickyEl.hide();
1006 hiding = false;
1007 }
1008 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
1009});
1010
1011/*
1012 * Manages secion card states and nav resize to conclude loading
1013 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001014(function() {
1015 $(document).ready(function() {
1016
Dirk Doughertyc3921652014-05-13 16:55:26 -07001017 // Stack hover states
1018 $('.section-card-menu').each(function(index, el) {
1019 var height = $(el).height();
1020 $(el).css({height:height+'px', position:'relative'});
1021 var $cardInfo = $(el).find('.card-info');
1022
1023 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1024 });
1025
Dirk Doughertyc3921652014-05-13 16:55:26 -07001026 });
1027
1028})();
1029
Scott Maine4d8f1b2012-06-21 18:03:05 -07001030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
Scott Maind7026f72013-06-17 15:08:49 -07001043/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001044
1045
1046
1047
1048
1049function toggle(obj, slide) {
1050 var ul = $("ul:first", obj);
1051 var li = ul.parent();
1052 if (li.hasClass("closed")) {
1053 if (slide) {
1054 ul.slideDown("fast");
1055 } else {
1056 ul.show();
1057 }
1058 li.removeClass("closed");
1059 li.addClass("open");
1060 $(".toggle-img", li).attr("title", "hide pages");
1061 } else {
1062 ul.slideUp("fast");
1063 li.removeClass("open");
1064 li.addClass("closed");
1065 $(".toggle-img", li).attr("title", "show pages");
1066 }
1067}
1068
1069
Scott Maine4d8f1b2012-06-21 18:03:05 -07001070function buildToggleLists() {
1071 $(".toggle-list").each(
1072 function(i) {
1073 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1074 $(this).addClass("closed");
1075 });
1076}
1077
1078
1079
Scott Maind7026f72013-06-17 15:08:49 -07001080function hideNestedItems(list, toggle) {
1081 $list = $(list);
1082 // hide nested lists
1083 if($list.hasClass('showing')) {
1084 $("li ol", $list).hide('fast');
1085 $list.removeClass('showing');
1086 // show nested lists
1087 } else {
1088 $("li ol", $list).show('fast');
1089 $list.addClass('showing');
1090 }
1091 $(".more,.less",$(toggle)).toggle();
1092}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001093
1094
smain@google.com95948b82014-06-16 19:24:25 -07001095/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1096function setupIdeDocToggle() {
1097 $( "select.ide" ).change(function() {
1098 var selected = $(this).find("option:selected").attr("value");
1099 $(".select-ide").hide();
1100 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001101
smain@google.com95948b82014-06-16 19:24:25 -07001102 $("select.ide").val(selected);
1103 });
1104}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129/* REFERENCE NAV SWAP */
1130
1131
1132function getNavPref() {
1133 var v = readCookie('reference_nav');
1134 if (v != NAV_PREF_TREE) {
1135 v = NAV_PREF_PANELS;
1136 }
1137 return v;
1138}
1139
1140function chooseDefaultNav() {
1141 nav_pref = getNavPref();
1142 if (nav_pref == NAV_PREF_TREE) {
1143 $("#nav-panels").toggle();
1144 $("#panel-link").toggle();
1145 $("#nav-tree").toggle();
1146 $("#tree-link").toggle();
1147 }
1148}
1149
1150function swapNav() {
1151 if (nav_pref == NAV_PREF_TREE) {
1152 nav_pref = NAV_PREF_PANELS;
1153 } else {
1154 nav_pref = NAV_PREF_TREE;
1155 init_default_navtree(toRoot);
1156 }
1157 var date = new Date();
1158 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1159 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1160
1161 $("#nav-panels").toggle();
1162 $("#panel-link").toggle();
1163 $("#nav-tree").toggle();
1164 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001165
Scott Maine4d8f1b2012-06-21 18:03:05 -07001166 resizeNav();
1167
1168 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1169 $("#nav-tree .jspContainer:visible")
1170 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1171 // Another nasty hack to make the scrollbar appear now that we have height
1172 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001173
Scott Maine4d8f1b2012-06-21 18:03:05 -07001174 if ($("#nav-tree").is(':visible')) {
1175 scrollIntoView("nav-tree");
1176 } else {
1177 scrollIntoView("packages-nav");
1178 scrollIntoView("classes-nav");
1179 }
1180}
1181
1182
1183
Scott Mainf5089842012-08-14 16:31:07 -07001184/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001185/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001186/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001187
1188function getBaseUri(uri) {
1189 var intlUrl = (uri.substring(0,6) == "/intl/");
1190 if (intlUrl) {
1191 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1192 base = base.substring(base.indexOf('/')+1, base.length);
1193 //alert("intl, returning base url: /" + base);
1194 return ("/" + base);
1195 } else {
1196 //alert("not intl, returning uri as found.");
1197 return uri;
1198 }
1199}
1200
1201function requestAppendHL(uri) {
1202//append "?hl=<lang> to an outgoing request (such as to blog)
1203 var lang = getLangPref();
1204 if (lang) {
1205 var q = 'hl=' + lang;
1206 uri += '?' + q;
1207 window.location = uri;
1208 return false;
1209 } else {
1210 return true;
1211 }
1212}
1213
1214
Scott Maine4d8f1b2012-06-21 18:03:05 -07001215function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001216 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1217 $links.each(function(i){ // for each link with a translation
1218 var $link = $(this);
1219 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1220 // put the desired language from the attribute as the text
1221 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001222 }
Scott Main6eb95f12012-10-02 17:12:23 -07001223 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001224}
1225
Scott Main015d6162013-01-29 09:01:52 -08001226function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001227 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001228 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001229 // keep this for 50 years
1230 //alert("expires: " + expires)
1231 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001232
1233 // ####### TODO: Remove this condition once we're stable on devsite #######
1234 // This condition is only needed if we still need to support legacy GAE server
1235 if (devsite) {
1236 // Switch language when on Devsite server
1237 if (submit) {
1238 $("#setlang").submit();
1239 }
1240 } else {
1241 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001242 if (submit) {
1243 window.location = getBaseUri(location.pathname);
1244 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001245 }
1246}
1247
1248function loadLangPref() {
1249 var lang = readCookie("pref_lang");
1250 if (lang != 0) {
1251 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1252 }
1253}
1254
1255function getLangPref() {
1256 var lang = $("#language").find(":selected").attr("value");
1257 if (!lang) {
1258 lang = readCookie("pref_lang");
1259 }
1260 return (lang != 0) ? lang : 'en';
1261}
1262
1263/* ########## END LOCALIZATION ############ */
1264
1265
1266
1267
1268
1269
1270/* Used to hide and reveal supplemental content, such as long code samples.
1271 See the companion CSS in android-developer-docs.css */
1272function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001273 var div = $(obj).closest(".toggle-content");
1274 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001275 if (div.hasClass("closed")) { // if it's closed, open it
1276 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001277 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001278 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001279 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001280 + "assets/images/triangle-opened.png");
1281 } else { // if it's open, close it
1282 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001283 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001284 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001285 div.find(".toggle-content").removeClass("open").addClass("closed")
1286 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001287 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001288 + "assets/images/triangle-closed.png");
1289 });
1290 }
1291 return false;
1292}
Scott Mainf5089842012-08-14 16:31:07 -07001293
1294
Scott Maindb3678b2012-10-23 14:13:41 -07001295/* New version of expandable content */
1296function toggleExpandable(link,id) {
1297 if($(id).is(':visible')) {
1298 $(id).slideUp();
1299 $(link).removeClass('expanded');
1300 } else {
1301 $(id).slideDown();
1302 $(link).addClass('expanded');
1303 }
1304}
1305
1306function hideExpandable(ids) {
1307 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001308 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001309}
1310
Scott Mainf5089842012-08-14 16:31:07 -07001311
1312
1313
1314
Scott Main3b90aff2013-08-01 18:09:35 -07001315/*
Scott Mainf5089842012-08-14 16:31:07 -07001316 * Slideshow 1.0
1317 * Used on /index.html and /develop/index.html for carousel
1318 *
1319 * Sample usage:
1320 * HTML -
1321 * <div class="slideshow-container">
1322 * <a href="" class="slideshow-prev">Prev</a>
1323 * <a href="" class="slideshow-next">Next</a>
1324 * <ul>
1325 * <li class="item"><img src="images/marquee1.jpg"></li>
1326 * <li class="item"><img src="images/marquee2.jpg"></li>
1327 * <li class="item"><img src="images/marquee3.jpg"></li>
1328 * <li class="item"><img src="images/marquee4.jpg"></li>
1329 * </ul>
1330 * </div>
1331 *
1332 * <script type="text/javascript">
1333 * $('.slideshow-container').dacSlideshow({
1334 * auto: true,
1335 * btnPrev: '.slideshow-prev',
1336 * btnNext: '.slideshow-next'
1337 * });
1338 * </script>
1339 *
1340 * Options:
1341 * btnPrev: optional identifier for previous button
1342 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001343 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001344 * auto: whether or not to auto-proceed
1345 * speed: animation speed
1346 * autoTime: time between auto-rotation
1347 * easing: easing function for transition
1348 * start: item to select by default
1349 * scroll: direction to scroll in
1350 * pagination: whether or not to include dotted pagination
1351 *
1352 */
1353
1354 (function($) {
1355 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001356
Scott Mainf5089842012-08-14 16:31:07 -07001357 //Options - see above
1358 o = $.extend({
1359 btnPrev: null,
1360 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001361 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001362 auto: true,
1363 speed: 500,
1364 autoTime: 12000,
1365 easing: null,
1366 start: 0,
1367 scroll: 1,
1368 pagination: true
1369
1370 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001371
1372 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001373 return this.each(function() {
1374
1375 var running = false;
1376 var animCss = o.vertical ? "top" : "left";
1377 var sizeCss = o.vertical ? "height" : "width";
1378 var div = $(this);
1379 var ul = $("ul", div);
1380 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001381 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001382 var timer = null;
1383
1384 var li = $("li", ul);
1385 var itemLength = li.size();
1386 var curr = o.start;
1387
1388 li.css({float: o.vertical ? "none" : "left"});
1389 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1390 div.css({position: "relative", "z-index": "2", left: "0px"});
1391
1392 var liSize = o.vertical ? height(li) : width(li);
1393 var ulSize = liSize * itemLength;
1394 var divSize = liSize;
1395
1396 li.css({width: li.width(), height: li.height()});
1397 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1398
1399 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001400
Scott Mainf5089842012-08-14 16:31:07 -07001401 //Pagination
1402 if (o.pagination) {
1403 var pagination = $("<div class='pagination'></div>");
1404 var pag_ul = $("<ul></ul>");
1405 if (tl > 1) {
1406 for (var i=0;i<tl;i++) {
1407 var li = $("<li>"+i+"</li>");
1408 pag_ul.append(li);
1409 if (i==o.start) li.addClass('active');
1410 li.click(function() {
1411 go(parseInt($(this).text()));
1412 })
1413 }
1414 pagination.append(pag_ul);
1415 div.append(pagination);
1416 }
1417 }
Scott Main3b90aff2013-08-01 18:09:35 -07001418
Scott Mainf5089842012-08-14 16:31:07 -07001419 //Previous button
1420 if(o.btnPrev)
1421 $(o.btnPrev).click(function(e) {
1422 e.preventDefault();
1423 return go(curr-o.scroll);
1424 });
1425
1426 //Next button
1427 if(o.btnNext)
1428 $(o.btnNext).click(function(e) {
1429 e.preventDefault();
1430 return go(curr+o.scroll);
1431 });
Scott Maineb410352013-01-14 19:03:40 -08001432
1433 //Pause button
1434 if(o.btnPause)
1435 $(o.btnPause).click(function(e) {
1436 e.preventDefault();
1437 if ($(this).hasClass('paused')) {
1438 startRotateTimer();
1439 } else {
1440 pauseRotateTimer();
1441 }
1442 });
Scott Main3b90aff2013-08-01 18:09:35 -07001443
Scott Mainf5089842012-08-14 16:31:07 -07001444 //Auto rotation
1445 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001446
Scott Mainf5089842012-08-14 16:31:07 -07001447 function startRotateTimer() {
1448 clearInterval(timer);
1449 timer = setInterval(function() {
1450 if (curr == tl-1) {
1451 go(0);
1452 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001453 go(curr+o.scroll);
1454 }
Scott Mainf5089842012-08-14 16:31:07 -07001455 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001456 $(o.btnPause).removeClass('paused');
1457 }
1458
1459 function pauseRotateTimer() {
1460 clearInterval(timer);
1461 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001462 }
1463
1464 //Go to an item
1465 function go(to) {
1466 if(!running) {
1467
1468 if(to<0) {
1469 to = itemLength-1;
1470 } else if (to>itemLength-1) {
1471 to = 0;
1472 }
1473 curr = to;
1474
1475 running = true;
1476
1477 ul.animate(
1478 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1479 function() {
1480 running = false;
1481 }
1482 );
1483
1484 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1485 $( (curr-o.scroll<0 && o.btnPrev)
1486 ||
1487 (curr+o.scroll > itemLength && o.btnNext)
1488 ||
1489 []
1490 ).addClass("disabled");
1491
Scott Main3b90aff2013-08-01 18:09:35 -07001492
Scott Mainf5089842012-08-14 16:31:07 -07001493 var nav_items = $('li', pagination);
1494 nav_items.removeClass('active');
1495 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001496
Scott Mainf5089842012-08-14 16:31:07 -07001497
1498 }
1499 if(o.auto) startRotateTimer();
1500 return false;
1501 };
1502 });
1503 };
1504
1505 function css(el, prop) {
1506 return parseInt($.css(el[0], prop)) || 0;
1507 };
1508 function width(el) {
1509 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1510 };
1511 function height(el) {
1512 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1513 };
1514
1515 })(jQuery);
1516
1517
Scott Main3b90aff2013-08-01 18:09:35 -07001518/*
Scott Mainf5089842012-08-14 16:31:07 -07001519 * dacSlideshow 1.0
1520 * Used on develop/index.html for side-sliding tabs
1521 *
1522 * Sample usage:
1523 * HTML -
1524 * <div class="slideshow-container">
1525 * <a href="" class="slideshow-prev">Prev</a>
1526 * <a href="" class="slideshow-next">Next</a>
1527 * <ul>
1528 * <li class="item"><img src="images/marquee1.jpg"></li>
1529 * <li class="item"><img src="images/marquee2.jpg"></li>
1530 * <li class="item"><img src="images/marquee3.jpg"></li>
1531 * <li class="item"><img src="images/marquee4.jpg"></li>
1532 * </ul>
1533 * </div>
1534 *
1535 * <script type="text/javascript">
1536 * $('.slideshow-container').dacSlideshow({
1537 * auto: true,
1538 * btnPrev: '.slideshow-prev',
1539 * btnNext: '.slideshow-next'
1540 * });
1541 * </script>
1542 *
1543 * Options:
1544 * btnPrev: optional identifier for previous button
1545 * btnNext: optional identifier for next button
1546 * auto: whether or not to auto-proceed
1547 * speed: animation speed
1548 * autoTime: time between auto-rotation
1549 * easing: easing function for transition
1550 * start: item to select by default
1551 * scroll: direction to scroll in
1552 * pagination: whether or not to include dotted pagination
1553 *
1554 */
1555 (function($) {
1556 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001557
Scott Mainf5089842012-08-14 16:31:07 -07001558 //Options - see above
1559 o = $.extend({
1560 speed : 250,
1561 easing: null,
1562 nav_id: null,
1563 frame_id: null
1564 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001565
1566 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001567 return this.each(function() {
1568
1569 var curr = 0;
1570 var running = false;
1571 var animCss = "margin-left";
1572 var sizeCss = "width";
1573 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001574
Scott Mainf5089842012-08-14 16:31:07 -07001575 var nav = $(o.nav_id, div);
1576 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001577 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001578 var frame = div.find(o.frame_id);
1579 var content_width = $(frame).find('ul').width();
1580 //Buttons
1581 $(nav_li).click(function(e) {
1582 go($(nav_li).index($(this)));
1583 })
Scott Main3b90aff2013-08-01 18:09:35 -07001584
Scott Mainf5089842012-08-14 16:31:07 -07001585 //Go to an item
1586 function go(to) {
1587 if(!running) {
1588 curr = to;
1589 running = true;
1590
1591 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1592 function() {
1593 running = false;
1594 }
1595 );
1596
Scott Main3b90aff2013-08-01 18:09:35 -07001597
Scott Mainf5089842012-08-14 16:31:07 -07001598 nav_li.removeClass('active');
1599 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001600
Scott Mainf5089842012-08-14 16:31:07 -07001601
1602 }
1603 return false;
1604 };
1605 });
1606 };
1607
1608 function css(el, prop) {
1609 return parseInt($.css(el[0], prop)) || 0;
1610 };
1611 function width(el) {
1612 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1613 };
1614 function height(el) {
1615 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1616 };
1617
1618 })(jQuery);
1619
1620
1621
1622
1623
1624/* ######################################################## */
1625/* ################ SEARCH SUGGESTIONS ################## */
1626/* ######################################################## */
1627
1628
Scott Main7e447ed2013-02-19 17:22:37 -08001629
Scott Main0e76e7e2013-03-12 10:24:07 -07001630var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1631var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1632
Scott Mainf5089842012-08-14 16:31:07 -07001633var gMatches = new Array();
1634var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001635var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001636var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1637var gListLength = 0;
1638
1639
1640var gGoogleMatches = new Array();
1641var ROW_COUNT_GOOGLE = 15; // max number of results in list
1642var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001643
Scott Main0e76e7e2013-03-12 10:24:07 -07001644var gDocsMatches = new Array();
1645var ROW_COUNT_DOCS = 100; // max number of results in list
1646var gDocsListLength = 0;
1647
Scott Mainde295272013-03-25 15:48:35 -07001648function onSuggestionClick(link) {
1649 // When user clicks a suggested document, track it
1650 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1651 'from: ' + $("#search_autocomplete").val()]);
1652}
1653
Scott Mainf5089842012-08-14 16:31:07 -07001654function set_item_selected($li, selected)
1655{
1656 if (selected) {
1657 $li.attr('class','jd-autocomplete jd-selected');
1658 } else {
1659 $li.attr('class','jd-autocomplete');
1660 }
1661}
1662
1663function set_item_values(toroot, $li, match)
1664{
1665 var $link = $('a',$li);
1666 $link.html(match.__hilabel || match.label);
1667 $link.attr('href',toroot + match.link);
1668}
1669
Scott Main719acb42013-12-05 16:05:09 -08001670function set_item_values_jd(toroot, $li, match)
1671{
1672 var $link = $('a',$li);
1673 $link.html(match.title);
1674 $link.attr('href',toroot + match.url);
1675}
1676
Scott Main0e76e7e2013-03-12 10:24:07 -07001677function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001678 var $li = $("<li class='jd-autocomplete'></li>");
1679 $list.append($li);
1680
1681 $li.mousedown(function() {
1682 window.location = this.firstChild.getAttribute("href");
1683 });
1684 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001685 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001686 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001687 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1688 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001689 });
Scott Mainde295272013-03-25 15:48:35 -07001690 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001691 $li.attr('class','show-item');
1692 return $li;
1693}
1694
Scott Mainf5089842012-08-14 16:31:07 -07001695function sync_selection_table(toroot)
1696{
Scott Mainf5089842012-08-14 16:31:07 -07001697 var $li; //list item jquery object
1698 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001699
Scott Main0e76e7e2013-03-12 10:24:07 -07001700 // if there are NO results at all, hide all columns
1701 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1702 $('.suggest-card').hide(300);
1703 return;
1704 }
1705
1706 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001707 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001708 // reveal suggestion list
1709 $('.suggest-card.dummy').show();
1710 $('.suggest-card.reference').show();
1711 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001712
Scott Main0e76e7e2013-03-12 10:24:07 -07001713 // reset the lists
1714 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001715
Scott Main0e76e7e2013-03-12 10:24:07 -07001716 // ########### ANDROID RESULTS #############
1717 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001718
Scott Main0e76e7e2013-03-12 10:24:07 -07001719 // determine android results to show
1720 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1721 gMatches.length : ROW_COUNT_FRAMEWORK;
1722 for (i=0; i<gListLength; i++) {
1723 var $li = new_suggestion($(".suggest-card.reference ul"));
1724 set_item_values(toroot, $li, gMatches[i]);
1725 set_item_selected($li, i == gSelectedIndex);
1726 }
1727 }
Scott Main7e447ed2013-02-19 17:22:37 -08001728
Scott Main0e76e7e2013-03-12 10:24:07 -07001729 // ########### GOOGLE RESULTS #############
1730 if (gGoogleMatches.length > 0) {
1731 // show header for list
1732 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001733
Scott Main0e76e7e2013-03-12 10:24:07 -07001734 // determine google results to show
1735 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1736 for (i=0; i<gGoogleListLength; i++) {
1737 var $li = new_suggestion($(".suggest-card.reference ul"));
1738 set_item_values(toroot, $li, gGoogleMatches[i]);
1739 set_item_selected($li, i == gSelectedIndex);
1740 }
1741 }
Scott Mainf5089842012-08-14 16:31:07 -07001742 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001743 $('.suggest-card.reference').hide();
1744 $('.suggest-card.dummy').hide();
1745 }
1746
1747 // ########### JD DOC RESULTS #############
1748 if (gDocsMatches.length > 0) {
1749 // reset the lists
1750 $(".search_filtered_wrapper.docs li").remove();
1751
1752 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001753 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1754 // The order must match the reverse order that each section appears as a card in
1755 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001756 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1757 for (i=0; i<gDocsListLength; i++) {
1758 var sugg = gDocsMatches[i];
1759 var $li;
1760 if (sugg.type == "design") {
1761 $li = new_suggestion($(".suggest-card.design ul"));
1762 } else
1763 if (sugg.type == "distribute") {
1764 $li = new_suggestion($(".suggest-card.distribute ul"));
1765 } else
Scott Main719acb42013-12-05 16:05:09 -08001766 if (sugg.type == "samples") {
1767 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1768 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001769 if (sugg.type == "training") {
1770 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1771 } else
Scott Main719acb42013-12-05 16:05:09 -08001772 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001773 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1774 } else {
1775 continue;
1776 }
1777
Scott Main719acb42013-12-05 16:05:09 -08001778 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001779 set_item_selected($li, i == gSelectedIndex);
1780 }
1781
1782 // add heading and show or hide card
1783 if ($(".suggest-card.design li").length > 0) {
1784 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1785 $(".suggest-card.design").show(300);
1786 } else {
1787 $('.suggest-card.design').hide(300);
1788 }
1789 if ($(".suggest-card.distribute li").length > 0) {
1790 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1791 $(".suggest-card.distribute").show(300);
1792 } else {
1793 $('.suggest-card.distribute').hide(300);
1794 }
1795 if ($(".child-card.guides li").length > 0) {
1796 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1797 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1798 }
1799 if ($(".child-card.training li").length > 0) {
1800 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1801 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1802 }
Scott Main719acb42013-12-05 16:05:09 -08001803 if ($(".child-card.samples li").length > 0) {
1804 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1805 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1806 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001807
1808 if ($(".suggest-card.develop li").length > 0) {
1809 $(".suggest-card.develop").show(300);
1810 } else {
1811 $('.suggest-card.develop').hide(300);
1812 }
1813
1814 } else {
1815 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001816 }
1817}
1818
Scott Main0e76e7e2013-03-12 10:24:07 -07001819/** Called by the search input's onkeydown and onkeyup events.
1820 * Handles navigation with keyboard arrows, Enter key to invoke search,
1821 * otherwise invokes search suggestions on key-up event.
1822 * @param e The JS event
1823 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001824 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001825 * @returns True if the event should bubble up
1826 */
Scott Mainf5089842012-08-14 16:31:07 -07001827function search_changed(e, kd, toroot)
1828{
Scott Main719acb42013-12-05 16:05:09 -08001829 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001830 var search = document.getElementById("search_autocomplete");
1831 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001832 // get the ul hosting the currently selected item
1833 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1834 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1835 var $selectedUl = $columns[gSelectedColumn];
1836
Scott Mainf5089842012-08-14 16:31:07 -07001837 // show/hide the close button
1838 if (text != '') {
1839 $(".search .close").removeClass("hide");
1840 } else {
1841 $(".search .close").addClass("hide");
1842 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001843 // 27 = esc
1844 if (e.keyCode == 27) {
1845 // close all search results
1846 if (kd) $('.search .close').trigger('click');
1847 return true;
1848 }
Scott Mainf5089842012-08-14 16:31:07 -07001849 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001850 else if (e.keyCode == 13) {
1851 if (gSelectedIndex < 0) {
1852 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001853 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1854 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001855 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001856 return true;
1857 } else {
1858 // otherwise, results are already showing, so allow ajax to auto refresh the results
1859 // and ignore this Enter press to avoid the reload.
1860 return false;
1861 }
1862 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001863 // click the link corresponding to selected item
1864 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001865 return false;
1866 }
1867 }
Scott Mainb16376f2014-05-21 20:35:47 -07001868 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001869 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001870 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001871 if ((sticky ) && (search.value != "")) {
1872 $('body,html').animate({scrollTop:0}, '500', 'swing');
1873 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001874 return true;
1875 }
1876 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001877 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001878 // if the next item is a header, skip it
1879 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001880 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001881 }
1882 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001883 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001884 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001885 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1886 // If user reaches top, reset selected column
1887 if (gSelectedIndex < 0) {
1888 gSelectedColumn = -1;
1889 }
Scott Mainf5089842012-08-14 16:31:07 -07001890 }
1891 return false;
1892 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001893 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001894 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001895 // if the next item is a header, skip it
1896 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001897 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001898 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001899 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1900 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1901 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001902 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001903 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001904 }
1905 return false;
1906 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001907 // Consider left/right arrow navigation
1908 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1909 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1910 // 37 LEFT ARROW
1911 // go left only if current column is not left-most column (last column)
1912 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1913 $('li', $selectedUl).removeClass('jd-selected');
1914 gSelectedColumn++;
1915 $selectedUl = $columns[gSelectedColumn];
1916 // keep or reset the selected item to last item as appropriate
1917 gSelectedIndex = gSelectedIndex >
1918 $("li", $selectedUl).length-1 ?
1919 $("li", $selectedUl).length-1 : gSelectedIndex;
1920 // if the corresponding item is a header, move down
1921 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1922 gSelectedIndex++;
1923 }
Scott Main3b90aff2013-08-01 18:09:35 -07001924 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001925 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1926 return false;
1927 }
1928 // 39 RIGHT ARROW
1929 // go right only if current column is not the right-most column (first column)
1930 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1931 $('li', $selectedUl).removeClass('jd-selected');
1932 gSelectedColumn--;
1933 $selectedUl = $columns[gSelectedColumn];
1934 // keep or reset the selected item to last item as appropriate
1935 gSelectedIndex = gSelectedIndex >
1936 $("li", $selectedUl).length-1 ?
1937 $("li", $selectedUl).length-1 : gSelectedIndex;
1938 // if the corresponding item is a header, move down
1939 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1940 gSelectedIndex++;
1941 }
Scott Main3b90aff2013-08-01 18:09:35 -07001942 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001943 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1944 return false;
1945 }
1946 }
1947
Scott Main719acb42013-12-05 16:05:09 -08001948 // if key-up event and not arrow down/up/left/right,
1949 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001950 else if (!kd && (e.keyCode != 40)
1951 && (e.keyCode != 38)
1952 && (e.keyCode != 37)
1953 && (e.keyCode != 39)) {
1954 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001955 gMatches = new Array();
1956 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001957 gGoogleMatches = new Array();
1958 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001959 gDocsMatches = new Array();
1960 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001961
1962 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001963 for (var i=0; i<DATA.length; i++) {
1964 var s = DATA[i];
1965 if (text.length != 0 &&
1966 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1967 gMatches[matchedCount] = s;
1968 matchedCount++;
1969 }
1970 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001971 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001972 for (var i=0; i<gMatches.length; i++) {
1973 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001974 }
1975
1976
1977 // Search for Google matches
1978 for (var i=0; i<GOOGLE_DATA.length; i++) {
1979 var s = GOOGLE_DATA[i];
1980 if (text.length != 0 &&
1981 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1982 gGoogleMatches[matchedCountGoogle] = s;
1983 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001984 }
1985 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001986 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001987 for (var i=0; i<gGoogleMatches.length; i++) {
1988 var s = gGoogleMatches[i];
1989 }
1990
Scott Mainf5089842012-08-14 16:31:07 -07001991 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001992
1993
1994
Scott Main719acb42013-12-05 16:05:09 -08001995 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07001996 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08001997 // Regex to match only the beginning of a word
1998 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1999
2000
2001 // Search for Training classes
2002 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002003 // current search comparison, with counters for tag and title,
2004 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002005 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002006 s.matched_tag = 0;
2007 s.matched_title = 0;
2008 var matched = false;
2009
2010 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08002011 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002012 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08002013 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002014 matched = true;
2015 s.matched_tag = j + 1; // add 1 to index position
2016 }
2017 }
Scott Main719acb42013-12-05 16:05:09 -08002018 // Don't consider doc title for lessons (only for class landing pages),
2019 // unless the lesson has a tag that already matches
2020 if ((s.lang == currentLang) &&
2021 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002022 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08002023 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002024 matched = true;
2025 s.matched_title = 1;
2026 }
2027 }
2028 if (matched) {
2029 gDocsMatches[matchedCountDocs] = s;
2030 matchedCountDocs++;
2031 }
2032 }
Scott Main719acb42013-12-05 16:05:09 -08002033
2034
2035 // Search for API Guides
2036 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2037 // current search comparison, with counters for tag and title,
2038 // used later to improve ranking
2039 var s = GUIDE_RESOURCES[i];
2040 s.matched_tag = 0;
2041 s.matched_title = 0;
2042 var matched = false;
2043
2044 // Check if query matches any tags; work backwards toward 1 to assist ranking
2045 for (var j = s.keywords.length - 1; j >= 0; j--) {
2046 // it matches a tag
2047 if (s.keywords[j].toLowerCase().match(textRegex)) {
2048 matched = true;
2049 s.matched_tag = j + 1; // add 1 to index position
2050 }
2051 }
2052 // Check if query matches the doc title, but only for current language
2053 if (s.lang == currentLang) {
2054 // if query matches the doc title
2055 if (s.title.toLowerCase().match(textRegex)) {
2056 matched = true;
2057 s.matched_title = 1;
2058 }
2059 }
2060 if (matched) {
2061 gDocsMatches[matchedCountDocs] = s;
2062 matchedCountDocs++;
2063 }
2064 }
2065
2066
2067 // Search for Tools Guides
2068 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2069 // current search comparison, with counters for tag and title,
2070 // used later to improve ranking
2071 var s = TOOLS_RESOURCES[i];
2072 s.matched_tag = 0;
2073 s.matched_title = 0;
2074 var matched = false;
2075
2076 // Check if query matches any tags; work backwards toward 1 to assist ranking
2077 for (var j = s.keywords.length - 1; j >= 0; j--) {
2078 // it matches a tag
2079 if (s.keywords[j].toLowerCase().match(textRegex)) {
2080 matched = true;
2081 s.matched_tag = j + 1; // add 1 to index position
2082 }
2083 }
2084 // Check if query matches the doc title, but only for current language
2085 if (s.lang == currentLang) {
2086 // if query matches the doc title
2087 if (s.title.toLowerCase().match(textRegex)) {
2088 matched = true;
2089 s.matched_title = 1;
2090 }
2091 }
2092 if (matched) {
2093 gDocsMatches[matchedCountDocs] = s;
2094 matchedCountDocs++;
2095 }
2096 }
2097
2098
2099 // Search for About docs
2100 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2101 // current search comparison, with counters for tag and title,
2102 // used later to improve ranking
2103 var s = ABOUT_RESOURCES[i];
2104 s.matched_tag = 0;
2105 s.matched_title = 0;
2106 var matched = false;
2107
2108 // Check if query matches any tags; work backwards toward 1 to assist ranking
2109 for (var j = s.keywords.length - 1; j >= 0; j--) {
2110 // it matches a tag
2111 if (s.keywords[j].toLowerCase().match(textRegex)) {
2112 matched = true;
2113 s.matched_tag = j + 1; // add 1 to index position
2114 }
2115 }
2116 // Check if query matches the doc title, but only for current language
2117 if (s.lang == currentLang) {
2118 // if query matches the doc title
2119 if (s.title.toLowerCase().match(textRegex)) {
2120 matched = true;
2121 s.matched_title = 1;
2122 }
2123 }
2124 if (matched) {
2125 gDocsMatches[matchedCountDocs] = s;
2126 matchedCountDocs++;
2127 }
2128 }
2129
2130
2131 // Search for Design guides
2132 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2133 // current search comparison, with counters for tag and title,
2134 // used later to improve ranking
2135 var s = DESIGN_RESOURCES[i];
2136 s.matched_tag = 0;
2137 s.matched_title = 0;
2138 var matched = false;
2139
2140 // Check if query matches any tags; work backwards toward 1 to assist ranking
2141 for (var j = s.keywords.length - 1; j >= 0; j--) {
2142 // it matches a tag
2143 if (s.keywords[j].toLowerCase().match(textRegex)) {
2144 matched = true;
2145 s.matched_tag = j + 1; // add 1 to index position
2146 }
2147 }
2148 // Check if query matches the doc title, but only for current language
2149 if (s.lang == currentLang) {
2150 // if query matches the doc title
2151 if (s.title.toLowerCase().match(textRegex)) {
2152 matched = true;
2153 s.matched_title = 1;
2154 }
2155 }
2156 if (matched) {
2157 gDocsMatches[matchedCountDocs] = s;
2158 matchedCountDocs++;
2159 }
2160 }
2161
2162
2163 // Search for Distribute guides
2164 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2165 // current search comparison, with counters for tag and title,
2166 // used later to improve ranking
2167 var s = DISTRIBUTE_RESOURCES[i];
2168 s.matched_tag = 0;
2169 s.matched_title = 0;
2170 var matched = false;
2171
2172 // Check if query matches any tags; work backwards toward 1 to assist ranking
2173 for (var j = s.keywords.length - 1; j >= 0; j--) {
2174 // it matches a tag
2175 if (s.keywords[j].toLowerCase().match(textRegex)) {
2176 matched = true;
2177 s.matched_tag = j + 1; // add 1 to index position
2178 }
2179 }
2180 // Check if query matches the doc title, but only for current language
2181 if (s.lang == currentLang) {
2182 // if query matches the doc title
2183 if (s.title.toLowerCase().match(textRegex)) {
2184 matched = true;
2185 s.matched_title = 1;
2186 }
2187 }
2188 if (matched) {
2189 gDocsMatches[matchedCountDocs] = s;
2190 matchedCountDocs++;
2191 }
2192 }
2193
2194
2195 // Search for Google guides
2196 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2197 // current search comparison, with counters for tag and title,
2198 // used later to improve ranking
2199 var s = GOOGLE_RESOURCES[i];
2200 s.matched_tag = 0;
2201 s.matched_title = 0;
2202 var matched = false;
2203
2204 // Check if query matches any tags; work backwards toward 1 to assist ranking
2205 for (var j = s.keywords.length - 1; j >= 0; j--) {
2206 // it matches a tag
2207 if (s.keywords[j].toLowerCase().match(textRegex)) {
2208 matched = true;
2209 s.matched_tag = j + 1; // add 1 to index position
2210 }
2211 }
2212 // Check if query matches the doc title, but only for current language
2213 if (s.lang == currentLang) {
2214 // if query matches the doc title
2215 if (s.title.toLowerCase().match(textRegex)) {
2216 matched = true;
2217 s.matched_title = 1;
2218 }
2219 }
2220 if (matched) {
2221 gDocsMatches[matchedCountDocs] = s;
2222 matchedCountDocs++;
2223 }
2224 }
2225
2226
2227 // Search for Samples
2228 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2229 // current search comparison, with counters for tag and title,
2230 // used later to improve ranking
2231 var s = SAMPLES_RESOURCES[i];
2232 s.matched_tag = 0;
2233 s.matched_title = 0;
2234 var matched = false;
2235 // Check if query matches any tags; work backwards toward 1 to assist ranking
2236 for (var j = s.keywords.length - 1; j >= 0; j--) {
2237 // it matches a tag
2238 if (s.keywords[j].toLowerCase().match(textRegex)) {
2239 matched = true;
2240 s.matched_tag = j + 1; // add 1 to index position
2241 }
2242 }
2243 // Check if query matches the doc title, but only for current language
2244 if (s.lang == currentLang) {
2245 // if query matches the doc title.t
2246 if (s.title.toLowerCase().match(textRegex)) {
2247 matched = true;
2248 s.matched_title = 1;
2249 }
2250 }
2251 if (matched) {
2252 gDocsMatches[matchedCountDocs] = s;
2253 matchedCountDocs++;
2254 }
2255 }
2256
2257 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002258 rank_autocomplete_doc_results(text, gDocsMatches);
2259 }
2260
2261 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002262 sync_selection_table(toroot);
2263 return true; // allow the event to bubble up to the search api
2264 }
2265}
2266
Scott Main0e76e7e2013-03-12 10:24:07 -07002267/* Order the jd doc result list based on match quality */
2268function rank_autocomplete_doc_results(query, matches) {
2269 query = query || '';
2270 if (!matches || !matches.length)
2271 return;
2272
2273 var _resultScoreFn = function(match) {
2274 var score = 1.0;
2275
2276 // if the query matched a tag
2277 if (match.matched_tag > 0) {
2278 // multiply score by factor relative to position in tags list (max of 3)
2279 score *= 3 / match.matched_tag;
2280
2281 // if it also matched the title
2282 if (match.matched_title > 0) {
2283 score *= 2;
2284 }
2285 } else if (match.matched_title > 0) {
2286 score *= 3;
2287 }
2288
2289 return score;
2290 };
2291
2292 for (var i=0; i<matches.length; i++) {
2293 matches[i].__resultScore = _resultScoreFn(matches[i]);
2294 }
2295
2296 matches.sort(function(a,b){
2297 var n = b.__resultScore - a.__resultScore;
2298 if (n == 0) // lexicographical sort if scores are the same
2299 n = (a.label < b.label) ? -1 : 1;
2300 return n;
2301 });
2302}
2303
Scott Main7e447ed2013-02-19 17:22:37 -08002304/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002305function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002306 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002307 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002308 return;
2309
2310 // helper function that gets the last occurence index of the given regex
2311 // in the given string, or -1 if not found
2312 var _lastSearch = function(s, re) {
2313 if (s == '')
2314 return -1;
2315 var l = -1;
2316 var tmp;
2317 while ((tmp = s.search(re)) >= 0) {
2318 if (l < 0) l = 0;
2319 l += tmp;
2320 s = s.substr(tmp + 1);
2321 }
2322 return l;
2323 };
2324
2325 // helper function that counts the occurrences of a given character in
2326 // a given string
2327 var _countChar = function(s, c) {
2328 var n = 0;
2329 for (var i=0; i<s.length; i++)
2330 if (s.charAt(i) == c) ++n;
2331 return n;
2332 };
2333
2334 var queryLower = query.toLowerCase();
2335 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2336 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2337 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2338
2339 var _resultScoreFn = function(result) {
2340 // scores are calculated based on exact and prefix matches,
2341 // and then number of path separators (dots) from the last
2342 // match (i.e. favoring classes and deep package names)
2343 var score = 1.0;
2344 var labelLower = result.label.toLowerCase();
2345 var t;
2346 t = _lastSearch(labelLower, partExactAlnumRE);
2347 if (t >= 0) {
2348 // exact part match
2349 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2350 score *= 200 / (partsAfter + 1);
2351 } else {
2352 t = _lastSearch(labelLower, partPrefixAlnumRE);
2353 if (t >= 0) {
2354 // part prefix match
2355 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2356 score *= 20 / (partsAfter + 1);
2357 }
2358 }
2359
2360 return score;
2361 };
2362
Scott Main7e447ed2013-02-19 17:22:37 -08002363 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002364 // if the API is deprecated, default score is 0; otherwise, perform scoring
2365 if (matches[i].deprecated == "true") {
2366 matches[i].__resultScore = 0;
2367 } else {
2368 matches[i].__resultScore = _resultScoreFn(matches[i]);
2369 }
Scott Mainf5089842012-08-14 16:31:07 -07002370 }
2371
Scott Main7e447ed2013-02-19 17:22:37 -08002372 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002373 var n = b.__resultScore - a.__resultScore;
2374 if (n == 0) // lexicographical sort if scores are the same
2375 n = (a.label < b.label) ? -1 : 1;
2376 return n;
2377 });
2378}
2379
Scott Main7e447ed2013-02-19 17:22:37 -08002380/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002381function highlight_autocomplete_result_labels(query) {
2382 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002383 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002384 return;
2385
2386 var queryLower = query.toLowerCase();
2387 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2388 var queryRE = new RegExp(
2389 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2390 for (var i=0; i<gMatches.length; i++) {
2391 gMatches[i].__hilabel = gMatches[i].label.replace(
2392 queryRE, '<b>$1</b>');
2393 }
Scott Main7e447ed2013-02-19 17:22:37 -08002394 for (var i=0; i<gGoogleMatches.length; i++) {
2395 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2396 queryRE, '<b>$1</b>');
2397 }
Scott Mainf5089842012-08-14 16:31:07 -07002398}
2399
2400function search_focus_changed(obj, focused)
2401{
Scott Main3b90aff2013-08-01 18:09:35 -07002402 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002403 if(obj.value == ""){
2404 $(".search .close").addClass("hide");
2405 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002406 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002407 }
2408}
2409
2410function submit_search() {
2411 var query = document.getElementById('search_autocomplete').value;
2412 location.hash = 'q=' + query;
2413 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002414 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002415 return false;
2416}
2417
2418
2419function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002420 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002421 $(".search .close").addClass("hide");
2422 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002423
Scott Mainf5089842012-08-14 16:31:07 -07002424 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002425
Scott Mainf5089842012-08-14 16:31:07 -07002426 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2427 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002428
2429 // forcefully regain key-up event control (previously jacked by search api)
2430 $("#search_autocomplete").keyup(function(event) {
2431 return search_changed(event, false, toRoot);
2432 });
2433
Scott Mainf5089842012-08-14 16:31:07 -07002434 return false;
2435}
2436
2437
2438
2439/* ########################################################## */
2440/* ################ CUSTOM SEARCH ENGINE ################## */
2441/* ########################################################## */
2442
Scott Mainf5089842012-08-14 16:31:07 -07002443var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002444google.load('search', '1', {"callback" : function() {
2445 searchControl = new google.search.SearchControl();
2446 } });
Scott Mainf5089842012-08-14 16:31:07 -07002447
2448function loadSearchResults() {
2449 document.getElementById("search_autocomplete").style.color = "#000";
2450
Scott Mainf5089842012-08-14 16:31:07 -07002451 searchControl = new google.search.SearchControl();
2452
2453 // use our existing search form and use tabs when multiple searchers are used
2454 drawOptions = new google.search.DrawOptions();
2455 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2456 drawOptions.setInput(document.getElementById("search_autocomplete"));
2457
2458 // configure search result options
2459 searchOptions = new google.search.SearcherOptions();
2460 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2461
2462 // configure each of the searchers, for each tab
2463 devSiteSearcher = new google.search.WebSearch();
2464 devSiteSearcher.setUserDefinedLabel("All");
2465 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2466
2467 designSearcher = new google.search.WebSearch();
2468 designSearcher.setUserDefinedLabel("Design");
2469 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2470
2471 trainingSearcher = new google.search.WebSearch();
2472 trainingSearcher.setUserDefinedLabel("Training");
2473 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2474
2475 guidesSearcher = new google.search.WebSearch();
2476 guidesSearcher.setUserDefinedLabel("Guides");
2477 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2478
2479 referenceSearcher = new google.search.WebSearch();
2480 referenceSearcher.setUserDefinedLabel("Reference");
2481 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2482
Scott Maindf08ada2012-12-03 08:54:37 -08002483 googleSearcher = new google.search.WebSearch();
2484 googleSearcher.setUserDefinedLabel("Google Services");
2485 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2486
Scott Mainf5089842012-08-14 16:31:07 -07002487 blogSearcher = new google.search.WebSearch();
2488 blogSearcher.setUserDefinedLabel("Blog");
2489 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2490
2491 // add each searcher to the search control
2492 searchControl.addSearcher(devSiteSearcher, searchOptions);
2493 searchControl.addSearcher(designSearcher, searchOptions);
2494 searchControl.addSearcher(trainingSearcher, searchOptions);
2495 searchControl.addSearcher(guidesSearcher, searchOptions);
2496 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002497 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002498 searchControl.addSearcher(blogSearcher, searchOptions);
2499
2500 // configure result options
2501 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2502 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2503 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2504 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2505
2506 // upon ajax search, refresh the url and search title
2507 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2508 updateResultTitle(query);
2509 var query = document.getElementById('search_autocomplete').value;
2510 location.hash = 'q=' + query;
2511 });
2512
Scott Mainde295272013-03-25 15:48:35 -07002513 // once search results load, set up click listeners
2514 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2515 addResultClickListeners();
2516 });
2517
Scott Mainf5089842012-08-14 16:31:07 -07002518 // draw the search results box
2519 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2520
2521 // get query and execute the search
2522 searchControl.execute(decodeURI(getQuery(location.hash)));
2523
2524 document.getElementById("search_autocomplete").focus();
2525 addTabListeners();
2526}
2527// End of loadSearchResults
2528
2529
2530google.setOnLoadCallback(function(){
2531 if (location.hash.indexOf("q=") == -1) {
2532 // if there's no query in the url, don't search and make sure results are hidden
2533 $('#searchResults').hide();
2534 return;
2535 } else {
2536 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002537 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002538 $(".search .close").removeClass("hide");
2539 loadSearchResults();
2540 }
2541}, true);
2542
Scott Mainb16376f2014-05-21 20:35:47 -07002543/* Adjust the scroll position to account for sticky header, only if the hash matches an id */
2544function offsetScrollForSticky() {
smain@google.com3b77ab52014-06-17 11:57:27 -07002545 var hash = escape(location.hash.substr(1));
2546 var $matchingElement = $("#"+hash);
Scott Mainb16376f2014-05-21 20:35:47 -07002547 // If there's no element with the hash as an ID, then look for an <a name=''> with it.
2548 if ($matchingElement.length < 1) {
smain@google.com3b77ab52014-06-17 11:57:27 -07002549 $matchingElement = $('a[name="' + hash + '"]');
Scott Mainb16376f2014-05-21 20:35:47 -07002550 }
smain@google.com3b77ab52014-06-17 11:57:27 -07002551 // Sanity check that there's an element with that ID on the page
2552 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002553 // If the position of the target element is near the top of the page (<20px, where we expect it
2554 // to be because we need to move it down 60px to become in view), then move it down 60px
2555 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2556 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002557 }
2558 }
2559}
2560
Scott Mainf5089842012-08-14 16:31:07 -07002561// when an event on the browser history occurs (back, forward, load) requery hash and do search
2562$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002563 // If the hash isn't a search query or there's an error in the query,
2564 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002565 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2566 // If the results pane is open, close it.
2567 if (!$("#searchResults").is(":hidden")) {
2568 hideResults();
2569 }
Scott Mainb16376f2014-05-21 20:35:47 -07002570 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002571 return;
2572 }
2573
2574 // Otherwise, we have a search to do
2575 var query = decodeURI(getQuery(location.hash));
2576 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002577 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002578 $("#search_autocomplete").focus();
2579 $(".search .close").removeClass("hide");
2580
2581 updateResultTitle(query);
2582});
2583
2584function updateResultTitle(query) {
2585 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2586}
2587
2588// forcefully regain key-up event control (previously jacked by search api)
2589$("#search_autocomplete").keyup(function(event) {
2590 return search_changed(event, false, toRoot);
2591});
2592
2593// add event listeners to each tab so we can track the browser history
2594function addTabListeners() {
2595 var tabHeaders = $(".gsc-tabHeader");
2596 for (var i = 0; i < tabHeaders.length; i++) {
2597 $(tabHeaders[i]).attr("id",i).click(function() {
2598 /*
2599 // make a copy of the page numbers for the search left pane
2600 setTimeout(function() {
2601 // remove any residual page numbers
2602 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002603 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002604 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002605 // and because we're going to remove it (previous line),
2606 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002607 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2608 .clone().appendTo('#searchResults .gsc-tabsArea');
2609 }, 200);
2610 */
2611 });
2612 }
2613 setTimeout(function(){$(tabHeaders[0]).click()},200);
2614}
2615
Scott Mainde295272013-03-25 15:48:35 -07002616// add analytics tracking events to each result link
2617function addResultClickListeners() {
2618 $("#searchResults a.gs-title").each(function(index, link) {
2619 // When user clicks enter for Google search results, track it
2620 $(link).click(function() {
2621 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2622 'from: ' + $("#search_autocomplete").val()]);
2623 });
2624 });
2625}
2626
Scott Mainf5089842012-08-14 16:31:07 -07002627
2628function getQuery(hash) {
2629 var queryParts = hash.split('=');
2630 return queryParts[1];
2631}
2632
2633/* returns the given string with all HTML brackets converted to entities
2634 TODO: move this to the site's JS library */
2635function escapeHTML(string) {
2636 return string.replace(/</g,"&lt;")
2637 .replace(/>/g,"&gt;");
2638}
2639
2640
2641
2642
2643
2644
2645
2646/* ######################################################## */
2647/* ################# JAVADOC REFERENCE ################### */
2648/* ######################################################## */
2649
Scott Main65511c02012-09-07 15:51:32 -07002650/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002651if (location.pathname.indexOf("/reference") == 0) {
2652 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2653 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2654 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002655 $(document).ready(function() {
2656 // init available apis based on user pref
2657 changeApiLevel();
2658 initSidenavHeightResize()
2659 });
2660 }
Scott Main65511c02012-09-07 15:51:32 -07002661}
Scott Mainf5089842012-08-14 16:31:07 -07002662
2663var API_LEVEL_COOKIE = "api_level";
2664var minLevel = 1;
2665var maxLevel = 1;
2666
2667/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002668
Scott Mainf5089842012-08-14 16:31:07 -07002669 function initSidenavHeightResize() {
2670 // Change the drag bar size to nicely fit the scrollbar positions
2671 var $dragBar = $(".ui-resizable-s");
2672 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002673
2674 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002675 containment: "#nav-panels",
2676 handles: "s",
2677 alsoResize: "#packages-nav",
2678 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2679 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2680 });
Scott Main3b90aff2013-08-01 18:09:35 -07002681
Scott Mainf5089842012-08-14 16:31:07 -07002682 }
Scott Main3b90aff2013-08-01 18:09:35 -07002683
Scott Mainf5089842012-08-14 16:31:07 -07002684function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002685 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002686 $('#devdoc-nav').css({
2687 'width' : $('#side-nav').css('width'),
2688 'margin' : $('#side-nav').css('margin')
2689 });
2690 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002691
Scott Mainf5089842012-08-14 16:31:07 -07002692 initSidenavHeightResize();
2693}
2694
2695function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002696 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002697 $('#devdoc-nav').css({
2698 'width' : $('#side-nav').css('width'),
2699 'margin' : $('#side-nav').css('margin')
2700 });
2701 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002702
Scott Mainf5089842012-08-14 16:31:07 -07002703 initSidenavHeightResize();
2704}
2705
2706function buildApiLevelSelector() {
2707 maxLevel = SINCE_DATA.length;
2708 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2709 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2710
2711 minLevel = parseInt($("#doc-api-level").attr("class"));
2712 // Handle provisional api levels; the provisional level will always be the highest possible level
2713 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2714 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2715 if (isNaN(minLevel) && minLevel.length) {
2716 minLevel = maxLevel;
2717 }
2718 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2719 for (var i = maxLevel-1; i >= 0; i--) {
2720 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2721 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2722 select.append(option);
2723 }
2724
2725 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2726 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2727 selectedLevelItem.setAttribute('selected',true);
2728}
2729
2730function changeApiLevel() {
2731 maxLevel = SINCE_DATA.length;
2732 var selectedLevel = maxLevel;
2733
2734 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2735 toggleVisisbleApis(selectedLevel, "body");
2736
2737 var date = new Date();
2738 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2739 var expiration = date.toGMTString();
2740 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2741
2742 if (selectedLevel < minLevel) {
2743 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002744 $("#naMessage").show().html("<div><p><strong>This " + thing
2745 + " requires API level " + minLevel + " or higher.</strong></p>"
2746 + "<p>This document is hidden because your selected API level for the documentation is "
2747 + selectedLevel + ". You can change the documentation API level with the selector "
2748 + "above the left navigation.</p>"
2749 + "<p>For more information about specifying the API level your app requires, "
2750 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2751 + ">Supporting Different Platform Versions</a>.</p>"
2752 + "<input type='button' value='OK, make this page visible' "
2753 + "title='Change the API level to " + minLevel + "' "
2754 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2755 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002756 } else {
2757 $("#naMessage").hide();
2758 }
2759}
2760
2761function toggleVisisbleApis(selectedLevel, context) {
2762 var apis = $(".api",context);
2763 apis.each(function(i) {
2764 var obj = $(this);
2765 var className = obj.attr("class");
2766 var apiLevelIndex = className.lastIndexOf("-")+1;
2767 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2768 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2769 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2770 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2771 return;
2772 }
2773 apiLevel = parseInt(apiLevel);
2774
2775 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2776 var selectedLevelNum = parseInt(selectedLevel)
2777 var apiLevelNum = parseInt(apiLevel);
2778 if (isNaN(apiLevelNum)) {
2779 apiLevelNum = maxLevel;
2780 }
2781
2782 // Grey things out that aren't available and give a tooltip title
2783 if (apiLevelNum > selectedLevelNum) {
2784 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002785 + apiLevel + "\" or higher. To reveal, change the target API level "
2786 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002787 }
Scott Mainf5089842012-08-14 16:31:07 -07002788 else obj.removeClass("absent").removeAttr("title");
2789 });
2790}
2791
2792
2793
2794
2795/* ################# SIDENAV TREE VIEW ################### */
2796
2797function new_node(me, mom, text, link, children_data, api_level)
2798{
2799 var node = new Object();
2800 node.children = Array();
2801 node.children_data = children_data;
2802 node.depth = mom.depth + 1;
2803
2804 node.li = document.createElement("li");
2805 mom.get_children_ul().appendChild(node.li);
2806
2807 node.label_div = document.createElement("div");
2808 node.label_div.className = "label";
2809 if (api_level != null) {
2810 $(node.label_div).addClass("api");
2811 $(node.label_div).addClass("api-level-"+api_level);
2812 }
2813 node.li.appendChild(node.label_div);
2814
2815 if (children_data != null) {
2816 node.expand_toggle = document.createElement("a");
2817 node.expand_toggle.href = "javascript:void(0)";
2818 node.expand_toggle.onclick = function() {
2819 if (node.expanded) {
2820 $(node.get_children_ul()).slideUp("fast");
2821 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2822 node.expanded = false;
2823 } else {
2824 expand_node(me, node);
2825 }
2826 };
2827 node.label_div.appendChild(node.expand_toggle);
2828
2829 node.plus_img = document.createElement("img");
2830 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2831 node.plus_img.className = "plus";
2832 node.plus_img.width = "8";
2833 node.plus_img.border = "0";
2834 node.expand_toggle.appendChild(node.plus_img);
2835
2836 node.expanded = false;
2837 }
2838
2839 var a = document.createElement("a");
2840 node.label_div.appendChild(a);
2841 node.label = document.createTextNode(text);
2842 a.appendChild(node.label);
2843 if (link) {
2844 a.href = me.toroot + link;
2845 } else {
2846 if (children_data != null) {
2847 a.className = "nolink";
2848 a.href = "javascript:void(0)";
2849 a.onclick = node.expand_toggle.onclick;
2850 // This next line shouldn't be necessary. I'll buy a beer for the first
2851 // person who figures out how to remove this line and have the link
2852 // toggle shut on the first try. --joeo@android.com
2853 node.expanded = false;
2854 }
2855 }
Scott Main3b90aff2013-08-01 18:09:35 -07002856
Scott Mainf5089842012-08-14 16:31:07 -07002857
2858 node.children_ul = null;
2859 node.get_children_ul = function() {
2860 if (!node.children_ul) {
2861 node.children_ul = document.createElement("ul");
2862 node.children_ul.className = "children_ul";
2863 node.children_ul.style.display = "none";
2864 node.li.appendChild(node.children_ul);
2865 }
2866 return node.children_ul;
2867 };
2868
2869 return node;
2870}
2871
Robert Lyd2dd6e52012-11-29 21:28:48 -08002872
2873
2874
Scott Mainf5089842012-08-14 16:31:07 -07002875function expand_node(me, node)
2876{
2877 if (node.children_data && !node.expanded) {
2878 if (node.children_visited) {
2879 $(node.get_children_ul()).slideDown("fast");
2880 } else {
2881 get_node(me, node);
2882 if ($(node.label_div).hasClass("absent")) {
2883 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002884 }
Scott Mainf5089842012-08-14 16:31:07 -07002885 $(node.get_children_ul()).slideDown("fast");
2886 }
2887 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2888 node.expanded = true;
2889
2890 // perform api level toggling because new nodes are new to the DOM
2891 var selectedLevel = $("#apiLevelSelector option:selected").val();
2892 toggleVisisbleApis(selectedLevel, "#side-nav");
2893 }
2894}
2895
2896function get_node(me, mom)
2897{
2898 mom.children_visited = true;
2899 for (var i in mom.children_data) {
2900 var node_data = mom.children_data[i];
2901 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2902 node_data[2], node_data[3]);
2903 }
2904}
2905
2906function this_page_relative(toroot)
2907{
2908 var full = document.location.pathname;
2909 var file = "";
2910 if (toroot.substr(0, 1) == "/") {
2911 if (full.substr(0, toroot.length) == toroot) {
2912 return full.substr(toroot.length);
2913 } else {
2914 // the file isn't under toroot. Fail.
2915 return null;
2916 }
2917 } else {
2918 if (toroot != "./") {
2919 toroot = "./" + toroot;
2920 }
2921 do {
2922 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2923 var pos = full.lastIndexOf("/");
2924 file = full.substr(pos) + file;
2925 full = full.substr(0, pos);
2926 toroot = toroot.substr(0, toroot.length-3);
2927 }
2928 } while (toroot != "" && toroot != "/");
2929 return file.substr(1);
2930 }
2931}
2932
2933function find_page(url, data)
2934{
2935 var nodes = data;
2936 var result = null;
2937 for (var i in nodes) {
2938 var d = nodes[i];
2939 if (d[1] == url) {
2940 return new Array(i);
2941 }
2942 else if (d[2] != null) {
2943 result = find_page(url, d[2]);
2944 if (result != null) {
2945 return (new Array(i).concat(result));
2946 }
2947 }
2948 }
2949 return null;
2950}
2951
Scott Mainf5089842012-08-14 16:31:07 -07002952function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002953 // load json file for navtree data
2954 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2955 // when the file is loaded, initialize the tree
2956 if(jqxhr.status === 200) {
2957 init_navtree("tree-list", toroot, NAVTREE_DATA);
2958 }
2959 });
Scott Main3b90aff2013-08-01 18:09:35 -07002960
Scott Mainf5089842012-08-14 16:31:07 -07002961 // perform api level toggling because because the whole tree is new to the DOM
2962 var selectedLevel = $("#apiLevelSelector option:selected").val();
2963 toggleVisisbleApis(selectedLevel, "#side-nav");
2964}
2965
2966function init_navtree(navtree_id, toroot, root_nodes)
2967{
2968 var me = new Object();
2969 me.toroot = toroot;
2970 me.node = new Object();
2971
2972 me.node.li = document.getElementById(navtree_id);
2973 me.node.children_data = root_nodes;
2974 me.node.children = new Array();
2975 me.node.children_ul = document.createElement("ul");
2976 me.node.get_children_ul = function() { return me.node.children_ul; };
2977 //me.node.children_ul.className = "children_ul";
2978 me.node.li.appendChild(me.node.children_ul);
2979 me.node.depth = 0;
2980
2981 get_node(me, me.node);
2982
2983 me.this_page = this_page_relative(toroot);
2984 me.breadcrumbs = find_page(me.this_page, root_nodes);
2985 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2986 var mom = me.node;
2987 for (var i in me.breadcrumbs) {
2988 var j = me.breadcrumbs[i];
2989 mom = mom.children[j];
2990 expand_node(me, mom);
2991 }
2992 mom.label_div.className = mom.label_div.className + " selected";
2993 addLoadEvent(function() {
2994 scrollIntoView("nav-tree");
2995 });
2996 }
2997}
2998
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07002999
3000
3001
3002
3003
3004
3005
Robert Lyd2dd6e52012-11-29 21:28:48 -08003006/* TODO: eliminate redundancy with non-google functions */
3007function init_google_navtree(navtree_id, toroot, root_nodes)
3008{
3009 var me = new Object();
3010 me.toroot = toroot;
3011 me.node = new Object();
3012
3013 me.node.li = document.getElementById(navtree_id);
3014 me.node.children_data = root_nodes;
3015 me.node.children = new Array();
3016 me.node.children_ul = document.createElement("ul");
3017 me.node.get_children_ul = function() { return me.node.children_ul; };
3018 //me.node.children_ul.className = "children_ul";
3019 me.node.li.appendChild(me.node.children_ul);
3020 me.node.depth = 0;
3021
3022 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003023}
3024
3025function new_google_node(me, mom, text, link, children_data, api_level)
3026{
3027 var node = new Object();
3028 var child;
3029 node.children = Array();
3030 node.children_data = children_data;
3031 node.depth = mom.depth + 1;
3032 node.get_children_ul = function() {
3033 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003034 node.children_ul = document.createElement("ul");
3035 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003036 node.li.appendChild(node.children_ul);
3037 }
3038 return node.children_ul;
3039 };
3040 node.li = document.createElement("li");
3041
3042 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003043
3044
Robert Lyd2dd6e52012-11-29 21:28:48 -08003045 if(link) {
3046 child = document.createElement("a");
3047
3048 }
3049 else {
3050 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003051 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003052
3053 }
3054 if (children_data != null) {
3055 node.li.className="nav-section";
3056 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003057 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003058 node.li.appendChild(node.label_div);
3059 get_google_node(me, node);
3060 node.label_div.appendChild(child);
3061 }
3062 else {
3063 node.li.appendChild(child);
3064 }
3065 if(link) {
3066 child.href = me.toroot + link;
3067 }
3068 node.label = document.createTextNode(text);
3069 child.appendChild(node.label);
3070
3071 node.children_ul = null;
3072
3073 return node;
3074}
3075
3076function get_google_node(me, mom)
3077{
3078 mom.children_visited = true;
3079 var linkText;
3080 for (var i in mom.children_data) {
3081 var node_data = mom.children_data[i];
3082 linkText = node_data[0];
3083
3084 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3085 linkText = linkText.substr(19, linkText.length);
3086 }
3087 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3088 node_data[2], node_data[3]);
3089 }
3090}
Scott Mainad08f072013-08-20 16:49:57 -07003091
3092
3093
3094
3095
3096
3097/****** NEW version of script to build google and sample navs dynamically ******/
3098// TODO: update Google reference docs to tolerate this new implementation
3099
Scott Maine624b3f2013-09-12 12:56:41 -07003100var NODE_NAME = 0;
3101var NODE_HREF = 1;
3102var NODE_GROUP = 2;
3103var NODE_TAGS = 3;
3104var NODE_CHILDREN = 4;
3105
Scott Mainad08f072013-08-20 16:49:57 -07003106function init_google_navtree2(navtree_id, data)
3107{
3108 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003109 for (var i in data) {
3110 var node_data = data[i];
3111 $containerUl.append(new_google_node2(node_data));
3112 }
3113
Scott Main70557ee2013-10-30 14:47:40 -07003114 // Make all third-generation list items 'sticky' to prevent them from collapsing
3115 $containerUl.find('li li li.nav-section').addClass('sticky');
3116
Scott Mainad08f072013-08-20 16:49:57 -07003117 initExpandableNavItems("#"+navtree_id);
3118}
3119
3120function new_google_node2(node_data)
3121{
Scott Maine624b3f2013-09-12 12:56:41 -07003122 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003123 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3124 linkText = linkText.substr(19, linkText.length);
3125 }
3126 var $li = $('<li>');
3127 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003128 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003129 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3130 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003131 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003132 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3133 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003134 }
3135 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003136 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003137 $li.addClass("nav-section");
3138 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003139 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003140
Scott Maine624b3f2013-09-12 12:56:41 -07003141 for (var i in node_data[NODE_CHILDREN]) {
3142 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003143 $childUl.append(new_google_node2(child_node_data));
3144 }
3145 $li.append($childUl);
3146 }
3147 $li.prepend($a);
3148
3149 return $li;
3150}
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
Robert Lyd2dd6e52012-11-29 21:28:48 -08003162function showGoogleRefTree() {
3163 init_default_google_navtree(toRoot);
3164 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003165}
3166
3167function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003168 // load json file for navtree data
3169 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3170 // when the file is loaded, initialize the tree
3171 if(jqxhr.status === 200) {
3172 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3173 highlightSidenav();
3174 resizeNav();
3175 }
3176 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003177}
3178
3179function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003180 // load json file for navtree data
3181 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3182 // when the file is loaded, initialize the tree
3183 if(jqxhr.status === 200) {
3184 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3185 highlightSidenav();
3186 resizeNav();
3187 }
3188 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003189}
3190
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003191function showSamplesRefTree() {
3192 init_default_samples_navtree(toRoot);
3193}
3194
3195function init_default_samples_navtree(toroot) {
3196 // load json file for navtree data
3197 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3198 // when the file is loaded, initialize the tree
3199 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003200 // hack to remove the "about the samples" link then put it back in
3201 // after we nuke the list to remove the dummy static list of samples
3202 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3203 $("#nav.samples-nav").empty();
3204 $("#nav.samples-nav").append($firstLi);
3205
Scott Mainad08f072013-08-20 16:49:57 -07003206 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003207 highlightSidenav();
3208 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003209 if ($("#jd-content #samples").length) {
3210 showSamples();
3211 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003212 }
3213 });
3214}
3215
Scott Mainf5089842012-08-14 16:31:07 -07003216/* TOGGLE INHERITED MEMBERS */
3217
3218/* Toggle an inherited class (arrow toggle)
3219 * @param linkObj The link that was clicked.
3220 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3221 * 'null' to simply toggle.
3222 */
3223function toggleInherited(linkObj, expand) {
3224 var base = linkObj.getAttribute("id");
3225 var list = document.getElementById(base + "-list");
3226 var summary = document.getElementById(base + "-summary");
3227 var trigger = document.getElementById(base + "-trigger");
3228 var a = $(linkObj);
3229 if ( (expand == null && a.hasClass("closed")) || expand ) {
3230 list.style.display = "none";
3231 summary.style.display = "block";
3232 trigger.src = toRoot + "assets/images/triangle-opened.png";
3233 a.removeClass("closed");
3234 a.addClass("opened");
3235 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3236 list.style.display = "block";
3237 summary.style.display = "none";
3238 trigger.src = toRoot + "assets/images/triangle-closed.png";
3239 a.removeClass("opened");
3240 a.addClass("closed");
3241 }
3242 return false;
3243}
3244
3245/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3246 * @param linkObj The link that was clicked.
3247 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3248 * 'null' to simply toggle.
3249 */
3250function toggleAllInherited(linkObj, expand) {
3251 var a = $(linkObj);
3252 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3253 var expandos = $(".jd-expando-trigger", table);
3254 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3255 expandos.each(function(i) {
3256 toggleInherited(this, true);
3257 });
3258 a.text("[Collapse]");
3259 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3260 expandos.each(function(i) {
3261 toggleInherited(this, false);
3262 });
3263 a.text("[Expand]");
3264 }
3265 return false;
3266}
3267
3268/* Toggle all inherited members in the class (link in the class title)
3269 */
3270function toggleAllClassInherited() {
3271 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3272 var toggles = $(".toggle-all", $("#body-content"));
3273 if (a.text() == "[Expand All]") {
3274 toggles.each(function(i) {
3275 toggleAllInherited(this, true);
3276 });
3277 a.text("[Collapse All]");
3278 } else {
3279 toggles.each(function(i) {
3280 toggleAllInherited(this, false);
3281 });
3282 a.text("[Expand All]");
3283 }
3284 return false;
3285}
3286
3287/* Expand all inherited members in the class. Used when initiating page search */
3288function ensureAllInheritedExpanded() {
3289 var toggles = $(".toggle-all", $("#body-content"));
3290 toggles.each(function(i) {
3291 toggleAllInherited(this, true);
3292 });
3293 $("#toggleAllClassInherited").text("[Collapse All]");
3294}
3295
3296
3297/* HANDLE KEY EVENTS
3298 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3299 */
3300var agent = navigator['userAgent'].toLowerCase();
3301var mac = agent.indexOf("macintosh") != -1;
3302
3303$(document).keydown( function(e) {
3304var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3305 if (control && e.which == 70) { // 70 is "F"
3306 ensureAllInheritedExpanded();
3307 }
3308});
Scott Main498d7102013-08-21 15:47:38 -07003309
3310
3311
3312
3313
3314
3315/* On-demand functions */
3316
3317/** Move sample code line numbers out of PRE block and into non-copyable column */
3318function initCodeLineNumbers() {
3319 var numbers = $("#codesample-block a.number");
3320 if (numbers.length) {
3321 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3322 }
3323
3324 $(document).ready(function() {
3325 // select entire line when clicked
3326 $("span.code-line").click(function() {
3327 if (!shifted) {
3328 selectText(this);
3329 }
3330 });
3331 // invoke line link on double click
3332 $(".code-line").dblclick(function() {
3333 document.location.hash = $(this).attr('id');
3334 });
3335 // highlight the line when hovering on the number
3336 $("#codesample-line-numbers a.number").mouseover(function() {
3337 var id = $(this).attr('href');
3338 $(id).css('background','#e7e7e7');
3339 });
3340 $("#codesample-line-numbers a.number").mouseout(function() {
3341 var id = $(this).attr('href');
3342 $(id).css('background','none');
3343 });
3344 });
3345}
3346
3347// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3348var shifted = false;
3349$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3350
3351// courtesy of jasonedelman.com
3352function selectText(element) {
3353 var doc = document
3354 , range, selection
3355 ;
3356 if (doc.body.createTextRange) { //ms
3357 range = doc.body.createTextRange();
3358 range.moveToElementText(element);
3359 range.select();
3360 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003361 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003362 range = doc.createRange();
3363 range.selectNodeContents(element);
3364 selection.removeAllRanges();
3365 selection.addRange(range);
3366 }
Scott Main285f0772013-08-22 23:22:09 +00003367}
Scott Main03aca9a2013-10-31 07:20:55 -07003368
3369
3370
3371
3372/** Display links and other information about samples that match the
3373 group specified by the URL */
3374function showSamples() {
3375 var group = $("#samples").attr('class');
3376 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3377
3378 var $ul = $("<ul>");
3379 $selectedLi = $("#nav li.selected");
3380
3381 $selectedLi.children("ul").children("li").each(function() {
3382 var $li = $("<li>").append($(this).find("a").first().clone());
3383 $ul.append($li);
3384 });
3385
3386 $("#samples").append($ul);
3387
3388}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003389
3390
3391
3392/* ########################################################## */
3393/* ################### RESOURCE CARDS ##################### */
3394/* ########################################################## */
3395
3396/** Handle resource queries, collections, and grids (sections). Requires
3397 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3398
3399(function() {
3400 // Prevent the same resource from being loaded more than once per page.
3401 var addedPageResources = {};
3402
3403 $(document).ready(function() {
3404 $('.resource-widget').each(function() {
3405 initResourceWidget(this);
3406 });
3407
3408 /* Pass the line height to ellipsisfade() to adjust the height of the
3409 text container to show the max number of lines possible, without
3410 showing lines that are cut off. This works with the css ellipsis
3411 classes to fade last text line and apply an ellipsis char. */
3412
Scott Mainb16376f2014-05-21 20:35:47 -07003413 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003414 var lineHeight = 15;
3415 $('.card-info .text').ellipsisfade(lineHeight);
3416 });
3417
3418 /*
3419 Three types of resource layouts:
3420 Flow - Uses a fixed row-height flow using float left style.
3421 Carousel - Single card slideshow all same dimension absolute.
3422 Stack - Uses fixed columns and flexible element height.
3423 */
3424 function initResourceWidget(widget) {
3425 var $widget = $(widget);
3426 var isFlow = $widget.hasClass('resource-flow-layout'),
3427 isCarousel = $widget.hasClass('resource-carousel-layout'),
3428 isStack = $widget.hasClass('resource-stack-layout');
3429
3430 // find size of widget by pulling out its class name
3431 var sizeCols = 1;
3432 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3433 if (m) {
3434 sizeCols = parseInt(m[1], 10);
3435 }
3436
3437 var opts = {
3438 cardSizes: ($widget.data('cardsizes') || '').split(','),
3439 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3440 itemsPerPage: $widget.data('itemsperpage'),
3441 sortOrder: $widget.data('sortorder'),
3442 query: $widget.data('query'),
3443 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003444 sizeCols: sizeCols,
3445 /* Added by LFL 6/6/14 */
3446 resourceStyle: $widget.data('resourcestyle') || 'card',
3447 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003448 };
3449
3450 // run the search for the set of resources to show
3451
3452 var resources = buildResourceList(opts);
3453
3454 if (isFlow) {
3455 drawResourcesFlowWidget($widget, opts, resources);
3456 } else if (isCarousel) {
3457 drawResourcesCarouselWidget($widget, opts, resources);
3458 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003459 /* Looks like this got removed and is not used, so repurposing for the
3460 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003461 Modified by LFL 6/6/14
3462 */
3463 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003464 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003465 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003466 }
3467 }
3468
3469 /* Initializes a Resource Carousel Widget */
3470 function drawResourcesCarouselWidget($widget, opts, resources) {
3471 $widget.empty();
3472 var plusone = true; //always show plusone on carousel
3473
3474 $widget.addClass('resource-card slideshow-container')
3475 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3476 .append($('<a>').addClass('slideshow-next').text('Next'));
3477
3478 var css = { 'width': $widget.width() + 'px',
3479 'height': $widget.height() + 'px' };
3480
3481 var $ul = $('<ul>');
3482
3483 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003484 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003485 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003486 .decorateResourceCard(resources[i],plusone);
3487
3488 $('<li>').css(css)
3489 .append($card)
3490 .appendTo($ul);
3491 }
3492
3493 $('<div>').addClass('frame')
3494 .append($ul)
3495 .appendTo($widget);
3496
3497 $widget.dacSlideshow({
3498 auto: true,
3499 btnPrev: '.slideshow-prev',
3500 btnNext: '.slideshow-next'
3501 });
3502 };
3503
Robert Lye7eeb402014-06-03 19:35:24 -07003504 /* Initializes a Resource Card Stack Widget (column-based layout)
3505 Modified by LFL 6/6/14
3506 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003507 function drawResourcesStackWidget($widget, opts, resources, sections) {
3508 // Don't empty widget, grab all items inside since they will be the first
3509 // items stacked, followed by the resource query
3510 var plusone = true; //by default show plusone on section cards
3511 var cards = $widget.find('.resource-card').detach().toArray();
3512 var numStacks = opts.numStacks || 1;
3513 var $stacks = [];
3514 var urlString;
3515
3516 for (var i = 0; i < numStacks; ++i) {
3517 $stacks[i] = $('<div>').addClass('resource-card-stack')
3518 .appendTo($widget);
3519 }
3520
3521 var sectionResources = [];
3522
3523 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003524 if (sections) {
3525 for (var i = 0; i < sections.length; ++i) {
3526 if (!sections[i].sections || !sections[i].sections.length) {
3527 // Render it as a resource card
3528 sectionResources.push(
3529 $('<a>')
3530 .addClass('resource-card section-card')
3531 .attr('href', cleanUrl(sections[i].resource.url))
3532 .decorateResourceCard(sections[i].resource,plusone)[0]
3533 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003534
Robert Lye7eeb402014-06-03 19:35:24 -07003535 } else {
3536 cards.push(
3537 $('<div>')
3538 .addClass('resource-card section-card-menu')
3539 .decorateResourceSection(sections[i],plusone)[0]
3540 );
3541 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003542 }
3543 }
3544
3545 cards = cards.concat(sectionResources);
3546
3547 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003548 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003549
Robert Lye7eeb402014-06-03 19:35:24 -07003550 if (opts.resourceStyle.indexOf('related') > -1) {
3551 $card.addClass('related-card');
3552 }
smain@google.com95948b82014-06-16 19:24:25 -07003553
Dirk Doughertyc3921652014-05-13 16:55:26 -07003554 cards.push($card[0]);
3555 }
3556
Robert Lye7eeb402014-06-03 19:35:24 -07003557 if (opts.stackSort != 'false') {
3558 for (var i = 0; i < cards.length; ++i) {
3559 // Find the stack with the shortest height, but give preference to
3560 // left to right order.
3561 var minHeight = $stacks[0].height();
3562 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003563
Robert Lye7eeb402014-06-03 19:35:24 -07003564 for (var j = 1; j < numStacks; ++j) {
3565 var height = $stacks[j].height();
3566 if (height < minHeight - 45) {
3567 minHeight = height;
3568 minIndex = j;
3569 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003570 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003571
Robert Lye7eeb402014-06-03 19:35:24 -07003572 $stacks[minIndex].append($(cards[i]));
3573 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003574 }
3575
3576 };
smain@google.com95948b82014-06-16 19:24:25 -07003577
3578 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003579 Create a resource card using the given resource object and a list of html
3580 configured options. Returns a jquery object containing the element.
3581 */
smain@google.com95948b82014-06-16 19:24:25 -07003582 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003583 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003584
Robert Lye7eeb402014-06-03 19:35:24 -07003585 // The difference here is that generic cards are not entirely clickable
3586 // so its a div instead of an a tag, also the generic one is not given
3587 // the resource-card class so it appears with a transparent background
3588 // and can be styled in whatever way the css setup.
3589 if (opts.resourceStyle == 'generic') {
3590 $el = $('<div>')
3591 .addClass('resource')
3592 .attr('href', cleanUrl(resource.url))
3593 .decorateResource(resource, opts);
3594 } else {
3595 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003596
Robert Lye7eeb402014-06-03 19:35:24 -07003597 $el = $('<a>')
3598 .addClass(cls)
3599 .attr('href', cleanUrl(resource.url))
3600 .decorateResourceCard(resource, plusone);
3601 }
smain@google.com95948b82014-06-16 19:24:25 -07003602
Robert Lye7eeb402014-06-03 19:35:24 -07003603 return $el;
3604 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003605
3606 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3607 function drawResourcesFlowWidget($widget, opts, resources) {
3608 $widget.empty();
3609 var cardSizes = opts.cardSizes || ['6x6'];
3610 var i = 0, j = 0;
3611 var plusone = true; // by default show plusone on resource cards
3612
3613 while (i < resources.length) {
3614 var cardSize = cardSizes[j++ % cardSizes.length];
3615 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003616 // Some card sizes do not get a plusone button, such as where space is constrained
3617 // or for cards commonly embedded in docs (to improve overall page speed).
3618 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3619 (cardSize == "9x2") || (cardSize == "9x3") ||
3620 (cardSize == "12x2") || (cardSize == "12x3"));
3621
3622 // A stack has a third dimension which is the number of stacked items
3623 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3624 var stackCount = 0;
3625 var $stackDiv = null;
3626
3627 if (isStack) {
3628 // Create a stack container which should have the dimensions defined
3629 // by the product of the items inside.
3630 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3631 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3632 }
3633
3634 // Build each stack item or just a single item
3635 do {
3636 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003637
Robert Lye7eeb402014-06-03 19:35:24 -07003638 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003639
3640 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003641 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003642
Dirk Doughertyc3921652014-05-13 16:55:26 -07003643 if (isStack) {
3644 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3645 if (++stackCount == parseInt(isStack[3])) {
3646 $card.addClass('resource-card-row-stack-last');
3647 stackCount = 0;
3648 }
3649 } else {
3650 stackCount = 0;
3651 }
3652
Robert Lye7eeb402014-06-03 19:35:24 -07003653 $card.appendTo($stackDiv || $widget);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003654
3655 } while (++i < resources.length && stackCount > 0);
3656 }
3657 }
3658
3659 /* Build a site map of resources using a section as a root. */
3660 function buildSectionList(opts) {
3661 if (opts.section && SECTION_BY_ID[opts.section]) {
3662 return SECTION_BY_ID[opts.section].sections || [];
3663 }
3664 return [];
3665 }
3666
3667 function buildResourceList(opts) {
3668 var maxResults = opts.maxResults || 100;
3669
3670 var query = opts.query || '';
3671 var expressions = parseResourceQuery(query);
3672 var addedResourceIndices = {};
3673 var results = [];
3674
3675 for (var i = 0; i < expressions.length; i++) {
3676 var clauses = expressions[i];
3677
3678 // build initial set of resources from first clause
3679 var firstClause = clauses[0];
3680 var resources = [];
3681 switch (firstClause.attr) {
3682 case 'type':
3683 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3684 break;
3685 case 'lang':
3686 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3687 break;
3688 case 'tag':
3689 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3690 break;
3691 case 'collection':
3692 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3693 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3694 break;
3695 case 'section':
3696 var urls = SITE_MAP[firstClause.value].sections || [];
3697 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3698 break;
3699 }
3700 // console.log(firstClause.attr + ':' + firstClause.value);
3701 resources = resources || [];
3702
3703 // use additional clauses to filter corpus
3704 if (clauses.length > 1) {
3705 var otherClauses = clauses.slice(1);
3706 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3707 }
3708
3709 // filter out resources already added
3710 if (i > 1) {
3711 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3712 }
3713
3714 // add to list of already added indices
3715 for (var j = 0; j < resources.length; j++) {
3716 // console.log(resources[j].title);
3717 addedResourceIndices[resources[j].index] = 1;
3718 }
3719
3720 // concat to final results list
3721 results = results.concat(resources);
3722 }
3723
3724 if (opts.sortOrder && results.length) {
3725 var attr = opts.sortOrder;
3726
3727 if (opts.sortOrder == 'random') {
3728 var i = results.length, j, temp;
3729 while (--i) {
3730 j = Math.floor(Math.random() * (i + 1));
3731 temp = results[i];
3732 results[i] = results[j];
3733 results[j] = temp;
3734 }
3735 } else {
3736 var desc = attr.charAt(0) == '-';
3737 if (desc) {
3738 attr = attr.substring(1);
3739 }
3740 results = results.sort(function(x,y) {
3741 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3742 });
3743 }
3744 }
3745
3746 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3747 results = results.slice(0, maxResults);
3748
3749 for (var j = 0; j < results.length; ++j) {
3750 addedPageResources[results[j].index] = 1;
3751 }
3752
3753 return results;
3754 }
3755
3756
3757 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3758 return function(resource) {
3759 return !addedResourceIndices[resource.index];
3760 };
3761 }
3762
3763
3764 function getResourceMatchesClausesFilter(clauses) {
3765 return function(resource) {
3766 return doesResourceMatchClauses(resource, clauses);
3767 };
3768 }
3769
3770
3771 function doesResourceMatchClauses(resource, clauses) {
3772 for (var i = 0; i < clauses.length; i++) {
3773 var map;
3774 switch (clauses[i].attr) {
3775 case 'type':
3776 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3777 break;
3778 case 'lang':
3779 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3780 break;
3781 case 'tag':
3782 map = IS_RESOURCE_TAGGED[clauses[i].value];
3783 break;
3784 }
3785
3786 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3787 return clauses[i].negative;
3788 }
3789 }
3790 return true;
3791 }
smain@google.com95948b82014-06-16 19:24:25 -07003792
Robert Lye7eeb402014-06-03 19:35:24 -07003793 function cleanUrl(url)
3794 {
3795 if (url && url.indexOf('//') === -1) {
3796 url = toRoot + url;
3797 }
smain@google.com95948b82014-06-16 19:24:25 -07003798
Robert Lye7eeb402014-06-03 19:35:24 -07003799 return url;
3800 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003801
3802
3803 function parseResourceQuery(query) {
3804 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3805 var expressions = [];
3806 var expressionStrs = query.split(',') || [];
3807 for (var i = 0; i < expressionStrs.length; i++) {
3808 var expr = expressionStrs[i] || '';
3809
3810 // Break expression into clauses (clause e.g. 'tag:foo')
3811 var clauses = [];
3812 var clauseStrs = expr.split(/(?=[\+\-])/);
3813 for (var j = 0; j < clauseStrs.length; j++) {
3814 var clauseStr = clauseStrs[j] || '';
3815
3816 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3817 var parts = clauseStr.split(':');
3818 var clause = {};
3819
3820 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3821 if (clause.attr) {
3822 if (clause.attr.charAt(0) == '+') {
3823 clause.attr = clause.attr.substring(1);
3824 } else if (clause.attr.charAt(0) == '-') {
3825 clause.negative = true;
3826 clause.attr = clause.attr.substring(1);
3827 }
3828 }
3829
3830 if (parts.length > 1) {
3831 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3832 }
3833
3834 clauses.push(clause);
3835 }
3836
3837 if (!clauses.length) {
3838 continue;
3839 }
3840
3841 expressions.push(clauses);
3842 }
3843
3844 return expressions;
3845 }
3846})();
3847
3848(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003849
smain@google.com95948b82014-06-16 19:24:25 -07003850 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003851 Utility method for creating dom for the description area of a card.
3852 Used in decorateResourceCard and decorateResource.
3853 */
3854 function buildResourceCardDescription(resource, plusone) {
3855 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07003856
Robert Lye7eeb402014-06-03 19:35:24 -07003857 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07003858
Robert Lye7eeb402014-06-03 19:35:24 -07003859 if (resource.cta) {
3860 $description.append($('<a>').addClass('cta').html(resource.cta));
3861 }
smain@google.com95948b82014-06-16 19:24:25 -07003862
Robert Lye7eeb402014-06-03 19:35:24 -07003863 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07003864 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07003865 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003866
Robert Lye7eeb402014-06-03 19:35:24 -07003867 $description.append($('<div>').addClass('util')
3868 .append($('<div>').addClass('g-plusone')
3869 .attr('data-size', 'small')
3870 .attr('data-align', 'right')
3871 .attr('data-href', plusurl)));
3872 }
smain@google.com95948b82014-06-16 19:24:25 -07003873
Robert Lye7eeb402014-06-03 19:35:24 -07003874 return $description;
3875 }
smain@google.com95948b82014-06-16 19:24:25 -07003876
3877
Dirk Doughertyc3921652014-05-13 16:55:26 -07003878 /* Simple jquery function to create dom for a standard resource card */
3879 $.fn.decorateResourceCard = function(resource,plusone) {
3880 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07003881 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003882 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07003883
Robert Lye7eeb402014-06-03 19:35:24 -07003884 if (imgUrl.indexOf('//') === -1) {
3885 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003886 }
Robert Lye7eeb402014-06-03 19:35:24 -07003887
3888 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07003889 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07003890 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07003891 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07003892
Robert Lye7eeb402014-06-03 19:35:24 -07003893 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3894 .append($('<div>').addClass('section').text(section))
3895 .append($('<div>').addClass('title').html(resource.title))
3896 .append(buildResourceCardDescription(resource, plusone))
3897 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003898
3899 return this;
3900 };
3901
3902 /* Simple jquery function to create dom for a resource section card (menu) */
3903 $.fn.decorateResourceSection = function(section,plusone) {
3904 var resource = section.resource;
3905 //keep url clean for matching and offline mode handling
3906 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3907 var $base = $('<a>')
3908 .addClass('card-bg')
3909 .attr('href', resource.url)
3910 .append($('<div>').addClass('card-section-icon')
3911 .append($('<div>').addClass('icon'))
3912 .append($('<div>').addClass('section').html(resource.title)))
3913 .appendTo(this);
3914
3915 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3916
3917 if (section.sections && section.sections.length) {
3918 // Recurse the section sub-tree to find a resource image.
3919 var stack = [section];
3920
3921 while (stack.length) {
3922 if (stack[0].resource.image) {
3923 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3924 break;
3925 }
3926
3927 if (stack[0].sections) {
3928 stack = stack.concat(stack[0].sections);
3929 }
3930
3931 stack.shift();
3932 }
3933
3934 var $ul = $('<ul>')
3935 .appendTo($cardInfo);
3936
3937 var max = section.sections.length > 3 ? 3 : section.sections.length;
3938
3939 for (var i = 0; i < max; ++i) {
3940
3941 var subResource = section.sections[i];
3942 if (!plusone) {
3943 $('<li>')
3944 .append($('<a>').attr('href', subResource.url)
3945 .append($('<div>').addClass('title').html(subResource.title))
3946 .append($('<div>').addClass('description ellipsis')
3947 .append($('<div>').addClass('text').html(subResource.summary))
3948 .append($('<div>').addClass('util'))))
3949 .appendTo($ul);
3950 } else {
3951 $('<li>')
3952 .append($('<a>').attr('href', subResource.url)
3953 .append($('<div>').addClass('title').html(subResource.title))
3954 .append($('<div>').addClass('description ellipsis')
3955 .append($('<div>').addClass('text').html(subResource.summary))
3956 .append($('<div>').addClass('util')
3957 .append($('<div>').addClass('g-plusone')
3958 .attr('data-size', 'small')
3959 .attr('data-align', 'right')
3960 .attr('data-href', resource.url)))))
3961 .appendTo($ul);
3962 }
3963 }
3964
3965 // Add a more row
3966 if (max < section.sections.length) {
3967 $('<li>')
3968 .append($('<a>').attr('href', resource.url)
3969 .append($('<div>')
3970 .addClass('title')
3971 .text('More')))
3972 .appendTo($ul);
3973 }
3974 } else {
3975 // No sub-resources, just render description?
3976 }
3977
3978 return this;
3979 };
smain@google.com95948b82014-06-16 19:24:25 -07003980
3981
3982
3983
Robert Lye7eeb402014-06-03 19:35:24 -07003984 /* Render other types of resource styles that are not cards. */
3985 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07003986 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003987 'assets/images/resource-card-default-android.jpg';
3988 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003989
Robert Lye7eeb402014-06-03 19:35:24 -07003990 if (imgUrl.indexOf('//') === -1) {
3991 imgUrl = toRoot + imgUrl;
3992 }
smain@google.com95948b82014-06-16 19:24:25 -07003993
Robert Lye7eeb402014-06-03 19:35:24 -07003994 if (linkUrl && linkUrl.indexOf('//') === -1) {
3995 linkUrl = toRoot + linkUrl;
3996 }
3997
3998 $(this).append(
3999 $('<div>').addClass('image')
4000 .css('background-image', 'url(' + imgUrl + ')'),
4001 $('<div>').addClass('info').append(
4002 $('<h4>').addClass('title').html(resource.title),
4003 $('<p>').addClass('summary').html(resource.summary),
4004 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4005 )
4006 );
4007
4008 return this;
4009 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004010})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004011
4012
Dirk Doughertyc3921652014-05-13 16:55:26 -07004013/* Calculate the vertical area remaining */
4014(function($) {
4015 $.fn.ellipsisfade= function(lineHeight) {
4016 this.each(function() {
4017 // get element text
4018 var $this = $(this);
4019 var remainingHeight = $this.parent().parent().height();
4020 $this.parent().siblings().each(function ()
4021 {
4022 var h = $(this).height();
4023 remainingHeight = remainingHeight - h;
4024 });
4025
4026 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4027 $this.parent().css({'height': adjustedRemainingHeight});
4028 $this.css({'height': "auto"});
4029 });
4030
4031 return this;
4032 };
4033}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004034
4035/*
4036 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004037
Robert Lye7eeb402014-06-03 19:35:24 -07004038 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004039 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004040 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004041
Robert Lye7eeb402014-06-03 19:35:24 -07004042 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004043
Robert Lye7eeb402014-06-03 19:35:24 -07004044 <div class="fullscreen-carousel">
4045 <div class="fullscreen-carousel-content">
4046 <!-- content here -->
4047 </div>
4048 <div class="fullscreen-carousel-content">
4049 <!-- content here -->
4050 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004051
Robert Lye7eeb402014-06-03 19:35:24 -07004052 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004053
Robert Lye7eeb402014-06-03 19:35:24 -07004054 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004055
Robert Lye7eeb402014-06-03 19:35:24 -07004056 Control over how the carousel takes over the screen can mostly be defined in
4057 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004058 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004059 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004060 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004061 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004062
Robert Lye7eeb402014-06-03 19:35:24 -07004063 There is limited functionality for having multiple sections since that request
4064 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4065 scroll between multiple content areas.
4066*/
4067
4068(function() {
4069 $(document).ready(function() {
4070 $('.fullscreen-carousel').each(function() {
4071 initWidget(this);
4072 });
4073 });
4074
4075 function initWidget(widget) {
4076 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004077
Robert Lye7eeb402014-06-03 19:35:24 -07004078 var topOffset = $widget.offset().top;
4079 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4080 var maxHeight = 0;
4081 var minHeight = 0;
4082 var $content = $widget.find('.fullscreen-carousel-content');
4083 var $nextArrow = $widget.find('.next-arrow');
4084 var $prevArrow = $widget.find('.prev-arrow');
4085 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004086
Robert Lye7eeb402014-06-03 19:35:24 -07004087 if ($content.length <= 1) {
4088 $nextArrow.hide();
4089 $prevArrow.hide();
4090 } else {
4091 $nextArrow.click(function() {
4092 var index = ($content.index($curSection) + 1);
4093 $curSection.hide();
4094 $curSection = $($content[index >= $content.length ? 0 : index]);
4095 $curSection.show();
4096 });
smain@google.com95948b82014-06-16 19:24:25 -07004097
Robert Lye7eeb402014-06-03 19:35:24 -07004098 $prevArrow.click(function() {
4099 var index = ($content.index($curSection) - 1);
4100 $curSection.hide();
4101 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4102 $curSection.show();
4103 });
4104 }
4105
4106 // Just hide all content sections except first.
4107 $content.each(function(index) {
4108 if ($(this).height() > minHeight) minHeight = $(this).height();
4109 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4110 });
4111
4112 // Register for changes to window size, and trigger.
4113 $(window).resize(resizeWidget);
4114 resizeWidget();
4115
4116 function resizeWidget() {
4117 var height = $(window).height() - topOffset - padBottom;
4118 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004119 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004120 (maxHeight && height > maxHeight ? maxHeight : height));
4121 }
smain@google.com95948b82014-06-16 19:24:25 -07004122 }
Robert Lye7eeb402014-06-03 19:35:24 -07004123})();
4124
4125
4126
4127
4128
4129/*
4130 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004131
Robert Lye7eeb402014-06-03 19:35:24 -07004132 The following allows tab widgets to be installed via the html below. Each
4133 tab content section should have a data-tab attribute matching one of the
4134 nav items'. Also each tab content section should have a width matching the
4135 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004136
Robert Lye7eeb402014-06-03 19:35:24 -07004137 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004138
Robert Lye7eeb402014-06-03 19:35:24 -07004139 <div class="tab-carousel">
4140 <ul class="tab-nav">
4141 <li><a href="#" data-tab="handsets">Handsets</a>
4142 <li><a href="#" data-tab="wearable">Wearable</a>
4143 <li><a href="#" data-tab="tv">TV</a>
4144 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004145
Robert Lye7eeb402014-06-03 19:35:24 -07004146 <div class="tab-carousel-content">
4147 <div data-tab="handsets">
4148 <!--Full width content here-->
4149 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004150
Robert Lye7eeb402014-06-03 19:35:24 -07004151 <div data-tab="wearable">
4152 <!--Full width content here-->
4153 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004154
Robert Lye7eeb402014-06-03 19:35:24 -07004155 <div data-tab="tv">
4156 <!--Full width content here-->
4157 </div>
4158 </div>
4159 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004160
Robert Lye7eeb402014-06-03 19:35:24 -07004161*/
4162(function() {
4163 $(document).ready(function() {
4164 $('.tab-carousel').each(function() {
4165 initWidget(this);
4166 });
4167 });
4168
4169 function initWidget(widget) {
4170 var $widget = $(widget);
4171 var $nav = $widget.find('.tab-nav');
4172 var $anchors = $nav.find('[data-tab]');
4173 var $li = $nav.find('li');
4174 var $contentContainer = $widget.find('.tab-carousel-content');
4175 var $tabs = $contentContainer.find('[data-tab]');
4176 var $curTab = $($tabs[0]); // Current tab is first tab.
4177 var width = $widget.width();
4178
4179 // Setup nav interactivity.
4180 $anchors.click(function(evt) {
4181 evt.preventDefault();
4182 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004183 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004184 });
smain@google.com95948b82014-06-16 19:24:25 -07004185
Robert Lye7eeb402014-06-03 19:35:24 -07004186 // Add highlight for navigation on first item.
4187 var $highlight = $('<div>').addClass('highlight')
4188 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4189 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004190
Robert Lye7eeb402014-06-03 19:35:24 -07004191 // Store height since we will change contents to absolute.
4192 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004193
Robert Lye7eeb402014-06-03 19:35:24 -07004194 // Absolutely position tabs so they're ready for transition.
4195 $tabs.each(function(index) {
4196 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4197 });
smain@google.com95948b82014-06-16 19:24:25 -07004198
Robert Lye7eeb402014-06-03 19:35:24 -07004199 function transitionWidget($toTab) {
4200 if (!$curTab.is($toTab)) {
4201 var curIndex = $tabs.index($curTab[0]);
4202 var toIndex = $tabs.index($toTab[0]);
4203 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004204
Robert Lye7eeb402014-06-03 19:35:24 -07004205 // Animate content sections.
4206 $toTab.css({left:(width * dir) + 'px'});
4207 $curTab.animate({left:(width * -dir) + 'px'});
4208 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004209
Robert Lye7eeb402014-06-03 19:35:24 -07004210 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004211 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004212 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004213
Robert Lye7eeb402014-06-03 19:35:24 -07004214 // Store new current section.
4215 $curTab = $toTab;
4216 }
4217 }
smain@google.com95948b82014-06-16 19:24:25 -07004218 }
Robert Lye7eeb402014-06-03 19:35:24 -07004219})();