blob: 7ed86cea4316bd0b4be427c5da932f39302ef87b [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) {
smain@google.comc91ecb72014-06-23 10:22:23 -0700258 if ($prevListItem.hasClass('nav-section') || crossBoundaries) {
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; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700315 // and completely hide the one in the footer
316 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700317 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700318 }
319 }
320 }
Scott Main5a1123e2012-09-26 12:51:28 -0700321
322 if (startClass) {
323 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
324
Scott Main3b90aff2013-08-01 18:09:35 -0700325 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700326 // then we need to add a bottom border to button
327 if (!$("#tb").length) {
328 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700329 }
Scott Main5a1123e2012-09-26 12:51:28 -0700330 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
331 $('.content-footer.next-class').show();
332 $('.next-page-link').attr('href','')
333 .removeClass("hide").addClass("disabled")
334 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700335 // and completely hide the one in the footer
336 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700337 if ($nextLink.length) {
338 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700339 .removeClass("hide")
340 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700341 $('.next-class-link').find('.new').empty();
342 }
Scott Main5a1123e2012-09-26 12:51:28 -0700343 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700344 $('.next-page-link').attr('href', $nextLink.attr('href'))
345 .removeClass("hide");
346 // for the footer link, also add the next page title
347 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700348 }
349
350 if (!startClass && $prevLink.length) {
351 var prevHref = $prevLink.attr('href');
352 if (prevHref == SITE_ROOT + 'index.html') {
353 // Don't show Previous when it leads to the homepage
354 } else {
355 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
356 }
Scott Main3b90aff2013-08-01 18:09:35 -0700357 }
Scott Main5a1123e2012-09-26 12:51:28 -0700358
359 // If this is a training 'article', there should be no prev/next nav
360 // ... if the grandparent is the "nav" ... and it has no child list items...
361 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
362 !$selListItem.find('li').length) {
363 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
364 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700365 }
Scott Main3b90aff2013-08-01 18:09:35 -0700366
Scott Maine4d8f1b2012-06-21 18:03:05 -0700367 }
Scott Main3b90aff2013-08-01 18:09:35 -0700368
369
370
Scott Main5a1123e2012-09-26 12:51:28 -0700371 // Set up the course landing pages for Training with class names and descriptions
372 if ($('body.trainingcourse').length) {
373 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700374
375 // create an array for all the class descriptions
376 var $classDescriptions = new Array($classLinks.length);
377 var lang = getLangPref();
378 $classLinks.each(function(index) {
379 var langDescr = $(this).attr(lang + "-description");
380 if (typeof langDescr !== 'undefined' && langDescr !== false) {
381 // if there's a class description in the selected language, use that
382 $classDescriptions[index] = langDescr;
383 } else {
384 // otherwise, use the default english description
385 $classDescriptions[index] = $(this).attr("description");
386 }
387 });
Scott Main3b90aff2013-08-01 18:09:35 -0700388
Scott Main5a1123e2012-09-26 12:51:28 -0700389 var $olClasses = $('<ol class="class-list"></ol>');
390 var $liClass;
391 var $imgIcon;
392 var $h2Title;
393 var $pSummary;
394 var $olLessons;
395 var $liLesson;
396 $classLinks.each(function(index) {
397 $liClass = $('<li></li>');
398 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700399 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700400
Scott Main5a1123e2012-09-26 12:51:28 -0700401 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700402
Scott Main5a1123e2012-09-26 12:51:28 -0700403 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700404
Scott Main5a1123e2012-09-26 12:51:28 -0700405 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700406 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
407 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700408 $lessons.each(function(index) {
409 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
410 });
411 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700412 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
413 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700414 $pSummary.addClass('article');
415 }
416
417 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
418 $olClasses.append($liClass);
419 });
420 $('.jd-descr').append($olClasses);
421 }
422
Scott Maine4d8f1b2012-06-21 18:03:05 -0700423 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700424 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700425
Scott Main3b90aff2013-08-01 18:09:35 -0700426
Scott Maine4d8f1b2012-06-21 18:03:05 -0700427 $(".scroll-pane").scroll(function(event) {
428 event.preventDefault();
429 return false;
430 });
431
432 /* Resize nav height when window height changes */
433 $(window).resize(function() {
434 if ($('#side-nav').length == 0) return;
435 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
436 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
437 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700438 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700439 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
440 updateSideNavPosition();
441 } else {
442 updateSidenavFullscreenWidth();
443 }
444 }
445 resizeNav();
446 });
447
448
Scott Maine4d8f1b2012-06-21 18:03:05 -0700449 var navBarLeftPos;
450 if ($('#devdoc-nav').length) {
451 setNavBarLeftPos();
452 }
453
454
Scott Maine4d8f1b2012-06-21 18:03:05 -0700455 // Set up play-on-hover <video> tags.
456 $('video.play-on-hover').bind('click', function(){
457 $(this).get(0).load(); // in case the video isn't seekable
458 $(this).get(0).play();
459 });
460
461 // Set up tooltips
462 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700463 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700464 var $target = $(this);
465 var $tooltip = $('<div>')
466 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700467 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700468 .hide()
469 .appendTo('body');
470 $target.removeAttr('title');
471
472 $target.hover(function() {
473 // in
474 var targetRect = $target.offset();
475 targetRect.width = $target.width();
476 targetRect.height = $target.height();
477
478 $tooltip.css({
479 left: targetRect.left,
480 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
481 });
482 $tooltip.addClass('below');
483 $tooltip.show();
484 }, function() {
485 // out
486 $tooltip.hide();
487 });
488 });
489
490 // Set up <h2> deeplinks
491 $('h2').click(function() {
492 var id = $(this).attr('id');
493 if (id) {
494 document.location.hash = id;
495 }
496 });
497
498 //Loads the +1 button
499 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
500 po.src = 'https://apis.google.com/js/plusone.js';
501 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
502
503
Scott Main3b90aff2013-08-01 18:09:35 -0700504 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700505 // which avoids the visible width from changing each time the bar appears
506 var $sidenav = $("#side-nav");
507 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700508
Scott Maine4d8f1b2012-06-21 18:03:05 -0700509 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
510
511
512 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700513
Scott Maine4d8f1b2012-06-21 18:03:05 -0700514 if ($(".scroll-pane").length > 1) {
515 // Check if there's a user preference for the panel heights
516 var cookieHeight = readCookie("reference_height");
517 if (cookieHeight) {
518 restoreHeight(cookieHeight);
519 }
520 }
Scott Main3b90aff2013-08-01 18:09:35 -0700521
Scott Main06f3f2c2014-05-30 11:23:00 -0700522 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700523 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700524 // Check if there's an anchor that we need to scroll into view.
525 // A delay is needed, because some browsers do not immediately scroll down to the anchor
526 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700527
Scott Main015d6162013-01-29 09:01:52 -0800528 /* init the language selector based on user cookie for lang */
529 loadLangPref();
530 changeNavLang(getLangPref());
531
532 /* setup event handlers to ensure the overflow menu is visible while picking lang */
533 $("#language select")
534 .mousedown(function() {
535 $("div.morehover").addClass("hover"); })
536 .blur(function() {
537 $("div.morehover").removeClass("hover"); });
538
539 /* some global variable setup */
540 resizePackagesNav = $("#resize-packages-nav");
541 classesNav = $("#classes-nav");
542 devdocNav = $("#devdoc-nav");
543
544 var cookiePath = "";
545 if (location.href.indexOf("/reference/") != -1) {
546 cookiePath = "reference_";
547 } else if (location.href.indexOf("/guide/") != -1) {
548 cookiePath = "guide_";
549 } else if (location.href.indexOf("/tools/") != -1) {
550 cookiePath = "tools_";
551 } else if (location.href.indexOf("/training/") != -1) {
552 cookiePath = "training_";
553 } else if (location.href.indexOf("/design/") != -1) {
554 cookiePath = "design_";
555 } else if (location.href.indexOf("/distribute/") != -1) {
556 cookiePath = "distribute_";
557 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700558
559});
Scott Main7e447ed2013-02-19 17:22:37 -0800560// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700561
562
Scott Mainad08f072013-08-20 16:49:57 -0700563function initExpandableNavItems(rootTag) {
564 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
565 var section = $(this).closest('li.nav-section');
566 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700567 /* hide me and descendants */
568 section.find('ul').slideUp(250, function() {
569 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700570 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700571 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700572 resizeNav();
573 });
574 } else {
575 /* show me */
576 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700577 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700578 $others.removeClass('expanded').children('ul').slideUp(250);
579
580 // now expand me
581 section.closest('li').addClass('expanded');
582 section.children('ul').slideDown(250, function() {
583 resizeNav();
584 });
585 }
586 });
Scott Mainf0093852013-08-22 11:37:11 -0700587
588 // Stop expand/collapse behavior when clicking on nav section links
589 // (since we're navigating away from the page)
590 // This selector captures the first instance of <a>, but not those with "#" as the href.
591 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
592 window.location.href = $(this).attr('href');
593 return false;
594 });
Scott Mainad08f072013-08-20 16:49:57 -0700595}
596
Dirk Doughertyc3921652014-05-13 16:55:26 -0700597
598/** Create the list of breadcrumb links in the sticky header */
599function buildBreadcrumbs() {
600 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
601 // Add the secondary horizontal nav item, if provided
602 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
603 if ($selectedSecondNav.length) {
604 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
605 }
606 // Add the primary horizontal nav
607 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
608 // If there's no header nav item, use the logo link and title from alt text
609 if ($selectedFirstNav.length < 1) {
610 $selectedFirstNav = $("<a>")
611 .attr('href', $("div#header .logo a").attr('href'))
612 .text($("div#header .logo img").attr('alt'));
613 }
614 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
615}
616
617
618
Scott Maine624b3f2013-09-12 12:56:41 -0700619/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700620function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700621 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
622 if ($("ul#nav li.selected").length) {
623 unHighlightSidenav();
624 }
625 // look for URL in sidenav, including the hash
626 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
627
628 // If the selNavLink is still empty, look for it without the hash
629 if ($selNavLink.length == 0) {
630 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
631 }
632
Scott Mainf6145542013-04-01 16:38:11 -0700633 var $selListItem;
634 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700635 // Find this page's <li> in sidenav and set selected
636 $selListItem = $selNavLink.closest('li');
637 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700638
Scott Mainf6145542013-04-01 16:38:11 -0700639 // Traverse up the tree and expand all parent nav-sections
640 $selNavLink.parents('li.nav-section').each(function() {
641 $(this).addClass('expanded');
642 $(this).children('ul').show();
643 });
644 }
645}
646
Scott Maine624b3f2013-09-12 12:56:41 -0700647function unHighlightSidenav() {
648 $("ul#nav li.selected").removeClass("selected");
649 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
650}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700651
652function toggleFullscreen(enable) {
653 var delay = 20;
654 var enabled = true;
655 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
656 if (enable) {
657 // Currently NOT USING fullscreen; enable fullscreen
658 stylesheet.removeAttr('disabled');
659 $('#nav-swap .fullscreen').removeClass('disabled');
660 $('#devdoc-nav').css({left:''});
661 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
662 enabled = true;
663 } else {
664 // Currently USING fullscreen; disable fullscreen
665 stylesheet.attr('disabled', 'disabled');
666 $('#nav-swap .fullscreen').addClass('disabled');
667 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
668 enabled = false;
669 }
670 writeCookie("fullscreen", enabled, null, null);
671 setNavBarLeftPos();
672 resizeNav(delay);
673 updateSideNavPosition();
674 setTimeout(initSidenavHeightResize,delay);
675}
676
677
678function setNavBarLeftPos() {
679 navBarLeftPos = $('#body-content').offset().left;
680}
681
682
683function updateSideNavPosition() {
684 var newLeft = $(window).scrollLeft() - navBarLeftPos;
685 $('#devdoc-nav').css({left: -newLeft});
686 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
687}
Scott Main3b90aff2013-08-01 18:09:35 -0700688
Scott Maine4d8f1b2012-06-21 18:03:05 -0700689// TODO: use $(document).ready instead
690function addLoadEvent(newfun) {
691 var current = window.onload;
692 if (typeof window.onload != 'function') {
693 window.onload = newfun;
694 } else {
695 window.onload = function() {
696 current();
697 newfun();
698 }
699 }
700}
701
702var agent = navigator['userAgent'].toLowerCase();
703// If a mobile phone, set flag and do mobile setup
704if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
705 (agent.indexOf("blackberry") != -1) ||
706 (agent.indexOf("webos") != -1) ||
707 (agent.indexOf("mini") != -1)) { // opera mini browsers
708 isMobile = true;
709}
710
711
Scott Main498d7102013-08-21 15:47:38 -0700712$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700713 $("pre:not(.no-pretty-print)").addClass("prettyprint");
714 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700715});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700716
Scott Maine4d8f1b2012-06-21 18:03:05 -0700717
718
719
720/* ######### RESIZE THE SIDENAV HEIGHT ########## */
721
722function resizeNav(delay) {
723 var $nav = $("#devdoc-nav");
724 var $window = $(window);
725 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700726
Scott Maine4d8f1b2012-06-21 18:03:05 -0700727 // Get the height of entire window and the total header height.
728 // Then figure out based on scroll position whether the header is visible
729 var windowHeight = $window.height();
730 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700731 var headerHeight = $('#header-wrapper').outerHeight();
732 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700733
734 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700735 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700736 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700737 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700738
Scott Maine4d8f1b2012-06-21 18:03:05 -0700739 // Depending on whether the header is visible, set the side nav's height.
740 if (headerVisible) {
741 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700742 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700743 } else {
744 // Once header is off screen, the nav height is almost full window height
745 navHeight = windowHeight - topMargin;
746 }
Scott Main3b90aff2013-08-01 18:09:35 -0700747
748
749
Scott Maine4d8f1b2012-06-21 18:03:05 -0700750 $scrollPanes = $(".scroll-pane");
751 if ($scrollPanes.length > 1) {
752 // subtract the height of the api level widget and nav swapper from the available nav height
753 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700754
Scott Maine4d8f1b2012-06-21 18:03:05 -0700755 $("#swapper").css({height:navHeight + "px"});
756 if ($("#nav-tree").is(":visible")) {
757 $("#nav-tree").css({height:navHeight});
758 }
Scott Main3b90aff2013-08-01 18:09:35 -0700759
760 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700761 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700762
763 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700764 // then the package panel should begin to shrink
765 if (parseInt(classesHeight) <= 0) {
766 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
767 $("#packages-nav").css({height:navHeight - 10});
768 }
Scott Main3b90aff2013-08-01 18:09:35 -0700769
Scott Maine4d8f1b2012-06-21 18:03:05 -0700770 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
771 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700772
773
Scott Maine4d8f1b2012-06-21 18:03:05 -0700774 } else {
775 $nav.height(navHeight);
776 }
Scott Main3b90aff2013-08-01 18:09:35 -0700777
Scott Maine4d8f1b2012-06-21 18:03:05 -0700778 if (delay) {
779 updateFromResize = true;
780 delayedReInitScrollbars(delay);
781 } else {
782 reInitScrollbars();
783 }
Scott Main3b90aff2013-08-01 18:09:35 -0700784
Scott Maine4d8f1b2012-06-21 18:03:05 -0700785}
786
787var updateScrollbars = false;
788var updateFromResize = false;
789
790/* Re-initialize the scrollbars to account for changed nav size.
791 * This method postpones the actual update by a 1/4 second in order to optimize the
792 * scroll performance while the header is still visible, because re-initializing the
793 * scroll panes is an intensive process.
794 */
795function delayedReInitScrollbars(delay) {
796 // If we're scheduled for an update, but have received another resize request
797 // before the scheduled resize has occured, just ignore the new request
798 // (and wait for the scheduled one).
799 if (updateScrollbars && updateFromResize) {
800 updateFromResize = false;
801 return;
802 }
Scott Main3b90aff2013-08-01 18:09:35 -0700803
Scott Maine4d8f1b2012-06-21 18:03:05 -0700804 // We're scheduled for an update and the update request came from this method's setTimeout
805 if (updateScrollbars && !updateFromResize) {
806 reInitScrollbars();
807 updateScrollbars = false;
808 } else {
809 updateScrollbars = true;
810 updateFromResize = false;
811 setTimeout('delayedReInitScrollbars()',delay);
812 }
813}
814
815/* Re-initialize the scrollbars to account for changed nav size. */
816function reInitScrollbars() {
817 var pane = $(".scroll-pane").each(function(){
818 var api = $(this).data('jsp');
819 if (!api) { setTimeout(reInitScrollbars,300); return;}
820 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700821 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700822 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
823}
824
825
826/* Resize the height of the nav panels in the reference,
827 * and save the new size to a cookie */
828function saveNavPanels() {
829 var basePath = getBaseUri(location.pathname);
830 var section = basePath.substring(1,basePath.indexOf("/",1));
831 writeCookie("height", resizePackagesNav.css("height"), section, null);
832}
833
834
835
836function restoreHeight(packageHeight) {
837 $("#resize-packages-nav").height(packageHeight);
838 $("#packages-nav").height(packageHeight);
839 // var classesHeight = navHeight - packageHeight;
840 // $("#classes-nav").css({height:classesHeight});
841 // $("#classes-nav .jspContainer").css({height:classesHeight});
842}
843
844
845
846/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
847
848
849
850
851
Scott Main3b90aff2013-08-01 18:09:35 -0700852/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700853 This is called when the page finished loading. */
854function scrollIntoView(nav) {
855 var $nav = $("#"+nav);
856 var element = $nav.jScrollPane({/* ...settings... */});
857 var api = element.data('jsp');
858
859 if ($nav.is(':visible')) {
860 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700861 if ($selected.length == 0) {
862 // If no selected item found, exit
863 return;
864 }
Scott Main52dd2062013-08-15 12:22:28 -0700865 // get the selected item's offset from its container nav by measuring the item's offset
866 // relative to the document then subtract the container nav's offset relative to the document
867 var selectedOffset = $selected.offset().top - $nav.offset().top;
868 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
869 // if it's more than 80% down the nav
870 // scroll the item up by an amount equal to 80% the container nav's height
871 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700872 }
873 }
874}
875
876
877
878
879
880
881/* Show popup dialogs */
882function showDialog(id) {
883 $dialog = $("#"+id);
884 $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>');
885 $dialog.wrapInner('<div/>');
886 $dialog.removeClass("hide");
887}
888
889
890
891
892
893/* ######### COOKIES! ########## */
894
895function readCookie(cookie) {
896 var myCookie = cookie_namespace+"_"+cookie+"=";
897 if (document.cookie) {
898 var index = document.cookie.indexOf(myCookie);
899 if (index != -1) {
900 var valStart = index + myCookie.length;
901 var valEnd = document.cookie.indexOf(";", valStart);
902 if (valEnd == -1) {
903 valEnd = document.cookie.length;
904 }
905 var val = document.cookie.substring(valStart, valEnd);
906 return val;
907 }
908 }
909 return 0;
910}
911
912function writeCookie(cookie, val, section, expiration) {
913 if (val==undefined) return;
914 section = section == null ? "_" : "_"+section+"_";
915 if (expiration == null) {
916 var date = new Date();
917 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
918 expiration = date.toGMTString();
919 }
Scott Main3b90aff2013-08-01 18:09:35 -0700920 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700921 + "; expires=" + expiration+"; path=/";
922 document.cookie = cookieValue;
923}
924
925/* ######### END COOKIES! ########## */
926
927
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700928var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -0700929var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700930var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -0700931/* Sets the vertical scoll position at which the sticky bar should appear.
932 This method is called to reset the position when search results appear or hide */
933function setStickyTop() {
934 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
935}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700936
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700937/*
Scott Mainb16376f2014-05-21 20:35:47 -0700938 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -0700939 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700940$(window).scroll(function(event) {
941
942 setStickyTop();
943 var hiding = false;
944 var $stickyEl = $('#sticky-header');
945 var $menuEl = $('.menu-container');
946 // Exit if there's no sidenav
947 if ($('#side-nav').length == 0) return;
948 // Exit if the mouse target is a DIV, because that means the event is coming
949 // from a scrollable div and so there's no need to make adjustments to our layout
950 if ($(event.target).nodeName == "DIV") {
951 return;
952 }
953
954 var top = $(window).scrollTop();
955 // we set the navbar fixed when the scroll position is beyond the height of the site header...
956 var shouldBeSticky = top >= stickyTop;
957 // ... except if the document content is shorter than the sidenav height.
958 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
959 if ($("#doc-col").height() < $("#side-nav").height()) {
960 shouldBeSticky = false;
961 }
Scott Mainf5257812014-05-22 17:26:38 -0700962 // Account for horizontal scroll
963 var scrollLeft = $(window).scrollLeft();
964 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
965 if (sticky && (scrollLeft != prevScrollLeft)) {
966 updateSideNavPosition();
967 prevScrollLeft = scrollLeft;
968 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700969
970 // Don't continue if the header is sufficently far away
971 // (to avoid intensive resizing that slows scrolling)
972 if (sticky == shouldBeSticky) {
973 return;
974 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700975
976 // If sticky header visible and position is now near top, hide sticky
977 if (sticky && !shouldBeSticky) {
978 sticky = false;
979 hiding = true;
980 // make the sidenav static again
981 $('#devdoc-nav')
982 .removeClass('fixed')
983 .css({'width':'auto','margin':''})
984 .prependTo('#side-nav');
985 // delay hide the sticky
986 $menuEl.removeClass('sticky-menu');
987 $stickyEl.fadeOut(250);
988 hiding = false;
989
990 // update the sidenaav position for side scrolling
991 updateSideNavPosition();
992 } else if (!sticky && shouldBeSticky) {
993 sticky = true;
994 $stickyEl.fadeIn(10);
995 $menuEl.addClass('sticky-menu');
996
997 // make the sidenav fixed
998 var width = $('#devdoc-nav').width();
999 $('#devdoc-nav')
1000 .addClass('fixed')
1001 .css({'width':width+'px'})
1002 .prependTo('#body-content');
1003
1004 // update the sidenaav position for side scrolling
1005 updateSideNavPosition();
1006
1007 } else if (hiding && top < 15) {
1008 $menuEl.removeClass('sticky-menu');
1009 $stickyEl.hide();
1010 hiding = false;
1011 }
1012 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
1013});
1014
1015/*
1016 * Manages secion card states and nav resize to conclude loading
1017 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001018(function() {
1019 $(document).ready(function() {
1020
Dirk Doughertyc3921652014-05-13 16:55:26 -07001021 // Stack hover states
1022 $('.section-card-menu').each(function(index, el) {
1023 var height = $(el).height();
1024 $(el).css({height:height+'px', position:'relative'});
1025 var $cardInfo = $(el).find('.card-info');
1026
1027 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1028 });
1029
Dirk Doughertyc3921652014-05-13 16:55:26 -07001030 });
1031
1032})();
1033
Scott Maine4d8f1b2012-06-21 18:03:05 -07001034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
Scott Maind7026f72013-06-17 15:08:49 -07001047/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001048
1049
1050
1051
1052
1053function toggle(obj, slide) {
1054 var ul = $("ul:first", obj);
1055 var li = ul.parent();
1056 if (li.hasClass("closed")) {
1057 if (slide) {
1058 ul.slideDown("fast");
1059 } else {
1060 ul.show();
1061 }
1062 li.removeClass("closed");
1063 li.addClass("open");
1064 $(".toggle-img", li).attr("title", "hide pages");
1065 } else {
1066 ul.slideUp("fast");
1067 li.removeClass("open");
1068 li.addClass("closed");
1069 $(".toggle-img", li).attr("title", "show pages");
1070 }
1071}
1072
1073
Scott Maine4d8f1b2012-06-21 18:03:05 -07001074function buildToggleLists() {
1075 $(".toggle-list").each(
1076 function(i) {
1077 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1078 $(this).addClass("closed");
1079 });
1080}
1081
1082
1083
Scott Maind7026f72013-06-17 15:08:49 -07001084function hideNestedItems(list, toggle) {
1085 $list = $(list);
1086 // hide nested lists
1087 if($list.hasClass('showing')) {
1088 $("li ol", $list).hide('fast');
1089 $list.removeClass('showing');
1090 // show nested lists
1091 } else {
1092 $("li ol", $list).show('fast');
1093 $list.addClass('showing');
1094 }
1095 $(".more,.less",$(toggle)).toggle();
1096}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001097
1098
smain@google.com95948b82014-06-16 19:24:25 -07001099/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1100function setupIdeDocToggle() {
1101 $( "select.ide" ).change(function() {
1102 var selected = $(this).find("option:selected").attr("value");
1103 $(".select-ide").hide();
1104 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001105
smain@google.com95948b82014-06-16 19:24:25 -07001106 $("select.ide").val(selected);
1107 });
1108}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133/* REFERENCE NAV SWAP */
1134
1135
1136function getNavPref() {
1137 var v = readCookie('reference_nav');
1138 if (v != NAV_PREF_TREE) {
1139 v = NAV_PREF_PANELS;
1140 }
1141 return v;
1142}
1143
1144function chooseDefaultNav() {
1145 nav_pref = getNavPref();
1146 if (nav_pref == NAV_PREF_TREE) {
1147 $("#nav-panels").toggle();
1148 $("#panel-link").toggle();
1149 $("#nav-tree").toggle();
1150 $("#tree-link").toggle();
1151 }
1152}
1153
1154function swapNav() {
1155 if (nav_pref == NAV_PREF_TREE) {
1156 nav_pref = NAV_PREF_PANELS;
1157 } else {
1158 nav_pref = NAV_PREF_TREE;
1159 init_default_navtree(toRoot);
1160 }
1161 var date = new Date();
1162 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1163 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1164
1165 $("#nav-panels").toggle();
1166 $("#panel-link").toggle();
1167 $("#nav-tree").toggle();
1168 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001169
Scott Maine4d8f1b2012-06-21 18:03:05 -07001170 resizeNav();
1171
1172 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1173 $("#nav-tree .jspContainer:visible")
1174 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1175 // Another nasty hack to make the scrollbar appear now that we have height
1176 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001177
Scott Maine4d8f1b2012-06-21 18:03:05 -07001178 if ($("#nav-tree").is(':visible')) {
1179 scrollIntoView("nav-tree");
1180 } else {
1181 scrollIntoView("packages-nav");
1182 scrollIntoView("classes-nav");
1183 }
1184}
1185
1186
1187
Scott Mainf5089842012-08-14 16:31:07 -07001188/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001189/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001190/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001191
1192function getBaseUri(uri) {
1193 var intlUrl = (uri.substring(0,6) == "/intl/");
1194 if (intlUrl) {
1195 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1196 base = base.substring(base.indexOf('/')+1, base.length);
1197 //alert("intl, returning base url: /" + base);
1198 return ("/" + base);
1199 } else {
1200 //alert("not intl, returning uri as found.");
1201 return uri;
1202 }
1203}
1204
1205function requestAppendHL(uri) {
1206//append "?hl=<lang> to an outgoing request (such as to blog)
1207 var lang = getLangPref();
1208 if (lang) {
1209 var q = 'hl=' + lang;
1210 uri += '?' + q;
1211 window.location = uri;
1212 return false;
1213 } else {
1214 return true;
1215 }
1216}
1217
1218
Scott Maine4d8f1b2012-06-21 18:03:05 -07001219function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001220 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1221 $links.each(function(i){ // for each link with a translation
1222 var $link = $(this);
1223 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1224 // put the desired language from the attribute as the text
1225 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001226 }
Scott Main6eb95f12012-10-02 17:12:23 -07001227 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001228}
1229
Scott Main015d6162013-01-29 09:01:52 -08001230function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001231 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001232 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001233 // keep this for 50 years
1234 //alert("expires: " + expires)
1235 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001236
1237 // ####### TODO: Remove this condition once we're stable on devsite #######
1238 // This condition is only needed if we still need to support legacy GAE server
1239 if (devsite) {
1240 // Switch language when on Devsite server
1241 if (submit) {
1242 $("#setlang").submit();
1243 }
1244 } else {
1245 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001246 if (submit) {
1247 window.location = getBaseUri(location.pathname);
1248 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001249 }
1250}
1251
1252function loadLangPref() {
1253 var lang = readCookie("pref_lang");
1254 if (lang != 0) {
1255 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1256 }
1257}
1258
1259function getLangPref() {
1260 var lang = $("#language").find(":selected").attr("value");
1261 if (!lang) {
1262 lang = readCookie("pref_lang");
1263 }
1264 return (lang != 0) ? lang : 'en';
1265}
1266
1267/* ########## END LOCALIZATION ############ */
1268
1269
1270
1271
1272
1273
1274/* Used to hide and reveal supplemental content, such as long code samples.
1275 See the companion CSS in android-developer-docs.css */
1276function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001277 var div = $(obj).closest(".toggle-content");
1278 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001279 if (div.hasClass("closed")) { // if it's closed, open it
1280 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001281 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001282 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001283 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001284 + "assets/images/triangle-opened.png");
1285 } else { // if it's open, close it
1286 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001287 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001288 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001289 div.find(".toggle-content").removeClass("open").addClass("closed")
1290 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001291 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001292 + "assets/images/triangle-closed.png");
1293 });
1294 }
1295 return false;
1296}
Scott Mainf5089842012-08-14 16:31:07 -07001297
1298
Scott Maindb3678b2012-10-23 14:13:41 -07001299/* New version of expandable content */
1300function toggleExpandable(link,id) {
1301 if($(id).is(':visible')) {
1302 $(id).slideUp();
1303 $(link).removeClass('expanded');
1304 } else {
1305 $(id).slideDown();
1306 $(link).addClass('expanded');
1307 }
1308}
1309
1310function hideExpandable(ids) {
1311 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001312 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001313}
1314
Scott Mainf5089842012-08-14 16:31:07 -07001315
1316
1317
1318
Scott Main3b90aff2013-08-01 18:09:35 -07001319/*
Scott Mainf5089842012-08-14 16:31:07 -07001320 * Slideshow 1.0
1321 * Used on /index.html and /develop/index.html for carousel
1322 *
1323 * Sample usage:
1324 * HTML -
1325 * <div class="slideshow-container">
1326 * <a href="" class="slideshow-prev">Prev</a>
1327 * <a href="" class="slideshow-next">Next</a>
1328 * <ul>
1329 * <li class="item"><img src="images/marquee1.jpg"></li>
1330 * <li class="item"><img src="images/marquee2.jpg"></li>
1331 * <li class="item"><img src="images/marquee3.jpg"></li>
1332 * <li class="item"><img src="images/marquee4.jpg"></li>
1333 * </ul>
1334 * </div>
1335 *
1336 * <script type="text/javascript">
1337 * $('.slideshow-container').dacSlideshow({
1338 * auto: true,
1339 * btnPrev: '.slideshow-prev',
1340 * btnNext: '.slideshow-next'
1341 * });
1342 * </script>
1343 *
1344 * Options:
1345 * btnPrev: optional identifier for previous button
1346 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001347 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001348 * auto: whether or not to auto-proceed
1349 * speed: animation speed
1350 * autoTime: time between auto-rotation
1351 * easing: easing function for transition
1352 * start: item to select by default
1353 * scroll: direction to scroll in
1354 * pagination: whether or not to include dotted pagination
1355 *
1356 */
1357
1358 (function($) {
1359 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001360
Scott Mainf5089842012-08-14 16:31:07 -07001361 //Options - see above
1362 o = $.extend({
1363 btnPrev: null,
1364 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001365 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001366 auto: true,
1367 speed: 500,
1368 autoTime: 12000,
1369 easing: null,
1370 start: 0,
1371 scroll: 1,
1372 pagination: true
1373
1374 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001375
1376 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001377 return this.each(function() {
1378
1379 var running = false;
1380 var animCss = o.vertical ? "top" : "left";
1381 var sizeCss = o.vertical ? "height" : "width";
1382 var div = $(this);
1383 var ul = $("ul", div);
1384 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001385 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001386 var timer = null;
1387
1388 var li = $("li", ul);
1389 var itemLength = li.size();
1390 var curr = o.start;
1391
1392 li.css({float: o.vertical ? "none" : "left"});
1393 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1394 div.css({position: "relative", "z-index": "2", left: "0px"});
1395
1396 var liSize = o.vertical ? height(li) : width(li);
1397 var ulSize = liSize * itemLength;
1398 var divSize = liSize;
1399
1400 li.css({width: li.width(), height: li.height()});
1401 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1402
1403 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001404
Scott Mainf5089842012-08-14 16:31:07 -07001405 //Pagination
1406 if (o.pagination) {
1407 var pagination = $("<div class='pagination'></div>");
1408 var pag_ul = $("<ul></ul>");
1409 if (tl > 1) {
1410 for (var i=0;i<tl;i++) {
1411 var li = $("<li>"+i+"</li>");
1412 pag_ul.append(li);
1413 if (i==o.start) li.addClass('active');
1414 li.click(function() {
1415 go(parseInt($(this).text()));
1416 })
1417 }
1418 pagination.append(pag_ul);
1419 div.append(pagination);
1420 }
1421 }
Scott Main3b90aff2013-08-01 18:09:35 -07001422
Scott Mainf5089842012-08-14 16:31:07 -07001423 //Previous button
1424 if(o.btnPrev)
1425 $(o.btnPrev).click(function(e) {
1426 e.preventDefault();
1427 return go(curr-o.scroll);
1428 });
1429
1430 //Next button
1431 if(o.btnNext)
1432 $(o.btnNext).click(function(e) {
1433 e.preventDefault();
1434 return go(curr+o.scroll);
1435 });
Scott Maineb410352013-01-14 19:03:40 -08001436
1437 //Pause button
1438 if(o.btnPause)
1439 $(o.btnPause).click(function(e) {
1440 e.preventDefault();
1441 if ($(this).hasClass('paused')) {
1442 startRotateTimer();
1443 } else {
1444 pauseRotateTimer();
1445 }
1446 });
Scott Main3b90aff2013-08-01 18:09:35 -07001447
Scott Mainf5089842012-08-14 16:31:07 -07001448 //Auto rotation
1449 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001450
Scott Mainf5089842012-08-14 16:31:07 -07001451 function startRotateTimer() {
1452 clearInterval(timer);
1453 timer = setInterval(function() {
1454 if (curr == tl-1) {
1455 go(0);
1456 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001457 go(curr+o.scroll);
1458 }
Scott Mainf5089842012-08-14 16:31:07 -07001459 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001460 $(o.btnPause).removeClass('paused');
1461 }
1462
1463 function pauseRotateTimer() {
1464 clearInterval(timer);
1465 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001466 }
1467
1468 //Go to an item
1469 function go(to) {
1470 if(!running) {
1471
1472 if(to<0) {
1473 to = itemLength-1;
1474 } else if (to>itemLength-1) {
1475 to = 0;
1476 }
1477 curr = to;
1478
1479 running = true;
1480
1481 ul.animate(
1482 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1483 function() {
1484 running = false;
1485 }
1486 );
1487
1488 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1489 $( (curr-o.scroll<0 && o.btnPrev)
1490 ||
1491 (curr+o.scroll > itemLength && o.btnNext)
1492 ||
1493 []
1494 ).addClass("disabled");
1495
Scott Main3b90aff2013-08-01 18:09:35 -07001496
Scott Mainf5089842012-08-14 16:31:07 -07001497 var nav_items = $('li', pagination);
1498 nav_items.removeClass('active');
1499 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001500
Scott Mainf5089842012-08-14 16:31:07 -07001501
1502 }
1503 if(o.auto) startRotateTimer();
1504 return false;
1505 };
1506 });
1507 };
1508
1509 function css(el, prop) {
1510 return parseInt($.css(el[0], prop)) || 0;
1511 };
1512 function width(el) {
1513 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1514 };
1515 function height(el) {
1516 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1517 };
1518
1519 })(jQuery);
1520
1521
Scott Main3b90aff2013-08-01 18:09:35 -07001522/*
Scott Mainf5089842012-08-14 16:31:07 -07001523 * dacSlideshow 1.0
1524 * Used on develop/index.html for side-sliding tabs
1525 *
1526 * Sample usage:
1527 * HTML -
1528 * <div class="slideshow-container">
1529 * <a href="" class="slideshow-prev">Prev</a>
1530 * <a href="" class="slideshow-next">Next</a>
1531 * <ul>
1532 * <li class="item"><img src="images/marquee1.jpg"></li>
1533 * <li class="item"><img src="images/marquee2.jpg"></li>
1534 * <li class="item"><img src="images/marquee3.jpg"></li>
1535 * <li class="item"><img src="images/marquee4.jpg"></li>
1536 * </ul>
1537 * </div>
1538 *
1539 * <script type="text/javascript">
1540 * $('.slideshow-container').dacSlideshow({
1541 * auto: true,
1542 * btnPrev: '.slideshow-prev',
1543 * btnNext: '.slideshow-next'
1544 * });
1545 * </script>
1546 *
1547 * Options:
1548 * btnPrev: optional identifier for previous button
1549 * btnNext: optional identifier for next button
1550 * auto: whether or not to auto-proceed
1551 * speed: animation speed
1552 * autoTime: time between auto-rotation
1553 * easing: easing function for transition
1554 * start: item to select by default
1555 * scroll: direction to scroll in
1556 * pagination: whether or not to include dotted pagination
1557 *
1558 */
1559 (function($) {
1560 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001561
Scott Mainf5089842012-08-14 16:31:07 -07001562 //Options - see above
1563 o = $.extend({
1564 speed : 250,
1565 easing: null,
1566 nav_id: null,
1567 frame_id: null
1568 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001569
1570 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001571 return this.each(function() {
1572
1573 var curr = 0;
1574 var running = false;
1575 var animCss = "margin-left";
1576 var sizeCss = "width";
1577 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001578
Scott Mainf5089842012-08-14 16:31:07 -07001579 var nav = $(o.nav_id, div);
1580 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001581 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001582 var frame = div.find(o.frame_id);
1583 var content_width = $(frame).find('ul').width();
1584 //Buttons
1585 $(nav_li).click(function(e) {
1586 go($(nav_li).index($(this)));
1587 })
Scott Main3b90aff2013-08-01 18:09:35 -07001588
Scott Mainf5089842012-08-14 16:31:07 -07001589 //Go to an item
1590 function go(to) {
1591 if(!running) {
1592 curr = to;
1593 running = true;
1594
1595 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1596 function() {
1597 running = false;
1598 }
1599 );
1600
Scott Main3b90aff2013-08-01 18:09:35 -07001601
Scott Mainf5089842012-08-14 16:31:07 -07001602 nav_li.removeClass('active');
1603 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001604
Scott Mainf5089842012-08-14 16:31:07 -07001605
1606 }
1607 return false;
1608 };
1609 });
1610 };
1611
1612 function css(el, prop) {
1613 return parseInt($.css(el[0], prop)) || 0;
1614 };
1615 function width(el) {
1616 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1617 };
1618 function height(el) {
1619 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1620 };
1621
1622 })(jQuery);
1623
1624
1625
1626
1627
1628/* ######################################################## */
1629/* ################ SEARCH SUGGESTIONS ################## */
1630/* ######################################################## */
1631
1632
Scott Main7e447ed2013-02-19 17:22:37 -08001633
Scott Main0e76e7e2013-03-12 10:24:07 -07001634var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1635var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1636
Scott Mainf5089842012-08-14 16:31:07 -07001637var gMatches = new Array();
1638var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001639var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001640var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1641var gListLength = 0;
1642
1643
1644var gGoogleMatches = new Array();
1645var ROW_COUNT_GOOGLE = 15; // max number of results in list
1646var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001647
Scott Main0e76e7e2013-03-12 10:24:07 -07001648var gDocsMatches = new Array();
1649var ROW_COUNT_DOCS = 100; // max number of results in list
1650var gDocsListLength = 0;
1651
Scott Mainde295272013-03-25 15:48:35 -07001652function onSuggestionClick(link) {
1653 // When user clicks a suggested document, track it
1654 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1655 'from: ' + $("#search_autocomplete").val()]);
1656}
1657
Scott Mainf5089842012-08-14 16:31:07 -07001658function set_item_selected($li, selected)
1659{
1660 if (selected) {
1661 $li.attr('class','jd-autocomplete jd-selected');
1662 } else {
1663 $li.attr('class','jd-autocomplete');
1664 }
1665}
1666
1667function set_item_values(toroot, $li, match)
1668{
1669 var $link = $('a',$li);
1670 $link.html(match.__hilabel || match.label);
1671 $link.attr('href',toroot + match.link);
1672}
1673
Scott Main719acb42013-12-05 16:05:09 -08001674function set_item_values_jd(toroot, $li, match)
1675{
1676 var $link = $('a',$li);
1677 $link.html(match.title);
1678 $link.attr('href',toroot + match.url);
1679}
1680
Scott Main0e76e7e2013-03-12 10:24:07 -07001681function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001682 var $li = $("<li class='jd-autocomplete'></li>");
1683 $list.append($li);
1684
1685 $li.mousedown(function() {
1686 window.location = this.firstChild.getAttribute("href");
1687 });
1688 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001689 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001690 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001691 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1692 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001693 });
Scott Mainde295272013-03-25 15:48:35 -07001694 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001695 $li.attr('class','show-item');
1696 return $li;
1697}
1698
Scott Mainf5089842012-08-14 16:31:07 -07001699function sync_selection_table(toroot)
1700{
Scott Mainf5089842012-08-14 16:31:07 -07001701 var $li; //list item jquery object
1702 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001703
Scott Main0e76e7e2013-03-12 10:24:07 -07001704 // if there are NO results at all, hide all columns
1705 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1706 $('.suggest-card').hide(300);
1707 return;
1708 }
1709
1710 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001711 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001712 // reveal suggestion list
1713 $('.suggest-card.dummy').show();
1714 $('.suggest-card.reference').show();
1715 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001716
Scott Main0e76e7e2013-03-12 10:24:07 -07001717 // reset the lists
1718 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001719
Scott Main0e76e7e2013-03-12 10:24:07 -07001720 // ########### ANDROID RESULTS #############
1721 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001722
Scott Main0e76e7e2013-03-12 10:24:07 -07001723 // determine android results to show
1724 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1725 gMatches.length : ROW_COUNT_FRAMEWORK;
1726 for (i=0; i<gListLength; i++) {
1727 var $li = new_suggestion($(".suggest-card.reference ul"));
1728 set_item_values(toroot, $li, gMatches[i]);
1729 set_item_selected($li, i == gSelectedIndex);
1730 }
1731 }
Scott Main7e447ed2013-02-19 17:22:37 -08001732
Scott Main0e76e7e2013-03-12 10:24:07 -07001733 // ########### GOOGLE RESULTS #############
1734 if (gGoogleMatches.length > 0) {
1735 // show header for list
1736 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001737
Scott Main0e76e7e2013-03-12 10:24:07 -07001738 // determine google results to show
1739 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1740 for (i=0; i<gGoogleListLength; i++) {
1741 var $li = new_suggestion($(".suggest-card.reference ul"));
1742 set_item_values(toroot, $li, gGoogleMatches[i]);
1743 set_item_selected($li, i == gSelectedIndex);
1744 }
1745 }
Scott Mainf5089842012-08-14 16:31:07 -07001746 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001747 $('.suggest-card.reference').hide();
1748 $('.suggest-card.dummy').hide();
1749 }
1750
1751 // ########### JD DOC RESULTS #############
1752 if (gDocsMatches.length > 0) {
1753 // reset the lists
1754 $(".search_filtered_wrapper.docs li").remove();
1755
1756 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001757 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1758 // The order must match the reverse order that each section appears as a card in
1759 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001760 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1761 for (i=0; i<gDocsListLength; i++) {
1762 var sugg = gDocsMatches[i];
1763 var $li;
1764 if (sugg.type == "design") {
1765 $li = new_suggestion($(".suggest-card.design ul"));
1766 } else
1767 if (sugg.type == "distribute") {
1768 $li = new_suggestion($(".suggest-card.distribute ul"));
1769 } else
Scott Main719acb42013-12-05 16:05:09 -08001770 if (sugg.type == "samples") {
1771 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1772 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001773 if (sugg.type == "training") {
1774 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1775 } else
Scott Main719acb42013-12-05 16:05:09 -08001776 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001777 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1778 } else {
1779 continue;
1780 }
1781
Scott Main719acb42013-12-05 16:05:09 -08001782 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001783 set_item_selected($li, i == gSelectedIndex);
1784 }
1785
1786 // add heading and show or hide card
1787 if ($(".suggest-card.design li").length > 0) {
1788 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1789 $(".suggest-card.design").show(300);
1790 } else {
1791 $('.suggest-card.design').hide(300);
1792 }
1793 if ($(".suggest-card.distribute li").length > 0) {
1794 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1795 $(".suggest-card.distribute").show(300);
1796 } else {
1797 $('.suggest-card.distribute').hide(300);
1798 }
1799 if ($(".child-card.guides li").length > 0) {
1800 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1801 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1802 }
1803 if ($(".child-card.training li").length > 0) {
1804 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1805 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1806 }
Scott Main719acb42013-12-05 16:05:09 -08001807 if ($(".child-card.samples li").length > 0) {
1808 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1809 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1810 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001811
1812 if ($(".suggest-card.develop li").length > 0) {
1813 $(".suggest-card.develop").show(300);
1814 } else {
1815 $('.suggest-card.develop').hide(300);
1816 }
1817
1818 } else {
1819 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001820 }
1821}
1822
Scott Main0e76e7e2013-03-12 10:24:07 -07001823/** Called by the search input's onkeydown and onkeyup events.
1824 * Handles navigation with keyboard arrows, Enter key to invoke search,
1825 * otherwise invokes search suggestions on key-up event.
1826 * @param e The JS event
1827 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001828 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001829 * @returns True if the event should bubble up
1830 */
Scott Mainf5089842012-08-14 16:31:07 -07001831function search_changed(e, kd, toroot)
1832{
Scott Main719acb42013-12-05 16:05:09 -08001833 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001834 var search = document.getElementById("search_autocomplete");
1835 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001836 // get the ul hosting the currently selected item
1837 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1838 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1839 var $selectedUl = $columns[gSelectedColumn];
1840
Scott Mainf5089842012-08-14 16:31:07 -07001841 // show/hide the close button
1842 if (text != '') {
1843 $(".search .close").removeClass("hide");
1844 } else {
1845 $(".search .close").addClass("hide");
1846 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001847 // 27 = esc
1848 if (e.keyCode == 27) {
1849 // close all search results
1850 if (kd) $('.search .close').trigger('click');
1851 return true;
1852 }
Scott Mainf5089842012-08-14 16:31:07 -07001853 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001854 else if (e.keyCode == 13) {
1855 if (gSelectedIndex < 0) {
1856 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001857 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1858 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001859 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001860 return true;
1861 } else {
1862 // otherwise, results are already showing, so allow ajax to auto refresh the results
1863 // and ignore this Enter press to avoid the reload.
1864 return false;
1865 }
1866 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001867 // click the link corresponding to selected item
1868 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001869 return false;
1870 }
1871 }
Scott Mainb16376f2014-05-21 20:35:47 -07001872 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001873 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001874 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001875 if ((sticky ) && (search.value != "")) {
1876 $('body,html').animate({scrollTop:0}, '500', 'swing');
1877 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001878 return true;
1879 }
1880 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001881 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001882 // if the next item is a header, skip it
1883 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001884 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001885 }
1886 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001887 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001888 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001889 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1890 // If user reaches top, reset selected column
1891 if (gSelectedIndex < 0) {
1892 gSelectedColumn = -1;
1893 }
Scott Mainf5089842012-08-14 16:31:07 -07001894 }
1895 return false;
1896 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001897 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001898 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001899 // if the next item is a header, skip it
1900 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001901 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001902 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001903 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1904 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1905 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001906 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001907 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001908 }
1909 return false;
1910 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001911 // Consider left/right arrow navigation
1912 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1913 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1914 // 37 LEFT ARROW
1915 // go left only if current column is not left-most column (last column)
1916 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1917 $('li', $selectedUl).removeClass('jd-selected');
1918 gSelectedColumn++;
1919 $selectedUl = $columns[gSelectedColumn];
1920 // keep or reset the selected item to last item as appropriate
1921 gSelectedIndex = gSelectedIndex >
1922 $("li", $selectedUl).length-1 ?
1923 $("li", $selectedUl).length-1 : gSelectedIndex;
1924 // if the corresponding item is a header, move down
1925 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1926 gSelectedIndex++;
1927 }
Scott Main3b90aff2013-08-01 18:09:35 -07001928 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001929 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1930 return false;
1931 }
1932 // 39 RIGHT ARROW
1933 // go right only if current column is not the right-most column (first column)
1934 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1935 $('li', $selectedUl).removeClass('jd-selected');
1936 gSelectedColumn--;
1937 $selectedUl = $columns[gSelectedColumn];
1938 // keep or reset the selected item to last item as appropriate
1939 gSelectedIndex = gSelectedIndex >
1940 $("li", $selectedUl).length-1 ?
1941 $("li", $selectedUl).length-1 : gSelectedIndex;
1942 // if the corresponding item is a header, move down
1943 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1944 gSelectedIndex++;
1945 }
Scott Main3b90aff2013-08-01 18:09:35 -07001946 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001947 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1948 return false;
1949 }
1950 }
1951
Scott Main719acb42013-12-05 16:05:09 -08001952 // if key-up event and not arrow down/up/left/right,
1953 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001954 else if (!kd && (e.keyCode != 40)
1955 && (e.keyCode != 38)
1956 && (e.keyCode != 37)
1957 && (e.keyCode != 39)) {
1958 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001959 gMatches = new Array();
1960 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001961 gGoogleMatches = new Array();
1962 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001963 gDocsMatches = new Array();
1964 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001965
1966 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001967 for (var i=0; i<DATA.length; i++) {
1968 var s = DATA[i];
1969 if (text.length != 0 &&
1970 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1971 gMatches[matchedCount] = s;
1972 matchedCount++;
1973 }
1974 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001975 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001976 for (var i=0; i<gMatches.length; i++) {
1977 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001978 }
1979
1980
1981 // Search for Google matches
1982 for (var i=0; i<GOOGLE_DATA.length; i++) {
1983 var s = GOOGLE_DATA[i];
1984 if (text.length != 0 &&
1985 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1986 gGoogleMatches[matchedCountGoogle] = s;
1987 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001988 }
1989 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001990 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001991 for (var i=0; i<gGoogleMatches.length; i++) {
1992 var s = gGoogleMatches[i];
1993 }
1994
Scott Mainf5089842012-08-14 16:31:07 -07001995 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001996
1997
1998
Scott Main719acb42013-12-05 16:05:09 -08001999 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07002000 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08002001 // Regex to match only the beginning of a word
2002 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
2003
2004
2005 // Search for Training classes
2006 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002007 // current search comparison, with counters for tag and title,
2008 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002009 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002010 s.matched_tag = 0;
2011 s.matched_title = 0;
2012 var matched = false;
2013
2014 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08002015 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002016 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08002017 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002018 matched = true;
2019 s.matched_tag = j + 1; // add 1 to index position
2020 }
2021 }
Scott Main719acb42013-12-05 16:05:09 -08002022 // Don't consider doc title for lessons (only for class landing pages),
2023 // unless the lesson has a tag that already matches
2024 if ((s.lang == currentLang) &&
2025 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002026 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08002027 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002028 matched = true;
2029 s.matched_title = 1;
2030 }
2031 }
2032 if (matched) {
2033 gDocsMatches[matchedCountDocs] = s;
2034 matchedCountDocs++;
2035 }
2036 }
Scott Main719acb42013-12-05 16:05:09 -08002037
2038
2039 // Search for API Guides
2040 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2041 // current search comparison, with counters for tag and title,
2042 // used later to improve ranking
2043 var s = GUIDE_RESOURCES[i];
2044 s.matched_tag = 0;
2045 s.matched_title = 0;
2046 var matched = false;
2047
2048 // Check if query matches any tags; work backwards toward 1 to assist ranking
2049 for (var j = s.keywords.length - 1; j >= 0; j--) {
2050 // it matches a tag
2051 if (s.keywords[j].toLowerCase().match(textRegex)) {
2052 matched = true;
2053 s.matched_tag = j + 1; // add 1 to index position
2054 }
2055 }
2056 // Check if query matches the doc title, but only for current language
2057 if (s.lang == currentLang) {
2058 // if query matches the doc title
2059 if (s.title.toLowerCase().match(textRegex)) {
2060 matched = true;
2061 s.matched_title = 1;
2062 }
2063 }
2064 if (matched) {
2065 gDocsMatches[matchedCountDocs] = s;
2066 matchedCountDocs++;
2067 }
2068 }
2069
2070
2071 // Search for Tools Guides
2072 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2073 // current search comparison, with counters for tag and title,
2074 // used later to improve ranking
2075 var s = TOOLS_RESOURCES[i];
2076 s.matched_tag = 0;
2077 s.matched_title = 0;
2078 var matched = false;
2079
2080 // Check if query matches any tags; work backwards toward 1 to assist ranking
2081 for (var j = s.keywords.length - 1; j >= 0; j--) {
2082 // it matches a tag
2083 if (s.keywords[j].toLowerCase().match(textRegex)) {
2084 matched = true;
2085 s.matched_tag = j + 1; // add 1 to index position
2086 }
2087 }
2088 // Check if query matches the doc title, but only for current language
2089 if (s.lang == currentLang) {
2090 // if query matches the doc title
2091 if (s.title.toLowerCase().match(textRegex)) {
2092 matched = true;
2093 s.matched_title = 1;
2094 }
2095 }
2096 if (matched) {
2097 gDocsMatches[matchedCountDocs] = s;
2098 matchedCountDocs++;
2099 }
2100 }
2101
2102
2103 // Search for About docs
2104 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2105 // current search comparison, with counters for tag and title,
2106 // used later to improve ranking
2107 var s = ABOUT_RESOURCES[i];
2108 s.matched_tag = 0;
2109 s.matched_title = 0;
2110 var matched = false;
2111
2112 // Check if query matches any tags; work backwards toward 1 to assist ranking
2113 for (var j = s.keywords.length - 1; j >= 0; j--) {
2114 // it matches a tag
2115 if (s.keywords[j].toLowerCase().match(textRegex)) {
2116 matched = true;
2117 s.matched_tag = j + 1; // add 1 to index position
2118 }
2119 }
2120 // Check if query matches the doc title, but only for current language
2121 if (s.lang == currentLang) {
2122 // if query matches the doc title
2123 if (s.title.toLowerCase().match(textRegex)) {
2124 matched = true;
2125 s.matched_title = 1;
2126 }
2127 }
2128 if (matched) {
2129 gDocsMatches[matchedCountDocs] = s;
2130 matchedCountDocs++;
2131 }
2132 }
2133
2134
2135 // Search for Design guides
2136 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2137 // current search comparison, with counters for tag and title,
2138 // used later to improve ranking
2139 var s = DESIGN_RESOURCES[i];
2140 s.matched_tag = 0;
2141 s.matched_title = 0;
2142 var matched = false;
2143
2144 // Check if query matches any tags; work backwards toward 1 to assist ranking
2145 for (var j = s.keywords.length - 1; j >= 0; j--) {
2146 // it matches a tag
2147 if (s.keywords[j].toLowerCase().match(textRegex)) {
2148 matched = true;
2149 s.matched_tag = j + 1; // add 1 to index position
2150 }
2151 }
2152 // Check if query matches the doc title, but only for current language
2153 if (s.lang == currentLang) {
2154 // if query matches the doc title
2155 if (s.title.toLowerCase().match(textRegex)) {
2156 matched = true;
2157 s.matched_title = 1;
2158 }
2159 }
2160 if (matched) {
2161 gDocsMatches[matchedCountDocs] = s;
2162 matchedCountDocs++;
2163 }
2164 }
2165
2166
2167 // Search for Distribute guides
2168 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2169 // current search comparison, with counters for tag and title,
2170 // used later to improve ranking
2171 var s = DISTRIBUTE_RESOURCES[i];
2172 s.matched_tag = 0;
2173 s.matched_title = 0;
2174 var matched = false;
2175
2176 // Check if query matches any tags; work backwards toward 1 to assist ranking
2177 for (var j = s.keywords.length - 1; j >= 0; j--) {
2178 // it matches a tag
2179 if (s.keywords[j].toLowerCase().match(textRegex)) {
2180 matched = true;
2181 s.matched_tag = j + 1; // add 1 to index position
2182 }
2183 }
2184 // Check if query matches the doc title, but only for current language
2185 if (s.lang == currentLang) {
2186 // if query matches the doc title
2187 if (s.title.toLowerCase().match(textRegex)) {
2188 matched = true;
2189 s.matched_title = 1;
2190 }
2191 }
2192 if (matched) {
2193 gDocsMatches[matchedCountDocs] = s;
2194 matchedCountDocs++;
2195 }
2196 }
2197
2198
2199 // Search for Google guides
2200 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2201 // current search comparison, with counters for tag and title,
2202 // used later to improve ranking
2203 var s = GOOGLE_RESOURCES[i];
2204 s.matched_tag = 0;
2205 s.matched_title = 0;
2206 var matched = false;
2207
2208 // Check if query matches any tags; work backwards toward 1 to assist ranking
2209 for (var j = s.keywords.length - 1; j >= 0; j--) {
2210 // it matches a tag
2211 if (s.keywords[j].toLowerCase().match(textRegex)) {
2212 matched = true;
2213 s.matched_tag = j + 1; // add 1 to index position
2214 }
2215 }
2216 // Check if query matches the doc title, but only for current language
2217 if (s.lang == currentLang) {
2218 // if query matches the doc title
2219 if (s.title.toLowerCase().match(textRegex)) {
2220 matched = true;
2221 s.matched_title = 1;
2222 }
2223 }
2224 if (matched) {
2225 gDocsMatches[matchedCountDocs] = s;
2226 matchedCountDocs++;
2227 }
2228 }
2229
2230
2231 // Search for Samples
2232 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2233 // current search comparison, with counters for tag and title,
2234 // used later to improve ranking
2235 var s = SAMPLES_RESOURCES[i];
2236 s.matched_tag = 0;
2237 s.matched_title = 0;
2238 var matched = false;
2239 // Check if query matches any tags; work backwards toward 1 to assist ranking
2240 for (var j = s.keywords.length - 1; j >= 0; j--) {
2241 // it matches a tag
2242 if (s.keywords[j].toLowerCase().match(textRegex)) {
2243 matched = true;
2244 s.matched_tag = j + 1; // add 1 to index position
2245 }
2246 }
2247 // Check if query matches the doc title, but only for current language
2248 if (s.lang == currentLang) {
2249 // if query matches the doc title.t
2250 if (s.title.toLowerCase().match(textRegex)) {
2251 matched = true;
2252 s.matched_title = 1;
2253 }
2254 }
2255 if (matched) {
2256 gDocsMatches[matchedCountDocs] = s;
2257 matchedCountDocs++;
2258 }
2259 }
2260
2261 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002262 rank_autocomplete_doc_results(text, gDocsMatches);
2263 }
2264
2265 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002266 sync_selection_table(toroot);
2267 return true; // allow the event to bubble up to the search api
2268 }
2269}
2270
Scott Main0e76e7e2013-03-12 10:24:07 -07002271/* Order the jd doc result list based on match quality */
2272function rank_autocomplete_doc_results(query, matches) {
2273 query = query || '';
2274 if (!matches || !matches.length)
2275 return;
2276
2277 var _resultScoreFn = function(match) {
2278 var score = 1.0;
2279
2280 // if the query matched a tag
2281 if (match.matched_tag > 0) {
2282 // multiply score by factor relative to position in tags list (max of 3)
2283 score *= 3 / match.matched_tag;
2284
2285 // if it also matched the title
2286 if (match.matched_title > 0) {
2287 score *= 2;
2288 }
2289 } else if (match.matched_title > 0) {
2290 score *= 3;
2291 }
2292
2293 return score;
2294 };
2295
2296 for (var i=0; i<matches.length; i++) {
2297 matches[i].__resultScore = _resultScoreFn(matches[i]);
2298 }
2299
2300 matches.sort(function(a,b){
2301 var n = b.__resultScore - a.__resultScore;
2302 if (n == 0) // lexicographical sort if scores are the same
2303 n = (a.label < b.label) ? -1 : 1;
2304 return n;
2305 });
2306}
2307
Scott Main7e447ed2013-02-19 17:22:37 -08002308/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002309function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002310 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002311 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002312 return;
2313
2314 // helper function that gets the last occurence index of the given regex
2315 // in the given string, or -1 if not found
2316 var _lastSearch = function(s, re) {
2317 if (s == '')
2318 return -1;
2319 var l = -1;
2320 var tmp;
2321 while ((tmp = s.search(re)) >= 0) {
2322 if (l < 0) l = 0;
2323 l += tmp;
2324 s = s.substr(tmp + 1);
2325 }
2326 return l;
2327 };
2328
2329 // helper function that counts the occurrences of a given character in
2330 // a given string
2331 var _countChar = function(s, c) {
2332 var n = 0;
2333 for (var i=0; i<s.length; i++)
2334 if (s.charAt(i) == c) ++n;
2335 return n;
2336 };
2337
2338 var queryLower = query.toLowerCase();
2339 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2340 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2341 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2342
2343 var _resultScoreFn = function(result) {
2344 // scores are calculated based on exact and prefix matches,
2345 // and then number of path separators (dots) from the last
2346 // match (i.e. favoring classes and deep package names)
2347 var score = 1.0;
2348 var labelLower = result.label.toLowerCase();
2349 var t;
2350 t = _lastSearch(labelLower, partExactAlnumRE);
2351 if (t >= 0) {
2352 // exact part match
2353 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2354 score *= 200 / (partsAfter + 1);
2355 } else {
2356 t = _lastSearch(labelLower, partPrefixAlnumRE);
2357 if (t >= 0) {
2358 // part prefix match
2359 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2360 score *= 20 / (partsAfter + 1);
2361 }
2362 }
2363
2364 return score;
2365 };
2366
Scott Main7e447ed2013-02-19 17:22:37 -08002367 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002368 // if the API is deprecated, default score is 0; otherwise, perform scoring
2369 if (matches[i].deprecated == "true") {
2370 matches[i].__resultScore = 0;
2371 } else {
2372 matches[i].__resultScore = _resultScoreFn(matches[i]);
2373 }
Scott Mainf5089842012-08-14 16:31:07 -07002374 }
2375
Scott Main7e447ed2013-02-19 17:22:37 -08002376 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002377 var n = b.__resultScore - a.__resultScore;
2378 if (n == 0) // lexicographical sort if scores are the same
2379 n = (a.label < b.label) ? -1 : 1;
2380 return n;
2381 });
2382}
2383
Scott Main7e447ed2013-02-19 17:22:37 -08002384/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002385function highlight_autocomplete_result_labels(query) {
2386 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002387 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002388 return;
2389
2390 var queryLower = query.toLowerCase();
2391 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2392 var queryRE = new RegExp(
2393 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2394 for (var i=0; i<gMatches.length; i++) {
2395 gMatches[i].__hilabel = gMatches[i].label.replace(
2396 queryRE, '<b>$1</b>');
2397 }
Scott Main7e447ed2013-02-19 17:22:37 -08002398 for (var i=0; i<gGoogleMatches.length; i++) {
2399 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2400 queryRE, '<b>$1</b>');
2401 }
Scott Mainf5089842012-08-14 16:31:07 -07002402}
2403
2404function search_focus_changed(obj, focused)
2405{
Scott Main3b90aff2013-08-01 18:09:35 -07002406 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002407 if(obj.value == ""){
2408 $(".search .close").addClass("hide");
2409 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002410 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002411 }
2412}
2413
2414function submit_search() {
2415 var query = document.getElementById('search_autocomplete').value;
2416 location.hash = 'q=' + query;
2417 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002418 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002419 return false;
2420}
2421
2422
2423function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002424 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002425 $(".search .close").addClass("hide");
2426 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002427
Scott Mainf5089842012-08-14 16:31:07 -07002428 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002429
Scott Mainf5089842012-08-14 16:31:07 -07002430 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2431 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002432
2433 // forcefully regain key-up event control (previously jacked by search api)
2434 $("#search_autocomplete").keyup(function(event) {
2435 return search_changed(event, false, toRoot);
2436 });
2437
Scott Mainf5089842012-08-14 16:31:07 -07002438 return false;
2439}
2440
2441
2442
2443/* ########################################################## */
2444/* ################ CUSTOM SEARCH ENGINE ################## */
2445/* ########################################################## */
2446
Scott Mainf5089842012-08-14 16:31:07 -07002447var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002448google.load('search', '1', {"callback" : function() {
2449 searchControl = new google.search.SearchControl();
2450 } });
Scott Mainf5089842012-08-14 16:31:07 -07002451
2452function loadSearchResults() {
2453 document.getElementById("search_autocomplete").style.color = "#000";
2454
Scott Mainf5089842012-08-14 16:31:07 -07002455 searchControl = new google.search.SearchControl();
2456
2457 // use our existing search form and use tabs when multiple searchers are used
2458 drawOptions = new google.search.DrawOptions();
2459 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2460 drawOptions.setInput(document.getElementById("search_autocomplete"));
2461
2462 // configure search result options
2463 searchOptions = new google.search.SearcherOptions();
2464 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2465
2466 // configure each of the searchers, for each tab
2467 devSiteSearcher = new google.search.WebSearch();
2468 devSiteSearcher.setUserDefinedLabel("All");
2469 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2470
2471 designSearcher = new google.search.WebSearch();
2472 designSearcher.setUserDefinedLabel("Design");
2473 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2474
2475 trainingSearcher = new google.search.WebSearch();
2476 trainingSearcher.setUserDefinedLabel("Training");
2477 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2478
2479 guidesSearcher = new google.search.WebSearch();
2480 guidesSearcher.setUserDefinedLabel("Guides");
2481 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2482
2483 referenceSearcher = new google.search.WebSearch();
2484 referenceSearcher.setUserDefinedLabel("Reference");
2485 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2486
Scott Maindf08ada2012-12-03 08:54:37 -08002487 googleSearcher = new google.search.WebSearch();
2488 googleSearcher.setUserDefinedLabel("Google Services");
2489 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2490
Scott Mainf5089842012-08-14 16:31:07 -07002491 blogSearcher = new google.search.WebSearch();
2492 blogSearcher.setUserDefinedLabel("Blog");
2493 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2494
2495 // add each searcher to the search control
2496 searchControl.addSearcher(devSiteSearcher, searchOptions);
2497 searchControl.addSearcher(designSearcher, searchOptions);
2498 searchControl.addSearcher(trainingSearcher, searchOptions);
2499 searchControl.addSearcher(guidesSearcher, searchOptions);
2500 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002501 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002502 searchControl.addSearcher(blogSearcher, searchOptions);
2503
2504 // configure result options
2505 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2506 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2507 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2508 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2509
2510 // upon ajax search, refresh the url and search title
2511 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2512 updateResultTitle(query);
2513 var query = document.getElementById('search_autocomplete').value;
2514 location.hash = 'q=' + query;
2515 });
2516
Scott Mainde295272013-03-25 15:48:35 -07002517 // once search results load, set up click listeners
2518 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2519 addResultClickListeners();
2520 });
2521
Scott Mainf5089842012-08-14 16:31:07 -07002522 // draw the search results box
2523 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2524
2525 // get query and execute the search
2526 searchControl.execute(decodeURI(getQuery(location.hash)));
2527
2528 document.getElementById("search_autocomplete").focus();
2529 addTabListeners();
2530}
2531// End of loadSearchResults
2532
2533
2534google.setOnLoadCallback(function(){
2535 if (location.hash.indexOf("q=") == -1) {
2536 // if there's no query in the url, don't search and make sure results are hidden
2537 $('#searchResults').hide();
2538 return;
2539 } else {
2540 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002541 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002542 $(".search .close").removeClass("hide");
2543 loadSearchResults();
2544 }
2545}, true);
2546
Scott Mainb16376f2014-05-21 20:35:47 -07002547/* Adjust the scroll position to account for sticky header, only if the hash matches an id */
2548function offsetScrollForSticky() {
smain@google.com3b77ab52014-06-17 11:57:27 -07002549 var hash = escape(location.hash.substr(1));
2550 var $matchingElement = $("#"+hash);
Scott Mainb16376f2014-05-21 20:35:47 -07002551 // If there's no element with the hash as an ID, then look for an <a name=''> with it.
2552 if ($matchingElement.length < 1) {
smain@google.com3b77ab52014-06-17 11:57:27 -07002553 $matchingElement = $('a[name="' + hash + '"]');
Scott Mainb16376f2014-05-21 20:35:47 -07002554 }
smain@google.com3b77ab52014-06-17 11:57:27 -07002555 // Sanity check that there's an element with that ID on the page
2556 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002557 // If the position of the target element is near the top of the page (<20px, where we expect it
2558 // to be because we need to move it down 60px to become in view), then move it down 60px
2559 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2560 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002561 }
2562 }
2563}
2564
Scott Mainf5089842012-08-14 16:31:07 -07002565// when an event on the browser history occurs (back, forward, load) requery hash and do search
2566$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002567 // If the hash isn't a search query or there's an error in the query,
2568 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002569 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2570 // If the results pane is open, close it.
2571 if (!$("#searchResults").is(":hidden")) {
2572 hideResults();
2573 }
Scott Mainb16376f2014-05-21 20:35:47 -07002574 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002575 return;
2576 }
2577
2578 // Otherwise, we have a search to do
2579 var query = decodeURI(getQuery(location.hash));
2580 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002581 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002582 $("#search_autocomplete").focus();
2583 $(".search .close").removeClass("hide");
2584
2585 updateResultTitle(query);
2586});
2587
2588function updateResultTitle(query) {
2589 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2590}
2591
2592// forcefully regain key-up event control (previously jacked by search api)
2593$("#search_autocomplete").keyup(function(event) {
2594 return search_changed(event, false, toRoot);
2595});
2596
2597// add event listeners to each tab so we can track the browser history
2598function addTabListeners() {
2599 var tabHeaders = $(".gsc-tabHeader");
2600 for (var i = 0; i < tabHeaders.length; i++) {
2601 $(tabHeaders[i]).attr("id",i).click(function() {
2602 /*
2603 // make a copy of the page numbers for the search left pane
2604 setTimeout(function() {
2605 // remove any residual page numbers
2606 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002607 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002608 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002609 // and because we're going to remove it (previous line),
2610 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002611 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2612 .clone().appendTo('#searchResults .gsc-tabsArea');
2613 }, 200);
2614 */
2615 });
2616 }
2617 setTimeout(function(){$(tabHeaders[0]).click()},200);
2618}
2619
Scott Mainde295272013-03-25 15:48:35 -07002620// add analytics tracking events to each result link
2621function addResultClickListeners() {
2622 $("#searchResults a.gs-title").each(function(index, link) {
2623 // When user clicks enter for Google search results, track it
2624 $(link).click(function() {
2625 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2626 'from: ' + $("#search_autocomplete").val()]);
2627 });
2628 });
2629}
2630
Scott Mainf5089842012-08-14 16:31:07 -07002631
2632function getQuery(hash) {
2633 var queryParts = hash.split('=');
2634 return queryParts[1];
2635}
2636
2637/* returns the given string with all HTML brackets converted to entities
2638 TODO: move this to the site's JS library */
2639function escapeHTML(string) {
2640 return string.replace(/</g,"&lt;")
2641 .replace(/>/g,"&gt;");
2642}
2643
2644
2645
2646
2647
2648
2649
2650/* ######################################################## */
2651/* ################# JAVADOC REFERENCE ################### */
2652/* ######################################################## */
2653
Scott Main65511c02012-09-07 15:51:32 -07002654/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002655if (location.pathname.indexOf("/reference") == 0) {
2656 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2657 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2658 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002659 $(document).ready(function() {
2660 // init available apis based on user pref
2661 changeApiLevel();
2662 initSidenavHeightResize()
2663 });
2664 }
Scott Main65511c02012-09-07 15:51:32 -07002665}
Scott Mainf5089842012-08-14 16:31:07 -07002666
2667var API_LEVEL_COOKIE = "api_level";
2668var minLevel = 1;
2669var maxLevel = 1;
2670
2671/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002672
Scott Mainf5089842012-08-14 16:31:07 -07002673 function initSidenavHeightResize() {
2674 // Change the drag bar size to nicely fit the scrollbar positions
2675 var $dragBar = $(".ui-resizable-s");
2676 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002677
2678 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002679 containment: "#nav-panels",
2680 handles: "s",
2681 alsoResize: "#packages-nav",
2682 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2683 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2684 });
Scott Main3b90aff2013-08-01 18:09:35 -07002685
Scott Mainf5089842012-08-14 16:31:07 -07002686 }
Scott Main3b90aff2013-08-01 18:09:35 -07002687
Scott Mainf5089842012-08-14 16:31:07 -07002688function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002689 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002690 $('#devdoc-nav').css({
2691 'width' : $('#side-nav').css('width'),
2692 'margin' : $('#side-nav').css('margin')
2693 });
2694 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002695
Scott Mainf5089842012-08-14 16:31:07 -07002696 initSidenavHeightResize();
2697}
2698
2699function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002700 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002701 $('#devdoc-nav').css({
2702 'width' : $('#side-nav').css('width'),
2703 'margin' : $('#side-nav').css('margin')
2704 });
2705 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002706
Scott Mainf5089842012-08-14 16:31:07 -07002707 initSidenavHeightResize();
2708}
2709
2710function buildApiLevelSelector() {
2711 maxLevel = SINCE_DATA.length;
2712 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2713 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2714
2715 minLevel = parseInt($("#doc-api-level").attr("class"));
2716 // Handle provisional api levels; the provisional level will always be the highest possible level
2717 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2718 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2719 if (isNaN(minLevel) && minLevel.length) {
2720 minLevel = maxLevel;
2721 }
2722 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2723 for (var i = maxLevel-1; i >= 0; i--) {
2724 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2725 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2726 select.append(option);
2727 }
2728
2729 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2730 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2731 selectedLevelItem.setAttribute('selected',true);
2732}
2733
2734function changeApiLevel() {
2735 maxLevel = SINCE_DATA.length;
2736 var selectedLevel = maxLevel;
2737
2738 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2739 toggleVisisbleApis(selectedLevel, "body");
2740
2741 var date = new Date();
2742 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2743 var expiration = date.toGMTString();
2744 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2745
2746 if (selectedLevel < minLevel) {
2747 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002748 $("#naMessage").show().html("<div><p><strong>This " + thing
2749 + " requires API level " + minLevel + " or higher.</strong></p>"
2750 + "<p>This document is hidden because your selected API level for the documentation is "
2751 + selectedLevel + ". You can change the documentation API level with the selector "
2752 + "above the left navigation.</p>"
2753 + "<p>For more information about specifying the API level your app requires, "
2754 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2755 + ">Supporting Different Platform Versions</a>.</p>"
2756 + "<input type='button' value='OK, make this page visible' "
2757 + "title='Change the API level to " + minLevel + "' "
2758 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2759 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002760 } else {
2761 $("#naMessage").hide();
2762 }
2763}
2764
2765function toggleVisisbleApis(selectedLevel, context) {
2766 var apis = $(".api",context);
2767 apis.each(function(i) {
2768 var obj = $(this);
2769 var className = obj.attr("class");
2770 var apiLevelIndex = className.lastIndexOf("-")+1;
2771 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2772 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2773 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2774 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2775 return;
2776 }
2777 apiLevel = parseInt(apiLevel);
2778
2779 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2780 var selectedLevelNum = parseInt(selectedLevel)
2781 var apiLevelNum = parseInt(apiLevel);
2782 if (isNaN(apiLevelNum)) {
2783 apiLevelNum = maxLevel;
2784 }
2785
2786 // Grey things out that aren't available and give a tooltip title
2787 if (apiLevelNum > selectedLevelNum) {
2788 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002789 + apiLevel + "\" or higher. To reveal, change the target API level "
2790 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002791 }
Scott Mainf5089842012-08-14 16:31:07 -07002792 else obj.removeClass("absent").removeAttr("title");
2793 });
2794}
2795
2796
2797
2798
2799/* ################# SIDENAV TREE VIEW ################### */
2800
2801function new_node(me, mom, text, link, children_data, api_level)
2802{
2803 var node = new Object();
2804 node.children = Array();
2805 node.children_data = children_data;
2806 node.depth = mom.depth + 1;
2807
2808 node.li = document.createElement("li");
2809 mom.get_children_ul().appendChild(node.li);
2810
2811 node.label_div = document.createElement("div");
2812 node.label_div.className = "label";
2813 if (api_level != null) {
2814 $(node.label_div).addClass("api");
2815 $(node.label_div).addClass("api-level-"+api_level);
2816 }
2817 node.li.appendChild(node.label_div);
2818
2819 if (children_data != null) {
2820 node.expand_toggle = document.createElement("a");
2821 node.expand_toggle.href = "javascript:void(0)";
2822 node.expand_toggle.onclick = function() {
2823 if (node.expanded) {
2824 $(node.get_children_ul()).slideUp("fast");
2825 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2826 node.expanded = false;
2827 } else {
2828 expand_node(me, node);
2829 }
2830 };
2831 node.label_div.appendChild(node.expand_toggle);
2832
2833 node.plus_img = document.createElement("img");
2834 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2835 node.plus_img.className = "plus";
2836 node.plus_img.width = "8";
2837 node.plus_img.border = "0";
2838 node.expand_toggle.appendChild(node.plus_img);
2839
2840 node.expanded = false;
2841 }
2842
2843 var a = document.createElement("a");
2844 node.label_div.appendChild(a);
2845 node.label = document.createTextNode(text);
2846 a.appendChild(node.label);
2847 if (link) {
2848 a.href = me.toroot + link;
2849 } else {
2850 if (children_data != null) {
2851 a.className = "nolink";
2852 a.href = "javascript:void(0)";
2853 a.onclick = node.expand_toggle.onclick;
2854 // This next line shouldn't be necessary. I'll buy a beer for the first
2855 // person who figures out how to remove this line and have the link
2856 // toggle shut on the first try. --joeo@android.com
2857 node.expanded = false;
2858 }
2859 }
Scott Main3b90aff2013-08-01 18:09:35 -07002860
Scott Mainf5089842012-08-14 16:31:07 -07002861
2862 node.children_ul = null;
2863 node.get_children_ul = function() {
2864 if (!node.children_ul) {
2865 node.children_ul = document.createElement("ul");
2866 node.children_ul.className = "children_ul";
2867 node.children_ul.style.display = "none";
2868 node.li.appendChild(node.children_ul);
2869 }
2870 return node.children_ul;
2871 };
2872
2873 return node;
2874}
2875
Robert Lyd2dd6e52012-11-29 21:28:48 -08002876
2877
2878
Scott Mainf5089842012-08-14 16:31:07 -07002879function expand_node(me, node)
2880{
2881 if (node.children_data && !node.expanded) {
2882 if (node.children_visited) {
2883 $(node.get_children_ul()).slideDown("fast");
2884 } else {
2885 get_node(me, node);
2886 if ($(node.label_div).hasClass("absent")) {
2887 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002888 }
Scott Mainf5089842012-08-14 16:31:07 -07002889 $(node.get_children_ul()).slideDown("fast");
2890 }
2891 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2892 node.expanded = true;
2893
2894 // perform api level toggling because new nodes are new to the DOM
2895 var selectedLevel = $("#apiLevelSelector option:selected").val();
2896 toggleVisisbleApis(selectedLevel, "#side-nav");
2897 }
2898}
2899
2900function get_node(me, mom)
2901{
2902 mom.children_visited = true;
2903 for (var i in mom.children_data) {
2904 var node_data = mom.children_data[i];
2905 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2906 node_data[2], node_data[3]);
2907 }
2908}
2909
2910function this_page_relative(toroot)
2911{
2912 var full = document.location.pathname;
2913 var file = "";
2914 if (toroot.substr(0, 1) == "/") {
2915 if (full.substr(0, toroot.length) == toroot) {
2916 return full.substr(toroot.length);
2917 } else {
2918 // the file isn't under toroot. Fail.
2919 return null;
2920 }
2921 } else {
2922 if (toroot != "./") {
2923 toroot = "./" + toroot;
2924 }
2925 do {
2926 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2927 var pos = full.lastIndexOf("/");
2928 file = full.substr(pos) + file;
2929 full = full.substr(0, pos);
2930 toroot = toroot.substr(0, toroot.length-3);
2931 }
2932 } while (toroot != "" && toroot != "/");
2933 return file.substr(1);
2934 }
2935}
2936
2937function find_page(url, data)
2938{
2939 var nodes = data;
2940 var result = null;
2941 for (var i in nodes) {
2942 var d = nodes[i];
2943 if (d[1] == url) {
2944 return new Array(i);
2945 }
2946 else if (d[2] != null) {
2947 result = find_page(url, d[2]);
2948 if (result != null) {
2949 return (new Array(i).concat(result));
2950 }
2951 }
2952 }
2953 return null;
2954}
2955
Scott Mainf5089842012-08-14 16:31:07 -07002956function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002957 // load json file for navtree data
2958 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2959 // when the file is loaded, initialize the tree
2960 if(jqxhr.status === 200) {
2961 init_navtree("tree-list", toroot, NAVTREE_DATA);
2962 }
2963 });
Scott Main3b90aff2013-08-01 18:09:35 -07002964
Scott Mainf5089842012-08-14 16:31:07 -07002965 // perform api level toggling because because the whole tree is new to the DOM
2966 var selectedLevel = $("#apiLevelSelector option:selected").val();
2967 toggleVisisbleApis(selectedLevel, "#side-nav");
2968}
2969
2970function init_navtree(navtree_id, toroot, root_nodes)
2971{
2972 var me = new Object();
2973 me.toroot = toroot;
2974 me.node = new Object();
2975
2976 me.node.li = document.getElementById(navtree_id);
2977 me.node.children_data = root_nodes;
2978 me.node.children = new Array();
2979 me.node.children_ul = document.createElement("ul");
2980 me.node.get_children_ul = function() { return me.node.children_ul; };
2981 //me.node.children_ul.className = "children_ul";
2982 me.node.li.appendChild(me.node.children_ul);
2983 me.node.depth = 0;
2984
2985 get_node(me, me.node);
2986
2987 me.this_page = this_page_relative(toroot);
2988 me.breadcrumbs = find_page(me.this_page, root_nodes);
2989 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2990 var mom = me.node;
2991 for (var i in me.breadcrumbs) {
2992 var j = me.breadcrumbs[i];
2993 mom = mom.children[j];
2994 expand_node(me, mom);
2995 }
2996 mom.label_div.className = mom.label_div.className + " selected";
2997 addLoadEvent(function() {
2998 scrollIntoView("nav-tree");
2999 });
3000 }
3001}
3002
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003003
3004
3005
3006
3007
3008
3009
Robert Lyd2dd6e52012-11-29 21:28:48 -08003010/* TODO: eliminate redundancy with non-google functions */
3011function init_google_navtree(navtree_id, toroot, root_nodes)
3012{
3013 var me = new Object();
3014 me.toroot = toroot;
3015 me.node = new Object();
3016
3017 me.node.li = document.getElementById(navtree_id);
3018 me.node.children_data = root_nodes;
3019 me.node.children = new Array();
3020 me.node.children_ul = document.createElement("ul");
3021 me.node.get_children_ul = function() { return me.node.children_ul; };
3022 //me.node.children_ul.className = "children_ul";
3023 me.node.li.appendChild(me.node.children_ul);
3024 me.node.depth = 0;
3025
3026 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003027}
3028
3029function new_google_node(me, mom, text, link, children_data, api_level)
3030{
3031 var node = new Object();
3032 var child;
3033 node.children = Array();
3034 node.children_data = children_data;
3035 node.depth = mom.depth + 1;
3036 node.get_children_ul = function() {
3037 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003038 node.children_ul = document.createElement("ul");
3039 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003040 node.li.appendChild(node.children_ul);
3041 }
3042 return node.children_ul;
3043 };
3044 node.li = document.createElement("li");
3045
3046 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003047
3048
Robert Lyd2dd6e52012-11-29 21:28:48 -08003049 if(link) {
3050 child = document.createElement("a");
3051
3052 }
3053 else {
3054 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003055 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003056
3057 }
3058 if (children_data != null) {
3059 node.li.className="nav-section";
3060 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003061 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003062 node.li.appendChild(node.label_div);
3063 get_google_node(me, node);
3064 node.label_div.appendChild(child);
3065 }
3066 else {
3067 node.li.appendChild(child);
3068 }
3069 if(link) {
3070 child.href = me.toroot + link;
3071 }
3072 node.label = document.createTextNode(text);
3073 child.appendChild(node.label);
3074
3075 node.children_ul = null;
3076
3077 return node;
3078}
3079
3080function get_google_node(me, mom)
3081{
3082 mom.children_visited = true;
3083 var linkText;
3084 for (var i in mom.children_data) {
3085 var node_data = mom.children_data[i];
3086 linkText = node_data[0];
3087
3088 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3089 linkText = linkText.substr(19, linkText.length);
3090 }
3091 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3092 node_data[2], node_data[3]);
3093 }
3094}
Scott Mainad08f072013-08-20 16:49:57 -07003095
3096
3097
3098
3099
3100
3101/****** NEW version of script to build google and sample navs dynamically ******/
3102// TODO: update Google reference docs to tolerate this new implementation
3103
Scott Maine624b3f2013-09-12 12:56:41 -07003104var NODE_NAME = 0;
3105var NODE_HREF = 1;
3106var NODE_GROUP = 2;
3107var NODE_TAGS = 3;
3108var NODE_CHILDREN = 4;
3109
Scott Mainad08f072013-08-20 16:49:57 -07003110function init_google_navtree2(navtree_id, data)
3111{
3112 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003113 for (var i in data) {
3114 var node_data = data[i];
3115 $containerUl.append(new_google_node2(node_data));
3116 }
3117
Scott Main70557ee2013-10-30 14:47:40 -07003118 // Make all third-generation list items 'sticky' to prevent them from collapsing
3119 $containerUl.find('li li li.nav-section').addClass('sticky');
3120
Scott Mainad08f072013-08-20 16:49:57 -07003121 initExpandableNavItems("#"+navtree_id);
3122}
3123
3124function new_google_node2(node_data)
3125{
Scott Maine624b3f2013-09-12 12:56:41 -07003126 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003127 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3128 linkText = linkText.substr(19, linkText.length);
3129 }
3130 var $li = $('<li>');
3131 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003132 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003133 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3134 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003135 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003136 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3137 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003138 }
3139 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003140 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003141 $li.addClass("nav-section");
3142 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003143 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003144
Scott Maine624b3f2013-09-12 12:56:41 -07003145 for (var i in node_data[NODE_CHILDREN]) {
3146 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003147 $childUl.append(new_google_node2(child_node_data));
3148 }
3149 $li.append($childUl);
3150 }
3151 $li.prepend($a);
3152
3153 return $li;
3154}
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
Robert Lyd2dd6e52012-11-29 21:28:48 -08003166function showGoogleRefTree() {
3167 init_default_google_navtree(toRoot);
3168 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003169}
3170
3171function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003172 // load json file for navtree data
3173 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3174 // when the file is loaded, initialize the tree
3175 if(jqxhr.status === 200) {
3176 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3177 highlightSidenav();
3178 resizeNav();
3179 }
3180 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003181}
3182
3183function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003184 // load json file for navtree data
3185 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3186 // when the file is loaded, initialize the tree
3187 if(jqxhr.status === 200) {
3188 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3189 highlightSidenav();
3190 resizeNav();
3191 }
3192 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003193}
3194
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003195function showSamplesRefTree() {
3196 init_default_samples_navtree(toRoot);
3197}
3198
3199function init_default_samples_navtree(toroot) {
3200 // load json file for navtree data
3201 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3202 // when the file is loaded, initialize the tree
3203 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003204 // hack to remove the "about the samples" link then put it back in
3205 // after we nuke the list to remove the dummy static list of samples
3206 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3207 $("#nav.samples-nav").empty();
3208 $("#nav.samples-nav").append($firstLi);
3209
Scott Mainad08f072013-08-20 16:49:57 -07003210 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003211 highlightSidenav();
3212 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003213 if ($("#jd-content #samples").length) {
3214 showSamples();
3215 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003216 }
3217 });
3218}
3219
Scott Mainf5089842012-08-14 16:31:07 -07003220/* TOGGLE INHERITED MEMBERS */
3221
3222/* Toggle an inherited class (arrow toggle)
3223 * @param linkObj The link that was clicked.
3224 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3225 * 'null' to simply toggle.
3226 */
3227function toggleInherited(linkObj, expand) {
3228 var base = linkObj.getAttribute("id");
3229 var list = document.getElementById(base + "-list");
3230 var summary = document.getElementById(base + "-summary");
3231 var trigger = document.getElementById(base + "-trigger");
3232 var a = $(linkObj);
3233 if ( (expand == null && a.hasClass("closed")) || expand ) {
3234 list.style.display = "none";
3235 summary.style.display = "block";
3236 trigger.src = toRoot + "assets/images/triangle-opened.png";
3237 a.removeClass("closed");
3238 a.addClass("opened");
3239 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3240 list.style.display = "block";
3241 summary.style.display = "none";
3242 trigger.src = toRoot + "assets/images/triangle-closed.png";
3243 a.removeClass("opened");
3244 a.addClass("closed");
3245 }
3246 return false;
3247}
3248
3249/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3250 * @param linkObj The link that was clicked.
3251 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3252 * 'null' to simply toggle.
3253 */
3254function toggleAllInherited(linkObj, expand) {
3255 var a = $(linkObj);
3256 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3257 var expandos = $(".jd-expando-trigger", table);
3258 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3259 expandos.each(function(i) {
3260 toggleInherited(this, true);
3261 });
3262 a.text("[Collapse]");
3263 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3264 expandos.each(function(i) {
3265 toggleInherited(this, false);
3266 });
3267 a.text("[Expand]");
3268 }
3269 return false;
3270}
3271
3272/* Toggle all inherited members in the class (link in the class title)
3273 */
3274function toggleAllClassInherited() {
3275 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3276 var toggles = $(".toggle-all", $("#body-content"));
3277 if (a.text() == "[Expand All]") {
3278 toggles.each(function(i) {
3279 toggleAllInherited(this, true);
3280 });
3281 a.text("[Collapse All]");
3282 } else {
3283 toggles.each(function(i) {
3284 toggleAllInherited(this, false);
3285 });
3286 a.text("[Expand All]");
3287 }
3288 return false;
3289}
3290
3291/* Expand all inherited members in the class. Used when initiating page search */
3292function ensureAllInheritedExpanded() {
3293 var toggles = $(".toggle-all", $("#body-content"));
3294 toggles.each(function(i) {
3295 toggleAllInherited(this, true);
3296 });
3297 $("#toggleAllClassInherited").text("[Collapse All]");
3298}
3299
3300
3301/* HANDLE KEY EVENTS
3302 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3303 */
3304var agent = navigator['userAgent'].toLowerCase();
3305var mac = agent.indexOf("macintosh") != -1;
3306
3307$(document).keydown( function(e) {
3308var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3309 if (control && e.which == 70) { // 70 is "F"
3310 ensureAllInheritedExpanded();
3311 }
3312});
Scott Main498d7102013-08-21 15:47:38 -07003313
3314
3315
3316
3317
3318
3319/* On-demand functions */
3320
3321/** Move sample code line numbers out of PRE block and into non-copyable column */
3322function initCodeLineNumbers() {
3323 var numbers = $("#codesample-block a.number");
3324 if (numbers.length) {
3325 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3326 }
3327
3328 $(document).ready(function() {
3329 // select entire line when clicked
3330 $("span.code-line").click(function() {
3331 if (!shifted) {
3332 selectText(this);
3333 }
3334 });
3335 // invoke line link on double click
3336 $(".code-line").dblclick(function() {
3337 document.location.hash = $(this).attr('id');
3338 });
3339 // highlight the line when hovering on the number
3340 $("#codesample-line-numbers a.number").mouseover(function() {
3341 var id = $(this).attr('href');
3342 $(id).css('background','#e7e7e7');
3343 });
3344 $("#codesample-line-numbers a.number").mouseout(function() {
3345 var id = $(this).attr('href');
3346 $(id).css('background','none');
3347 });
3348 });
3349}
3350
3351// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3352var shifted = false;
3353$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3354
3355// courtesy of jasonedelman.com
3356function selectText(element) {
3357 var doc = document
3358 , range, selection
3359 ;
3360 if (doc.body.createTextRange) { //ms
3361 range = doc.body.createTextRange();
3362 range.moveToElementText(element);
3363 range.select();
3364 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003365 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003366 range = doc.createRange();
3367 range.selectNodeContents(element);
3368 selection.removeAllRanges();
3369 selection.addRange(range);
3370 }
Scott Main285f0772013-08-22 23:22:09 +00003371}
Scott Main03aca9a2013-10-31 07:20:55 -07003372
3373
3374
3375
3376/** Display links and other information about samples that match the
3377 group specified by the URL */
3378function showSamples() {
3379 var group = $("#samples").attr('class');
3380 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3381
3382 var $ul = $("<ul>");
3383 $selectedLi = $("#nav li.selected");
3384
3385 $selectedLi.children("ul").children("li").each(function() {
3386 var $li = $("<li>").append($(this).find("a").first().clone());
3387 $ul.append($li);
3388 });
3389
3390 $("#samples").append($ul);
3391
3392}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003393
3394
3395
3396/* ########################################################## */
3397/* ################### RESOURCE CARDS ##################### */
3398/* ########################################################## */
3399
3400/** Handle resource queries, collections, and grids (sections). Requires
3401 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3402
3403(function() {
3404 // Prevent the same resource from being loaded more than once per page.
3405 var addedPageResources = {};
3406
3407 $(document).ready(function() {
3408 $('.resource-widget').each(function() {
3409 initResourceWidget(this);
3410 });
3411
3412 /* Pass the line height to ellipsisfade() to adjust the height of the
3413 text container to show the max number of lines possible, without
3414 showing lines that are cut off. This works with the css ellipsis
3415 classes to fade last text line and apply an ellipsis char. */
3416
Scott Mainb16376f2014-05-21 20:35:47 -07003417 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003418 var lineHeight = 15;
3419 $('.card-info .text').ellipsisfade(lineHeight);
3420 });
3421
3422 /*
3423 Three types of resource layouts:
3424 Flow - Uses a fixed row-height flow using float left style.
3425 Carousel - Single card slideshow all same dimension absolute.
3426 Stack - Uses fixed columns and flexible element height.
3427 */
3428 function initResourceWidget(widget) {
3429 var $widget = $(widget);
3430 var isFlow = $widget.hasClass('resource-flow-layout'),
3431 isCarousel = $widget.hasClass('resource-carousel-layout'),
3432 isStack = $widget.hasClass('resource-stack-layout');
3433
3434 // find size of widget by pulling out its class name
3435 var sizeCols = 1;
3436 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3437 if (m) {
3438 sizeCols = parseInt(m[1], 10);
3439 }
3440
3441 var opts = {
3442 cardSizes: ($widget.data('cardsizes') || '').split(','),
3443 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3444 itemsPerPage: $widget.data('itemsperpage'),
3445 sortOrder: $widget.data('sortorder'),
3446 query: $widget.data('query'),
3447 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003448 sizeCols: sizeCols,
3449 /* Added by LFL 6/6/14 */
3450 resourceStyle: $widget.data('resourcestyle') || 'card',
3451 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003452 };
3453
3454 // run the search for the set of resources to show
3455
3456 var resources = buildResourceList(opts);
3457
3458 if (isFlow) {
3459 drawResourcesFlowWidget($widget, opts, resources);
3460 } else if (isCarousel) {
3461 drawResourcesCarouselWidget($widget, opts, resources);
3462 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003463 /* Looks like this got removed and is not used, so repurposing for the
3464 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003465 Modified by LFL 6/6/14
3466 */
3467 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003468 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003469 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003470 }
3471 }
3472
3473 /* Initializes a Resource Carousel Widget */
3474 function drawResourcesCarouselWidget($widget, opts, resources) {
3475 $widget.empty();
3476 var plusone = true; //always show plusone on carousel
3477
3478 $widget.addClass('resource-card slideshow-container')
3479 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3480 .append($('<a>').addClass('slideshow-next').text('Next'));
3481
3482 var css = { 'width': $widget.width() + 'px',
3483 'height': $widget.height() + 'px' };
3484
3485 var $ul = $('<ul>');
3486
3487 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003488 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003489 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003490 .decorateResourceCard(resources[i],plusone);
3491
3492 $('<li>').css(css)
3493 .append($card)
3494 .appendTo($ul);
3495 }
3496
3497 $('<div>').addClass('frame')
3498 .append($ul)
3499 .appendTo($widget);
3500
3501 $widget.dacSlideshow({
3502 auto: true,
3503 btnPrev: '.slideshow-prev',
3504 btnNext: '.slideshow-next'
3505 });
3506 };
3507
Robert Lye7eeb402014-06-03 19:35:24 -07003508 /* Initializes a Resource Card Stack Widget (column-based layout)
3509 Modified by LFL 6/6/14
3510 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003511 function drawResourcesStackWidget($widget, opts, resources, sections) {
3512 // Don't empty widget, grab all items inside since they will be the first
3513 // items stacked, followed by the resource query
3514 var plusone = true; //by default show plusone on section cards
3515 var cards = $widget.find('.resource-card').detach().toArray();
3516 var numStacks = opts.numStacks || 1;
3517 var $stacks = [];
3518 var urlString;
3519
3520 for (var i = 0; i < numStacks; ++i) {
3521 $stacks[i] = $('<div>').addClass('resource-card-stack')
3522 .appendTo($widget);
3523 }
3524
3525 var sectionResources = [];
3526
3527 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003528 if (sections) {
3529 for (var i = 0; i < sections.length; ++i) {
3530 if (!sections[i].sections || !sections[i].sections.length) {
3531 // Render it as a resource card
3532 sectionResources.push(
3533 $('<a>')
3534 .addClass('resource-card section-card')
3535 .attr('href', cleanUrl(sections[i].resource.url))
3536 .decorateResourceCard(sections[i].resource,plusone)[0]
3537 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003538
Robert Lye7eeb402014-06-03 19:35:24 -07003539 } else {
3540 cards.push(
3541 $('<div>')
3542 .addClass('resource-card section-card-menu')
3543 .decorateResourceSection(sections[i],plusone)[0]
3544 );
3545 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003546 }
3547 }
3548
3549 cards = cards.concat(sectionResources);
3550
3551 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003552 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003553
Robert Lye7eeb402014-06-03 19:35:24 -07003554 if (opts.resourceStyle.indexOf('related') > -1) {
3555 $card.addClass('related-card');
3556 }
smain@google.com95948b82014-06-16 19:24:25 -07003557
Dirk Doughertyc3921652014-05-13 16:55:26 -07003558 cards.push($card[0]);
3559 }
3560
Robert Lye7eeb402014-06-03 19:35:24 -07003561 if (opts.stackSort != 'false') {
3562 for (var i = 0; i < cards.length; ++i) {
3563 // Find the stack with the shortest height, but give preference to
3564 // left to right order.
3565 var minHeight = $stacks[0].height();
3566 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003567
Robert Lye7eeb402014-06-03 19:35:24 -07003568 for (var j = 1; j < numStacks; ++j) {
3569 var height = $stacks[j].height();
3570 if (height < minHeight - 45) {
3571 minHeight = height;
3572 minIndex = j;
3573 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003574 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003575
Robert Lye7eeb402014-06-03 19:35:24 -07003576 $stacks[minIndex].append($(cards[i]));
3577 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003578 }
3579
3580 };
smain@google.com95948b82014-06-16 19:24:25 -07003581
3582 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003583 Create a resource card using the given resource object and a list of html
3584 configured options. Returns a jquery object containing the element.
3585 */
smain@google.com95948b82014-06-16 19:24:25 -07003586 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003587 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003588
Robert Lye7eeb402014-06-03 19:35:24 -07003589 // The difference here is that generic cards are not entirely clickable
3590 // so its a div instead of an a tag, also the generic one is not given
3591 // the resource-card class so it appears with a transparent background
3592 // and can be styled in whatever way the css setup.
3593 if (opts.resourceStyle == 'generic') {
3594 $el = $('<div>')
3595 .addClass('resource')
3596 .attr('href', cleanUrl(resource.url))
3597 .decorateResource(resource, opts);
3598 } else {
3599 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003600
Robert Lye7eeb402014-06-03 19:35:24 -07003601 $el = $('<a>')
3602 .addClass(cls)
3603 .attr('href', cleanUrl(resource.url))
3604 .decorateResourceCard(resource, plusone);
3605 }
smain@google.com95948b82014-06-16 19:24:25 -07003606
Robert Lye7eeb402014-06-03 19:35:24 -07003607 return $el;
3608 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003609
3610 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3611 function drawResourcesFlowWidget($widget, opts, resources) {
3612 $widget.empty();
3613 var cardSizes = opts.cardSizes || ['6x6'];
3614 var i = 0, j = 0;
3615 var plusone = true; // by default show plusone on resource cards
3616
3617 while (i < resources.length) {
3618 var cardSize = cardSizes[j++ % cardSizes.length];
3619 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003620 // Some card sizes do not get a plusone button, such as where space is constrained
3621 // or for cards commonly embedded in docs (to improve overall page speed).
3622 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3623 (cardSize == "9x2") || (cardSize == "9x3") ||
3624 (cardSize == "12x2") || (cardSize == "12x3"));
3625
3626 // A stack has a third dimension which is the number of stacked items
3627 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3628 var stackCount = 0;
3629 var $stackDiv = null;
3630
3631 if (isStack) {
3632 // Create a stack container which should have the dimensions defined
3633 // by the product of the items inside.
3634 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3635 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3636 }
3637
3638 // Build each stack item or just a single item
3639 do {
3640 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003641
Robert Lye7eeb402014-06-03 19:35:24 -07003642 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003643
3644 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003645 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003646
Dirk Doughertyc3921652014-05-13 16:55:26 -07003647 if (isStack) {
3648 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3649 if (++stackCount == parseInt(isStack[3])) {
3650 $card.addClass('resource-card-row-stack-last');
3651 stackCount = 0;
3652 }
3653 } else {
3654 stackCount = 0;
3655 }
3656
Robert Lye7eeb402014-06-03 19:35:24 -07003657 $card.appendTo($stackDiv || $widget);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003658
3659 } while (++i < resources.length && stackCount > 0);
3660 }
3661 }
3662
3663 /* Build a site map of resources using a section as a root. */
3664 function buildSectionList(opts) {
3665 if (opts.section && SECTION_BY_ID[opts.section]) {
3666 return SECTION_BY_ID[opts.section].sections || [];
3667 }
3668 return [];
3669 }
3670
3671 function buildResourceList(opts) {
3672 var maxResults = opts.maxResults || 100;
3673
3674 var query = opts.query || '';
3675 var expressions = parseResourceQuery(query);
3676 var addedResourceIndices = {};
3677 var results = [];
3678
3679 for (var i = 0; i < expressions.length; i++) {
3680 var clauses = expressions[i];
3681
3682 // build initial set of resources from first clause
3683 var firstClause = clauses[0];
3684 var resources = [];
3685 switch (firstClause.attr) {
3686 case 'type':
3687 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3688 break;
3689 case 'lang':
3690 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3691 break;
3692 case 'tag':
3693 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3694 break;
3695 case 'collection':
3696 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3697 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3698 break;
3699 case 'section':
3700 var urls = SITE_MAP[firstClause.value].sections || [];
3701 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3702 break;
3703 }
3704 // console.log(firstClause.attr + ':' + firstClause.value);
3705 resources = resources || [];
3706
3707 // use additional clauses to filter corpus
3708 if (clauses.length > 1) {
3709 var otherClauses = clauses.slice(1);
3710 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3711 }
3712
3713 // filter out resources already added
3714 if (i > 1) {
3715 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3716 }
3717
3718 // add to list of already added indices
3719 for (var j = 0; j < resources.length; j++) {
3720 // console.log(resources[j].title);
3721 addedResourceIndices[resources[j].index] = 1;
3722 }
3723
3724 // concat to final results list
3725 results = results.concat(resources);
3726 }
3727
3728 if (opts.sortOrder && results.length) {
3729 var attr = opts.sortOrder;
3730
3731 if (opts.sortOrder == 'random') {
3732 var i = results.length, j, temp;
3733 while (--i) {
3734 j = Math.floor(Math.random() * (i + 1));
3735 temp = results[i];
3736 results[i] = results[j];
3737 results[j] = temp;
3738 }
3739 } else {
3740 var desc = attr.charAt(0) == '-';
3741 if (desc) {
3742 attr = attr.substring(1);
3743 }
3744 results = results.sort(function(x,y) {
3745 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3746 });
3747 }
3748 }
3749
3750 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3751 results = results.slice(0, maxResults);
3752
3753 for (var j = 0; j < results.length; ++j) {
3754 addedPageResources[results[j].index] = 1;
3755 }
3756
3757 return results;
3758 }
3759
3760
3761 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3762 return function(resource) {
3763 return !addedResourceIndices[resource.index];
3764 };
3765 }
3766
3767
3768 function getResourceMatchesClausesFilter(clauses) {
3769 return function(resource) {
3770 return doesResourceMatchClauses(resource, clauses);
3771 };
3772 }
3773
3774
3775 function doesResourceMatchClauses(resource, clauses) {
3776 for (var i = 0; i < clauses.length; i++) {
3777 var map;
3778 switch (clauses[i].attr) {
3779 case 'type':
3780 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3781 break;
3782 case 'lang':
3783 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3784 break;
3785 case 'tag':
3786 map = IS_RESOURCE_TAGGED[clauses[i].value];
3787 break;
3788 }
3789
3790 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3791 return clauses[i].negative;
3792 }
3793 }
3794 return true;
3795 }
smain@google.com95948b82014-06-16 19:24:25 -07003796
Robert Lye7eeb402014-06-03 19:35:24 -07003797 function cleanUrl(url)
3798 {
3799 if (url && url.indexOf('//') === -1) {
3800 url = toRoot + url;
3801 }
smain@google.com95948b82014-06-16 19:24:25 -07003802
Robert Lye7eeb402014-06-03 19:35:24 -07003803 return url;
3804 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003805
3806
3807 function parseResourceQuery(query) {
3808 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3809 var expressions = [];
3810 var expressionStrs = query.split(',') || [];
3811 for (var i = 0; i < expressionStrs.length; i++) {
3812 var expr = expressionStrs[i] || '';
3813
3814 // Break expression into clauses (clause e.g. 'tag:foo')
3815 var clauses = [];
3816 var clauseStrs = expr.split(/(?=[\+\-])/);
3817 for (var j = 0; j < clauseStrs.length; j++) {
3818 var clauseStr = clauseStrs[j] || '';
3819
3820 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3821 var parts = clauseStr.split(':');
3822 var clause = {};
3823
3824 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3825 if (clause.attr) {
3826 if (clause.attr.charAt(0) == '+') {
3827 clause.attr = clause.attr.substring(1);
3828 } else if (clause.attr.charAt(0) == '-') {
3829 clause.negative = true;
3830 clause.attr = clause.attr.substring(1);
3831 }
3832 }
3833
3834 if (parts.length > 1) {
3835 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3836 }
3837
3838 clauses.push(clause);
3839 }
3840
3841 if (!clauses.length) {
3842 continue;
3843 }
3844
3845 expressions.push(clauses);
3846 }
3847
3848 return expressions;
3849 }
3850})();
3851
3852(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003853
smain@google.com95948b82014-06-16 19:24:25 -07003854 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003855 Utility method for creating dom for the description area of a card.
3856 Used in decorateResourceCard and decorateResource.
3857 */
3858 function buildResourceCardDescription(resource, plusone) {
3859 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07003860
Robert Lye7eeb402014-06-03 19:35:24 -07003861 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07003862
Robert Lye7eeb402014-06-03 19:35:24 -07003863 if (resource.cta) {
3864 $description.append($('<a>').addClass('cta').html(resource.cta));
3865 }
smain@google.com95948b82014-06-16 19:24:25 -07003866
Robert Lye7eeb402014-06-03 19:35:24 -07003867 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07003868 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07003869 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003870
Robert Lye7eeb402014-06-03 19:35:24 -07003871 $description.append($('<div>').addClass('util')
3872 .append($('<div>').addClass('g-plusone')
3873 .attr('data-size', 'small')
3874 .attr('data-align', 'right')
3875 .attr('data-href', plusurl)));
3876 }
smain@google.com95948b82014-06-16 19:24:25 -07003877
Robert Lye7eeb402014-06-03 19:35:24 -07003878 return $description;
3879 }
smain@google.com95948b82014-06-16 19:24:25 -07003880
3881
Dirk Doughertyc3921652014-05-13 16:55:26 -07003882 /* Simple jquery function to create dom for a standard resource card */
3883 $.fn.decorateResourceCard = function(resource,plusone) {
3884 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07003885 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003886 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07003887
Robert Lye7eeb402014-06-03 19:35:24 -07003888 if (imgUrl.indexOf('//') === -1) {
3889 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003890 }
Robert Lye7eeb402014-06-03 19:35:24 -07003891
3892 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07003893 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07003894 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07003895 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07003896
Robert Lye7eeb402014-06-03 19:35:24 -07003897 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3898 .append($('<div>').addClass('section').text(section))
3899 .append($('<div>').addClass('title').html(resource.title))
3900 .append(buildResourceCardDescription(resource, plusone))
3901 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003902
3903 return this;
3904 };
3905
3906 /* Simple jquery function to create dom for a resource section card (menu) */
3907 $.fn.decorateResourceSection = function(section,plusone) {
3908 var resource = section.resource;
3909 //keep url clean for matching and offline mode handling
3910 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3911 var $base = $('<a>')
3912 .addClass('card-bg')
3913 .attr('href', resource.url)
3914 .append($('<div>').addClass('card-section-icon')
3915 .append($('<div>').addClass('icon'))
3916 .append($('<div>').addClass('section').html(resource.title)))
3917 .appendTo(this);
3918
3919 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3920
3921 if (section.sections && section.sections.length) {
3922 // Recurse the section sub-tree to find a resource image.
3923 var stack = [section];
3924
3925 while (stack.length) {
3926 if (stack[0].resource.image) {
3927 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3928 break;
3929 }
3930
3931 if (stack[0].sections) {
3932 stack = stack.concat(stack[0].sections);
3933 }
3934
3935 stack.shift();
3936 }
3937
3938 var $ul = $('<ul>')
3939 .appendTo($cardInfo);
3940
3941 var max = section.sections.length > 3 ? 3 : section.sections.length;
3942
3943 for (var i = 0; i < max; ++i) {
3944
3945 var subResource = section.sections[i];
3946 if (!plusone) {
3947 $('<li>')
3948 .append($('<a>').attr('href', subResource.url)
3949 .append($('<div>').addClass('title').html(subResource.title))
3950 .append($('<div>').addClass('description ellipsis')
3951 .append($('<div>').addClass('text').html(subResource.summary))
3952 .append($('<div>').addClass('util'))))
3953 .appendTo($ul);
3954 } else {
3955 $('<li>')
3956 .append($('<a>').attr('href', subResource.url)
3957 .append($('<div>').addClass('title').html(subResource.title))
3958 .append($('<div>').addClass('description ellipsis')
3959 .append($('<div>').addClass('text').html(subResource.summary))
3960 .append($('<div>').addClass('util')
3961 .append($('<div>').addClass('g-plusone')
3962 .attr('data-size', 'small')
3963 .attr('data-align', 'right')
3964 .attr('data-href', resource.url)))))
3965 .appendTo($ul);
3966 }
3967 }
3968
3969 // Add a more row
3970 if (max < section.sections.length) {
3971 $('<li>')
3972 .append($('<a>').attr('href', resource.url)
3973 .append($('<div>')
3974 .addClass('title')
3975 .text('More')))
3976 .appendTo($ul);
3977 }
3978 } else {
3979 // No sub-resources, just render description?
3980 }
3981
3982 return this;
3983 };
smain@google.com95948b82014-06-16 19:24:25 -07003984
3985
3986
3987
Robert Lye7eeb402014-06-03 19:35:24 -07003988 /* Render other types of resource styles that are not cards. */
3989 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07003990 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003991 'assets/images/resource-card-default-android.jpg';
3992 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003993
Robert Lye7eeb402014-06-03 19:35:24 -07003994 if (imgUrl.indexOf('//') === -1) {
3995 imgUrl = toRoot + imgUrl;
3996 }
smain@google.com95948b82014-06-16 19:24:25 -07003997
Robert Lye7eeb402014-06-03 19:35:24 -07003998 if (linkUrl && linkUrl.indexOf('//') === -1) {
3999 linkUrl = toRoot + linkUrl;
4000 }
4001
4002 $(this).append(
4003 $('<div>').addClass('image')
4004 .css('background-image', 'url(' + imgUrl + ')'),
4005 $('<div>').addClass('info').append(
4006 $('<h4>').addClass('title').html(resource.title),
4007 $('<p>').addClass('summary').html(resource.summary),
4008 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4009 )
4010 );
4011
4012 return this;
4013 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004014})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004015
4016
Dirk Doughertyc3921652014-05-13 16:55:26 -07004017/* Calculate the vertical area remaining */
4018(function($) {
4019 $.fn.ellipsisfade= function(lineHeight) {
4020 this.each(function() {
4021 // get element text
4022 var $this = $(this);
4023 var remainingHeight = $this.parent().parent().height();
4024 $this.parent().siblings().each(function ()
smain@google.comc91ecb72014-06-23 10:22:23 -07004025 {
smain@google.comcda1a9a2014-06-19 17:07:46 -07004026 if ($(this).is(":visible")) {
4027 var h = $(this).height();
4028 remainingHeight = remainingHeight - h;
4029 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07004030 });
4031
4032 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4033 $this.parent().css({'height': adjustedRemainingHeight});
4034 $this.css({'height': "auto"});
4035 });
4036
4037 return this;
4038 };
4039}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004040
4041/*
4042 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004043
Robert Lye7eeb402014-06-03 19:35:24 -07004044 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004045 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004046 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004047
Robert Lye7eeb402014-06-03 19:35:24 -07004048 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004049
Robert Lye7eeb402014-06-03 19:35:24 -07004050 <div class="fullscreen-carousel">
4051 <div class="fullscreen-carousel-content">
4052 <!-- content here -->
4053 </div>
4054 <div class="fullscreen-carousel-content">
4055 <!-- content here -->
4056 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004057
Robert Lye7eeb402014-06-03 19:35:24 -07004058 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004059
Robert Lye7eeb402014-06-03 19:35:24 -07004060 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004061
Robert Lye7eeb402014-06-03 19:35:24 -07004062 Control over how the carousel takes over the screen can mostly be defined in
4063 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004064 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004065 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004066 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004067 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004068
Robert Lye7eeb402014-06-03 19:35:24 -07004069 There is limited functionality for having multiple sections since that request
4070 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4071 scroll between multiple content areas.
4072*/
4073
4074(function() {
4075 $(document).ready(function() {
4076 $('.fullscreen-carousel').each(function() {
4077 initWidget(this);
4078 });
4079 });
4080
4081 function initWidget(widget) {
4082 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004083
Robert Lye7eeb402014-06-03 19:35:24 -07004084 var topOffset = $widget.offset().top;
4085 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4086 var maxHeight = 0;
4087 var minHeight = 0;
4088 var $content = $widget.find('.fullscreen-carousel-content');
4089 var $nextArrow = $widget.find('.next-arrow');
4090 var $prevArrow = $widget.find('.prev-arrow');
4091 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004092
Robert Lye7eeb402014-06-03 19:35:24 -07004093 if ($content.length <= 1) {
4094 $nextArrow.hide();
4095 $prevArrow.hide();
4096 } else {
4097 $nextArrow.click(function() {
4098 var index = ($content.index($curSection) + 1);
4099 $curSection.hide();
4100 $curSection = $($content[index >= $content.length ? 0 : index]);
4101 $curSection.show();
4102 });
smain@google.com95948b82014-06-16 19:24:25 -07004103
Robert Lye7eeb402014-06-03 19:35:24 -07004104 $prevArrow.click(function() {
4105 var index = ($content.index($curSection) - 1);
4106 $curSection.hide();
4107 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4108 $curSection.show();
4109 });
4110 }
4111
4112 // Just hide all content sections except first.
4113 $content.each(function(index) {
4114 if ($(this).height() > minHeight) minHeight = $(this).height();
4115 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4116 });
4117
4118 // Register for changes to window size, and trigger.
4119 $(window).resize(resizeWidget);
4120 resizeWidget();
4121
4122 function resizeWidget() {
4123 var height = $(window).height() - topOffset - padBottom;
4124 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004125 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004126 (maxHeight && height > maxHeight ? maxHeight : height));
4127 }
smain@google.com95948b82014-06-16 19:24:25 -07004128 }
Robert Lye7eeb402014-06-03 19:35:24 -07004129})();
4130
4131
4132
4133
4134
4135/*
4136 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004137
Robert Lye7eeb402014-06-03 19:35:24 -07004138 The following allows tab widgets to be installed via the html below. Each
4139 tab content section should have a data-tab attribute matching one of the
4140 nav items'. Also each tab content section should have a width matching the
4141 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004142
Robert Lye7eeb402014-06-03 19:35:24 -07004143 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004144
Robert Lye7eeb402014-06-03 19:35:24 -07004145 <div class="tab-carousel">
4146 <ul class="tab-nav">
4147 <li><a href="#" data-tab="handsets">Handsets</a>
4148 <li><a href="#" data-tab="wearable">Wearable</a>
4149 <li><a href="#" data-tab="tv">TV</a>
4150 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004151
Robert Lye7eeb402014-06-03 19:35:24 -07004152 <div class="tab-carousel-content">
4153 <div data-tab="handsets">
4154 <!--Full width content here-->
4155 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004156
Robert Lye7eeb402014-06-03 19:35:24 -07004157 <div data-tab="wearable">
4158 <!--Full width content here-->
4159 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004160
Robert Lye7eeb402014-06-03 19:35:24 -07004161 <div data-tab="tv">
4162 <!--Full width content here-->
4163 </div>
4164 </div>
4165 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004166
Robert Lye7eeb402014-06-03 19:35:24 -07004167*/
4168(function() {
4169 $(document).ready(function() {
4170 $('.tab-carousel').each(function() {
4171 initWidget(this);
4172 });
4173 });
4174
4175 function initWidget(widget) {
4176 var $widget = $(widget);
4177 var $nav = $widget.find('.tab-nav');
4178 var $anchors = $nav.find('[data-tab]');
4179 var $li = $nav.find('li');
4180 var $contentContainer = $widget.find('.tab-carousel-content');
4181 var $tabs = $contentContainer.find('[data-tab]');
4182 var $curTab = $($tabs[0]); // Current tab is first tab.
4183 var width = $widget.width();
4184
4185 // Setup nav interactivity.
4186 $anchors.click(function(evt) {
4187 evt.preventDefault();
4188 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004189 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004190 });
smain@google.com95948b82014-06-16 19:24:25 -07004191
Robert Lye7eeb402014-06-03 19:35:24 -07004192 // Add highlight for navigation on first item.
4193 var $highlight = $('<div>').addClass('highlight')
4194 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4195 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004196
Robert Lye7eeb402014-06-03 19:35:24 -07004197 // Store height since we will change contents to absolute.
4198 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004199
Robert Lye7eeb402014-06-03 19:35:24 -07004200 // Absolutely position tabs so they're ready for transition.
4201 $tabs.each(function(index) {
4202 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4203 });
smain@google.com95948b82014-06-16 19:24:25 -07004204
Robert Lye7eeb402014-06-03 19:35:24 -07004205 function transitionWidget($toTab) {
4206 if (!$curTab.is($toTab)) {
4207 var curIndex = $tabs.index($curTab[0]);
4208 var toIndex = $tabs.index($toTab[0]);
4209 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004210
Robert Lye7eeb402014-06-03 19:35:24 -07004211 // Animate content sections.
4212 $toTab.css({left:(width * dir) + 'px'});
4213 $curTab.animate({left:(width * -dir) + 'px'});
4214 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004215
Robert Lye7eeb402014-06-03 19:35:24 -07004216 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004217 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004218 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004219
Robert Lye7eeb402014-06-03 19:35:24 -07004220 // Store new current section.
4221 $curTab = $toTab;
4222 }
4223 }
smain@google.com95948b82014-06-16 19:24:25 -07004224 }
Robert Lye7eeb402014-06-03 19:35:24 -07004225})();