blob: 60a2596a34acfb081df7f804374c8458d6a12ffd [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() {
smain@google.com698fff02014-11-20 20:39:33 -080023
Dirk Doughertyb87e3002014-11-18 19:34:34 -080024 // show lang dialog if the URL includes /intl/
25 //if (location.pathname.substring(0,6) == "/intl/") {
26 // var lang = location.pathname.split('/')[2];
27 // if (lang != getLangPref()) {
28 // $("#langMessage a.yes").attr("onclick","changeLangPref('" + lang
29 // + "', true); $('#langMessage').hide(); return false;");
30 // $("#langMessage .lang." + lang).show();
31 // $("#langMessage").show();
32 // }
33 //}
Scott Main7e447ed2013-02-19 17:22:37 -080034
Scott Main0e76e7e2013-03-12 10:24:07 -070035 // load json file for JD doc search suggestions
Scott Main719acb42013-12-05 16:05:09 -080036 $.getScript(toRoot + 'jd_lists_unified.js');
Scott Main7e447ed2013-02-19 17:22:37 -080037 // load json file for Android API search suggestions
38 $.getScript(toRoot + 'reference/lists.js');
39 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080040 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080041 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
42 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080043 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080044 if(jqxhr.status === 200) {
45 // combine GCM and GMS data
46 GOOGLE_DATA = GMS_DATA;
47 var start = GOOGLE_DATA.length;
48 for (var i=0; i<GCM_DATA.length; i++) {
49 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
50 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
51 }
52 }
53 });
54 }
55 });
56
Scott Main0e76e7e2013-03-12 10:24:07 -070057 // setup keyboard listener for search shortcut
58 $('body').keyup(function(event) {
59 if (event.which == 191) {
60 $('#search_autocomplete').focus();
61 }
62 });
Scott Main015d6162013-01-29 09:01:52 -080063
Scott Maine4d8f1b2012-06-21 18:03:05 -070064 // init the fullscreen toggle click event
65 $('#nav-swap .fullscreen').click(function(){
66 if ($(this).hasClass('disabled')) {
67 toggleFullscreen(true);
68 } else {
69 toggleFullscreen(false);
70 }
71 });
Scott Main3b90aff2013-08-01 18:09:35 -070072
Scott Maine4d8f1b2012-06-21 18:03:05 -070073 // initialize the divs with custom scrollbars
74 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070075
Scott Maine4d8f1b2012-06-21 18:03:05 -070076 // add HRs below all H2s (except for a few other h2 variants)
Scott Mainc29b3f52014-05-30 21:18:30 -070077 $('h2').not('#qv h2')
78 .not('#tb h2')
79 .not('.sidebox h2')
80 .not('#devdoc-nav h2')
81 .not('h2.norule').css({marginBottom:0})
82 .after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070083
84 // set up the search close button
85 $('.search .close').click(function() {
86 $searchInput = $('#search_autocomplete');
87 $searchInput.attr('value', '');
88 $(this).addClass("hide");
89 $("#search-container").removeClass('active');
90 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070091 search_focus_changed($searchInput.get(), false);
92 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070093 });
94
95 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070096 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070097 $("#btn-quicknav").click(function() {
98 if (quicknav_open) {
99 $(this).removeClass('active');
100 quicknav_open = false;
101 collapse();
102 } else {
103 $(this).addClass('active');
104 quicknav_open = true;
105 expand();
106 }
107 })
Scott Main3b90aff2013-08-01 18:09:35 -0700108
Scott Maine4d8f1b2012-06-21 18:03:05 -0700109 var expand = function() {
110 $('#header-wrap').addClass('quicknav');
111 $('#quicknav').stop().show().animate({opacity:'1'});
112 }
Scott Main3b90aff2013-08-01 18:09:35 -0700113
Scott Maine4d8f1b2012-06-21 18:03:05 -0700114 var collapse = function() {
115 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
116 $(this).hide();
117 $('#header-wrap').removeClass('quicknav');
118 });
119 }
Scott Main3b90aff2013-08-01 18:09:35 -0700120
121
Scott Maine4d8f1b2012-06-21 18:03:05 -0700122 //Set up search
123 $("#search_autocomplete").focus(function() {
124 $("#search-container").addClass('active');
125 })
126 $("#search-container").mouseover(function() {
127 $("#search-container").addClass('active');
128 $("#search_autocomplete").focus();
129 })
130 $("#search-container").mouseout(function() {
131 if ($("#search_autocomplete").is(":focus")) return;
132 if ($("#search_autocomplete").val() == '') {
133 setTimeout(function(){
134 $("#search-container").removeClass('active');
135 $("#search_autocomplete").blur();
136 },250);
137 }
138 })
139 $("#search_autocomplete").blur(function() {
140 if ($("#search_autocomplete").val() == '') {
141 $("#search-container").removeClass('active');
142 }
143 })
144
Scott Main3b90aff2013-08-01 18:09:35 -0700145
Scott Maine4d8f1b2012-06-21 18:03:05 -0700146 // prep nav expandos
147 var pagePath = document.location.pathname;
148 // account for intl docs by removing the intl/*/ path
149 if (pagePath.indexOf("/intl/") == 0) {
150 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
151 }
Scott Mainac2aef52013-02-12 14:15:23 -0800152
Scott Maine4d8f1b2012-06-21 18:03:05 -0700153 if (pagePath.indexOf(SITE_ROOT) == 0) {
154 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
155 pagePath += 'index.html';
156 }
157 }
158
Scott Main01a25452013-02-12 17:32:27 -0800159 // Need a copy of the pagePath before it gets changed in the next block;
160 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
161 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700162 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
163 // If running locally, SITE_ROOT will be a relative path, so account for that by
164 // finding the relative URL to this page. This will allow us to find links on the page
165 // leading back to this page.
166 var pathParts = pagePath.split('/');
167 var relativePagePathParts = [];
168 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
169 for (var i = 0; i < upDirs; i++) {
170 relativePagePathParts.push('..');
171 }
172 for (var i = 0; i < upDirs; i++) {
173 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
174 }
175 relativePagePathParts.push(pathParts[pathParts.length - 1]);
176 pagePath = relativePagePathParts.join('/');
177 } else {
178 // Otherwise the page path is already an absolute URL
179 }
180
Scott Mainac2aef52013-02-12 14:15:23 -0800181 // Highlight the header tabs...
182 // highlight Design tab
183 if ($("body").hasClass("design")) {
184 $("#header li.design a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700185 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800186
smain@google.com6040ffa2014-06-13 15:06:23 -0700187 // highlight About tabs
188 } else if ($("body").hasClass("about")) {
189 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
190 if (rootDir == "about") {
191 $("#nav-x li.about a").addClass("selected");
192 } else if (rootDir == "wear") {
193 $("#nav-x li.wear a").addClass("selected");
194 } else if (rootDir == "tv") {
195 $("#nav-x li.tv a").addClass("selected");
196 } else if (rootDir == "auto") {
197 $("#nav-x li.auto a").addClass("selected");
198 }
Scott Mainac2aef52013-02-12 14:15:23 -0800199 // highlight Develop tab
200 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
201 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700202 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800203 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800204 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800205 if (rootDir == "training") {
206 $("#nav-x li.training a").addClass("selected");
207 } else if (rootDir == "guide") {
208 $("#nav-x li.guide a").addClass("selected");
209 } else if (rootDir == "reference") {
210 // If the root is reference, but page is also part of Google Services, select Google
211 if ($("body").hasClass("google")) {
212 $("#nav-x li.google a").addClass("selected");
213 } else {
214 $("#nav-x li.reference a").addClass("selected");
215 }
216 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
217 $("#nav-x li.tools a").addClass("selected");
218 } else if ($("body").hasClass("google")) {
219 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700220 } else if ($("body").hasClass("samples")) {
221 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800222 }
223
224 // highlight Distribute tab
225 } else if ($("body").hasClass("distribute")) {
226 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700227 $("#sticky-header").addClass("distribute");
228
229 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
230 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
231 if (secondFrag == "users") {
232 $("#nav-x li.users a").addClass("selected");
233 } else if (secondFrag == "engage") {
234 $("#nav-x li.engage a").addClass("selected");
235 } else if (secondFrag == "monetize") {
236 $("#nav-x li.monetize a").addClass("selected");
237 } else if (secondFrag == "tools") {
238 $("#nav-x li.disttools a").addClass("selected");
239 } else if (secondFrag == "stories") {
240 $("#nav-x li.stories a").addClass("selected");
241 } else if (secondFrag == "essentials") {
242 $("#nav-x li.essentials a").addClass("selected");
243 } else if (secondFrag == "googleplay") {
244 $("#nav-x li.googleplay a").addClass("selected");
245 }
246 } else if ($("body").hasClass("about")) {
247 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700248 }
Scott Mainac2aef52013-02-12 14:15:23 -0800249
Scott Mainf6145542013-04-01 16:38:11 -0700250 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
251 // and highlight the sidenav
252 mPagePath = pagePath;
253 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700254 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800255
Scott Mainf6145542013-04-01 16:38:11 -0700256 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700257 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700258 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700259 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800260 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700261
262 // set up prev links
263 var $prevLink = [];
264 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700265
Scott Maine4d8f1b2012-06-21 18:03:05 -0700266 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
267false; // navigate across topic boundaries only in design docs
268 if ($prevListItem.length) {
smain@google.comc91ecb72014-06-23 10:22:23 -0700269 if ($prevListItem.hasClass('nav-section') || crossBoundaries) {
Scott Main5a1123e2012-09-26 12:51:28 -0700270 // jump to last topic of previous section
271 $prevLink = $prevListItem.find('a:last');
272 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700273 // jump to previous topic in this section
274 $prevLink = $prevListItem.find('a:eq(0)');
275 }
276 } else {
277 // jump to this section's index page (if it exists)
278 var $parentListItem = $selListItem.parents('li');
279 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700280
Scott Maine4d8f1b2012-06-21 18:03:05 -0700281 // except if cross boundaries aren't allowed, and we're at the top of a section already
282 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700283 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700284 && $selListItem.hasClass('nav-section')) {
285 $prevLink = [];
286 }
287 }
288
Scott Maine4d8f1b2012-06-21 18:03:05 -0700289 // set up next links
290 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700291 var startClass = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700292 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700293
Scott Main1a00f7f2013-10-29 11:11:19 -0700294 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700295 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700296 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700297
298 // if there aren't any children, go to the next section (required for About pages)
299 if($nextLink.length == 0) {
300 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700301 } else if ($('.topic-start-link').length) {
302 // as long as there's a child link and there is a "topic start link" (we're on a landing)
303 // then set the landing page "start link" text to be the first doc title
304 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700305 }
Scott Main3b90aff2013-08-01 18:09:35 -0700306
Scott Main5a1123e2012-09-26 12:51:28 -0700307 // If the selected page has a description, then it's a class or article homepage
308 if ($selListItem.find('a[description]').length) {
309 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700310 startClass = true;
311 }
312 } else {
313 // jump to the next topic in this section (if it exists)
314 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700315 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700316 isCrossingBoundary = true;
317 // no more topics in this section, jump to the first topic in the next section
smain@google.comabf34112014-06-23 11:39:02 -0700318 $nextLink = $selListItem.parents('li:eq(0)').next('li').find('a:eq(0)');
Scott Main5a1123e2012-09-26 12:51:28 -0700319 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
320 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700321 if ($nextLink.length == 0) {
322 // if that doesn't work, we're at the end of the list, so disable NEXT link
323 $('.next-page-link').attr('href','').addClass("disabled")
324 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700325 // and completely hide the one in the footer
326 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700327 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700328 }
329 }
330 }
Scott Main5a1123e2012-09-26 12:51:28 -0700331
332 if (startClass) {
333 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
334
Scott Main3b90aff2013-08-01 18:09:35 -0700335 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700336 // then we need to add a bottom border to button
337 if (!$("#tb").length) {
338 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700339 }
Scott Main5a1123e2012-09-26 12:51:28 -0700340 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
341 $('.content-footer.next-class').show();
342 $('.next-page-link').attr('href','')
343 .removeClass("hide").addClass("disabled")
344 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700345 // and completely hide the one in the footer
346 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700347 if ($nextLink.length) {
348 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700349 .removeClass("hide")
350 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700351 $('.next-class-link').find('.new').empty();
352 }
Scott Main5a1123e2012-09-26 12:51:28 -0700353 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700354 $('.next-page-link').attr('href', $nextLink.attr('href'))
355 .removeClass("hide");
356 // for the footer link, also add the next page title
357 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700358 }
359
360 if (!startClass && $prevLink.length) {
361 var prevHref = $prevLink.attr('href');
362 if (prevHref == SITE_ROOT + 'index.html') {
363 // Don't show Previous when it leads to the homepage
364 } else {
365 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
366 }
Scott Main3b90aff2013-08-01 18:09:35 -0700367 }
Scott Main5a1123e2012-09-26 12:51:28 -0700368
Scott Maine4d8f1b2012-06-21 18:03:05 -0700369 }
Scott Main3b90aff2013-08-01 18:09:35 -0700370
371
372
Scott Main5a1123e2012-09-26 12:51:28 -0700373 // Set up the course landing pages for Training with class names and descriptions
374 if ($('body.trainingcourse').length) {
375 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700376
377 // create an array for all the class descriptions
378 var $classDescriptions = new Array($classLinks.length);
379 var lang = getLangPref();
380 $classLinks.each(function(index) {
381 var langDescr = $(this).attr(lang + "-description");
382 if (typeof langDescr !== 'undefined' && langDescr !== false) {
383 // if there's a class description in the selected language, use that
384 $classDescriptions[index] = langDescr;
385 } else {
386 // otherwise, use the default english description
387 $classDescriptions[index] = $(this).attr("description");
388 }
389 });
Scott Main3b90aff2013-08-01 18:09:35 -0700390
Scott Main5a1123e2012-09-26 12:51:28 -0700391 var $olClasses = $('<ol class="class-list"></ol>');
392 var $liClass;
393 var $imgIcon;
394 var $h2Title;
395 var $pSummary;
396 var $olLessons;
397 var $liLesson;
398 $classLinks.each(function(index) {
399 $liClass = $('<li></li>');
400 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700401 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700402
Scott Main5a1123e2012-09-26 12:51:28 -0700403 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700404
Scott Main5a1123e2012-09-26 12:51:28 -0700405 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700406
Scott Main5a1123e2012-09-26 12:51:28 -0700407 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700408 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
409 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700410 $lessons.each(function(index) {
411 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
412 });
413 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700414 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
415 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700416 $pSummary.addClass('article');
417 }
418
419 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
420 $olClasses.append($liClass);
421 });
422 $('.jd-descr').append($olClasses);
423 }
424
Scott Maine4d8f1b2012-06-21 18:03:05 -0700425 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700426 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700427
Scott Main3b90aff2013-08-01 18:09:35 -0700428
Scott Maine4d8f1b2012-06-21 18:03:05 -0700429 $(".scroll-pane").scroll(function(event) {
430 event.preventDefault();
431 return false;
432 });
433
434 /* Resize nav height when window height changes */
435 $(window).resize(function() {
436 if ($('#side-nav').length == 0) return;
437 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
438 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
439 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700440 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700441 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
442 updateSideNavPosition();
443 } else {
444 updateSidenavFullscreenWidth();
445 }
446 }
447 resizeNav();
448 });
449
450
Scott Maine4d8f1b2012-06-21 18:03:05 -0700451 var navBarLeftPos;
452 if ($('#devdoc-nav').length) {
453 setNavBarLeftPos();
454 }
455
456
Scott Maine4d8f1b2012-06-21 18:03:05 -0700457 // Set up play-on-hover <video> tags.
458 $('video.play-on-hover').bind('click', function(){
459 $(this).get(0).load(); // in case the video isn't seekable
460 $(this).get(0).play();
461 });
462
463 // Set up tooltips
464 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700465 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700466 var $target = $(this);
467 var $tooltip = $('<div>')
468 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700469 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700470 .hide()
471 .appendTo('body');
472 $target.removeAttr('title');
473
474 $target.hover(function() {
475 // in
476 var targetRect = $target.offset();
477 targetRect.width = $target.width();
478 targetRect.height = $target.height();
479
480 $tooltip.css({
481 left: targetRect.left,
482 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
483 });
484 $tooltip.addClass('below');
485 $tooltip.show();
486 }, function() {
487 // out
488 $tooltip.hide();
489 });
490 });
491
492 // Set up <h2> deeplinks
493 $('h2').click(function() {
494 var id = $(this).attr('id');
495 if (id) {
496 document.location.hash = id;
497 }
498 });
499
500 //Loads the +1 button
501 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
502 po.src = 'https://apis.google.com/js/plusone.js';
503 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
504
505
Scott Main3b90aff2013-08-01 18:09:35 -0700506 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700507 // which avoids the visible width from changing each time the bar appears
508 var $sidenav = $("#side-nav");
509 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700510
Scott Maine4d8f1b2012-06-21 18:03:05 -0700511 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
512
513
514 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700515
Scott Maine4d8f1b2012-06-21 18:03:05 -0700516 if ($(".scroll-pane").length > 1) {
517 // Check if there's a user preference for the panel heights
518 var cookieHeight = readCookie("reference_height");
519 if (cookieHeight) {
520 restoreHeight(cookieHeight);
521 }
522 }
Scott Main3b90aff2013-08-01 18:09:35 -0700523
Scott Main06f3f2c2014-05-30 11:23:00 -0700524 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700525 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700526 // Check if there's an anchor that we need to scroll into view.
527 // A delay is needed, because some browsers do not immediately scroll down to the anchor
528 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700529
Scott Main015d6162013-01-29 09:01:52 -0800530 /* init the language selector based on user cookie for lang */
531 loadLangPref();
532 changeNavLang(getLangPref());
533
534 /* setup event handlers to ensure the overflow menu is visible while picking lang */
535 $("#language select")
536 .mousedown(function() {
537 $("div.morehover").addClass("hover"); })
538 .blur(function() {
539 $("div.morehover").removeClass("hover"); });
540
541 /* some global variable setup */
542 resizePackagesNav = $("#resize-packages-nav");
543 classesNav = $("#classes-nav");
544 devdocNav = $("#devdoc-nav");
545
546 var cookiePath = "";
547 if (location.href.indexOf("/reference/") != -1) {
548 cookiePath = "reference_";
549 } else if (location.href.indexOf("/guide/") != -1) {
550 cookiePath = "guide_";
551 } else if (location.href.indexOf("/tools/") != -1) {
552 cookiePath = "tools_";
553 } else if (location.href.indexOf("/training/") != -1) {
554 cookiePath = "training_";
555 } else if (location.href.indexOf("/design/") != -1) {
556 cookiePath = "design_";
557 } else if (location.href.indexOf("/distribute/") != -1) {
558 cookiePath = "distribute_";
559 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700560
smain@google.com698fff02014-11-20 20:39:33 -0800561
562 /* setup shadowbox for any videos that want it */
smain@google.comf75ee212014-11-24 09:42:59 -0800563 var $videoLinks = $("a.video-shadowbox-button, a.notice-developers-video");
smain@google.com698fff02014-11-20 20:39:33 -0800564 if ($videoLinks.length) {
565 // if there's at least one, add the shadowbox HTML to the body
566 $('body').prepend(
567'<div id="video-container">'+
568 '<div id="video-frame">'+
569 '<div class="video-close">'+
570 '<span id="icon-video-close" onclick="closeVideo()">&nbsp;</span>'+
571 '</div>'+
572 '<div id="youTubePlayer"></div>'+
573 '</div>'+
574'</div>');
575
576 // loads the IFrame Player API code asynchronously.
577 $.getScript("https://www.youtube.com/iframe_api");
578
579 $videoLinks.each(function() {
580 var videoId = $(this).attr('href').split('?v=')[1];
581 $(this).click(function(event) {
582 event.preventDefault();
583 startYouTubePlayer(videoId);
584 });
585 });
smain@google.com698fff02014-11-20 20:39:33 -0800586 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700587});
Scott Main7e447ed2013-02-19 17:22:37 -0800588// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700589
590
smain@google.com698fff02014-11-20 20:39:33 -0800591var youTubePlayer;
592function onYouTubeIframeAPIReady() {
593}
594
595function startYouTubePlayer(videoId) {
smain@google.com570c2212014-11-25 19:01:40 -0800596 var idAndHash = videoId.split("#");
597 var startTime = 0;
smain@google.com481f15c2014-12-12 11:57:27 -0800598 var lang = getLangPref();
599 var captionsOn = lang == 'en' ? 0 : 1;
smain@google.com570c2212014-11-25 19:01:40 -0800600 if (idAndHash.length > 1) {
601 startTime = idAndHash[1].split("t=")[1] != undefined ? idAndHash[1].split("t=")[1] : 0;
602 }
smain@google.com698fff02014-11-20 20:39:33 -0800603 if (youTubePlayer == null) {
604 youTubePlayer = new YT.Player('youTubePlayer', {
605 height: '529',
606 width: '940',
smain@google.com570c2212014-11-25 19:01:40 -0800607 videoId: idAndHash[0],
smain@google.com481f15c2014-12-12 11:57:27 -0800608 playerVars: {start: startTime, hl: lang, cc_load_policy: captionsOn},
smain@google.com698fff02014-11-20 20:39:33 -0800609 events: {
smain@google.comf75ee212014-11-24 09:42:59 -0800610 'onReady': onPlayerReady,
611 'onStateChange': onPlayerStateChange
smain@google.com698fff02014-11-20 20:39:33 -0800612 }
613 });
614 } else {
615 youTubePlayer.playVideo();
616 }
617 $("#video-container").fadeIn(200, function(){$("#video-frame").show()});
618}
619
620function onPlayerReady(event) {
621 event.target.playVideo();
smain@google.comd24088c2014-12-12 11:31:13 -0800622 // track the start playing event so we know from which page the video was selected
623 ga('send', 'event', 'Videos', 'Start: ' +
624 youTubePlayer.getVideoUrl().split('?v=')[1], 'on: ' + document.location.href);
smain@google.com698fff02014-11-20 20:39:33 -0800625}
626
627function closeVideo() {
628 try {
smain@google.comf75ee212014-11-24 09:42:59 -0800629 youTubePlayer.pauseVideo();
smain@google.com698fff02014-11-20 20:39:33 -0800630 $("#video-container").fadeOut(200);
631 } catch(e) {
632 console.log('Video not available');
633 $("#video-container").fadeOut(200);
634 }
635}
636
smain@google.comf75ee212014-11-24 09:42:59 -0800637/* Track youtube playback for analytics */
638function onPlayerStateChange(event) {
639 // Video starts, send the video ID
640 if (event.data == YT.PlayerState.PLAYING) {
smain@google.comd24088c2014-12-12 11:31:13 -0800641 ga('send', 'event', 'Videos', 'Play',
642 youTubePlayer.getVideoUrl().split('?v=')[1]);
smain@google.comf75ee212014-11-24 09:42:59 -0800643 }
644 // Video paused, send video ID and video elapsed time
645 if (event.data == YT.PlayerState.PAUSED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800646 ga('send', 'event', 'Videos', 'Paused',
647 youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
smain@google.comf75ee212014-11-24 09:42:59 -0800648 }
649 // Video finished, send video ID and video elapsed time
650 if (event.data == YT.PlayerState.ENDED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800651 ga('send', 'event', 'Videos', 'Finished',
652 youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
smain@google.comf75ee212014-11-24 09:42:59 -0800653 }
654}
655
smain@google.com698fff02014-11-20 20:39:33 -0800656
657
Scott Mainad08f072013-08-20 16:49:57 -0700658function initExpandableNavItems(rootTag) {
659 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
660 var section = $(this).closest('li.nav-section');
661 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700662 /* hide me and descendants */
663 section.find('ul').slideUp(250, function() {
664 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700665 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700666 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700667 resizeNav();
668 });
669 } else {
670 /* show me */
671 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700672 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700673 $others.removeClass('expanded').children('ul').slideUp(250);
674
675 // now expand me
676 section.closest('li').addClass('expanded');
677 section.children('ul').slideDown(250, function() {
678 resizeNav();
679 });
680 }
681 });
Scott Mainf0093852013-08-22 11:37:11 -0700682
683 // Stop expand/collapse behavior when clicking on nav section links
684 // (since we're navigating away from the page)
685 // This selector captures the first instance of <a>, but not those with "#" as the href.
686 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
687 window.location.href = $(this).attr('href');
688 return false;
689 });
Scott Mainad08f072013-08-20 16:49:57 -0700690}
691
Dirk Doughertyc3921652014-05-13 16:55:26 -0700692
693/** Create the list of breadcrumb links in the sticky header */
694function buildBreadcrumbs() {
695 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
696 // Add the secondary horizontal nav item, if provided
697 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
698 if ($selectedSecondNav.length) {
699 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
700 }
701 // Add the primary horizontal nav
702 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
703 // If there's no header nav item, use the logo link and title from alt text
704 if ($selectedFirstNav.length < 1) {
705 $selectedFirstNav = $("<a>")
706 .attr('href', $("div#header .logo a").attr('href'))
707 .text($("div#header .logo img").attr('alt'));
708 }
709 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
710}
711
712
713
Scott Maine624b3f2013-09-12 12:56:41 -0700714/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700715function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700716 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
717 if ($("ul#nav li.selected").length) {
718 unHighlightSidenav();
719 }
720 // look for URL in sidenav, including the hash
721 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
722
723 // If the selNavLink is still empty, look for it without the hash
724 if ($selNavLink.length == 0) {
725 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
726 }
727
Scott Mainf6145542013-04-01 16:38:11 -0700728 var $selListItem;
729 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700730 // Find this page's <li> in sidenav and set selected
731 $selListItem = $selNavLink.closest('li');
732 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700733
Scott Mainf6145542013-04-01 16:38:11 -0700734 // Traverse up the tree and expand all parent nav-sections
735 $selNavLink.parents('li.nav-section').each(function() {
736 $(this).addClass('expanded');
737 $(this).children('ul').show();
738 });
739 }
740}
741
Scott Maine624b3f2013-09-12 12:56:41 -0700742function unHighlightSidenav() {
743 $("ul#nav li.selected").removeClass("selected");
744 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
745}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700746
747function toggleFullscreen(enable) {
748 var delay = 20;
749 var enabled = true;
750 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
751 if (enable) {
752 // Currently NOT USING fullscreen; enable fullscreen
753 stylesheet.removeAttr('disabled');
754 $('#nav-swap .fullscreen').removeClass('disabled');
755 $('#devdoc-nav').css({left:''});
756 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
757 enabled = true;
758 } else {
759 // Currently USING fullscreen; disable fullscreen
760 stylesheet.attr('disabled', 'disabled');
761 $('#nav-swap .fullscreen').addClass('disabled');
762 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
763 enabled = false;
764 }
smain@google.com6bdcb982014-11-14 11:53:07 -0800765 writeCookie("fullscreen", enabled, null);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700766 setNavBarLeftPos();
767 resizeNav(delay);
768 updateSideNavPosition();
769 setTimeout(initSidenavHeightResize,delay);
770}
771
772
773function setNavBarLeftPos() {
774 navBarLeftPos = $('#body-content').offset().left;
775}
776
777
778function updateSideNavPosition() {
779 var newLeft = $(window).scrollLeft() - navBarLeftPos;
780 $('#devdoc-nav').css({left: -newLeft});
781 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
782}
Scott Main3b90aff2013-08-01 18:09:35 -0700783
Scott Maine4d8f1b2012-06-21 18:03:05 -0700784// TODO: use $(document).ready instead
785function addLoadEvent(newfun) {
786 var current = window.onload;
787 if (typeof window.onload != 'function') {
788 window.onload = newfun;
789 } else {
790 window.onload = function() {
791 current();
792 newfun();
793 }
794 }
795}
796
797var agent = navigator['userAgent'].toLowerCase();
798// If a mobile phone, set flag and do mobile setup
799if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
800 (agent.indexOf("blackberry") != -1) ||
801 (agent.indexOf("webos") != -1) ||
802 (agent.indexOf("mini") != -1)) { // opera mini browsers
803 isMobile = true;
804}
805
806
Scott Main498d7102013-08-21 15:47:38 -0700807$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700808 $("pre:not(.no-pretty-print)").addClass("prettyprint");
809 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700810});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700811
Scott Maine4d8f1b2012-06-21 18:03:05 -0700812
813
814
815/* ######### RESIZE THE SIDENAV HEIGHT ########## */
816
817function resizeNav(delay) {
818 var $nav = $("#devdoc-nav");
819 var $window = $(window);
820 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700821
Scott Maine4d8f1b2012-06-21 18:03:05 -0700822 // Get the height of entire window and the total header height.
823 // Then figure out based on scroll position whether the header is visible
824 var windowHeight = $window.height();
825 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700826 var headerHeight = $('#header-wrapper').outerHeight();
827 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700828
829 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700830 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700831 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700832 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700833
Scott Maine4d8f1b2012-06-21 18:03:05 -0700834 // Depending on whether the header is visible, set the side nav's height.
835 if (headerVisible) {
836 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700837 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700838 } else {
839 // Once header is off screen, the nav height is almost full window height
840 navHeight = windowHeight - topMargin;
841 }
Scott Main3b90aff2013-08-01 18:09:35 -0700842
843
844
Scott Maine4d8f1b2012-06-21 18:03:05 -0700845 $scrollPanes = $(".scroll-pane");
846 if ($scrollPanes.length > 1) {
847 // subtract the height of the api level widget and nav swapper from the available nav height
848 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700849
Scott Maine4d8f1b2012-06-21 18:03:05 -0700850 $("#swapper").css({height:navHeight + "px"});
851 if ($("#nav-tree").is(":visible")) {
852 $("#nav-tree").css({height:navHeight});
853 }
Scott Main3b90aff2013-08-01 18:09:35 -0700854
855 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700856 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700857
858 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700859 // then the package panel should begin to shrink
860 if (parseInt(classesHeight) <= 0) {
861 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
862 $("#packages-nav").css({height:navHeight - 10});
863 }
Scott Main3b90aff2013-08-01 18:09:35 -0700864
Scott Maine4d8f1b2012-06-21 18:03:05 -0700865 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
866 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700867
868
Scott Maine4d8f1b2012-06-21 18:03:05 -0700869 } else {
870 $nav.height(navHeight);
871 }
Scott Main3b90aff2013-08-01 18:09:35 -0700872
Scott Maine4d8f1b2012-06-21 18:03:05 -0700873 if (delay) {
874 updateFromResize = true;
875 delayedReInitScrollbars(delay);
876 } else {
877 reInitScrollbars();
878 }
Scott Main3b90aff2013-08-01 18:09:35 -0700879
Scott Maine4d8f1b2012-06-21 18:03:05 -0700880}
881
882var updateScrollbars = false;
883var updateFromResize = false;
884
885/* Re-initialize the scrollbars to account for changed nav size.
886 * This method postpones the actual update by a 1/4 second in order to optimize the
887 * scroll performance while the header is still visible, because re-initializing the
888 * scroll panes is an intensive process.
889 */
890function delayedReInitScrollbars(delay) {
891 // If we're scheduled for an update, but have received another resize request
892 // before the scheduled resize has occured, just ignore the new request
893 // (and wait for the scheduled one).
894 if (updateScrollbars && updateFromResize) {
895 updateFromResize = false;
896 return;
897 }
Scott Main3b90aff2013-08-01 18:09:35 -0700898
Scott Maine4d8f1b2012-06-21 18:03:05 -0700899 // We're scheduled for an update and the update request came from this method's setTimeout
900 if (updateScrollbars && !updateFromResize) {
901 reInitScrollbars();
902 updateScrollbars = false;
903 } else {
904 updateScrollbars = true;
905 updateFromResize = false;
906 setTimeout('delayedReInitScrollbars()',delay);
907 }
908}
909
910/* Re-initialize the scrollbars to account for changed nav size. */
911function reInitScrollbars() {
912 var pane = $(".scroll-pane").each(function(){
913 var api = $(this).data('jsp');
914 if (!api) { setTimeout(reInitScrollbars,300); return;}
915 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700916 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700917 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
918}
919
920
921/* Resize the height of the nav panels in the reference,
922 * and save the new size to a cookie */
923function saveNavPanels() {
924 var basePath = getBaseUri(location.pathname);
925 var section = basePath.substring(1,basePath.indexOf("/",1));
smain@google.com6bdcb982014-11-14 11:53:07 -0800926 writeCookie("height", resizePackagesNav.css("height"), section);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700927}
928
929
930
931function restoreHeight(packageHeight) {
932 $("#resize-packages-nav").height(packageHeight);
933 $("#packages-nav").height(packageHeight);
934 // var classesHeight = navHeight - packageHeight;
935 // $("#classes-nav").css({height:classesHeight});
936 // $("#classes-nav .jspContainer").css({height:classesHeight});
937}
938
939
940
941/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
942
943
944
945
946
Scott Main3b90aff2013-08-01 18:09:35 -0700947/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700948 This is called when the page finished loading. */
949function scrollIntoView(nav) {
950 var $nav = $("#"+nav);
951 var element = $nav.jScrollPane({/* ...settings... */});
952 var api = element.data('jsp');
953
954 if ($nav.is(':visible')) {
955 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700956 if ($selected.length == 0) {
957 // If no selected item found, exit
958 return;
959 }
Scott Main52dd2062013-08-15 12:22:28 -0700960 // get the selected item's offset from its container nav by measuring the item's offset
961 // relative to the document then subtract the container nav's offset relative to the document
962 var selectedOffset = $selected.offset().top - $nav.offset().top;
963 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
964 // if it's more than 80% down the nav
965 // scroll the item up by an amount equal to 80% the container nav's height
966 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700967 }
968 }
969}
970
971
972
973
974
975
976/* Show popup dialogs */
977function showDialog(id) {
978 $dialog = $("#"+id);
979 $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>');
980 $dialog.wrapInner('<div/>');
981 $dialog.removeClass("hide");
982}
983
984
985
986
987
988/* ######### COOKIES! ########## */
989
990function readCookie(cookie) {
991 var myCookie = cookie_namespace+"_"+cookie+"=";
992 if (document.cookie) {
993 var index = document.cookie.indexOf(myCookie);
994 if (index != -1) {
995 var valStart = index + myCookie.length;
996 var valEnd = document.cookie.indexOf(";", valStart);
997 if (valEnd == -1) {
998 valEnd = document.cookie.length;
999 }
1000 var val = document.cookie.substring(valStart, valEnd);
1001 return val;
1002 }
1003 }
1004 return 0;
1005}
1006
smain@google.com6bdcb982014-11-14 11:53:07 -08001007function writeCookie(cookie, val, section) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001008 if (val==undefined) return;
1009 section = section == null ? "_" : "_"+section+"_";
smain@google.com6bdcb982014-11-14 11:53:07 -08001010 var age = 2*365*24*60*60; // set max-age to 2 years
Scott Main3b90aff2013-08-01 18:09:35 -07001011 var cookieValue = cookie_namespace + section + cookie + "=" + val
smain@google.com80e38f42014-11-03 10:47:12 -08001012 + "; max-age=" + age +"; path=/";
Scott Maine4d8f1b2012-06-21 18:03:05 -07001013 document.cookie = cookieValue;
1014}
1015
1016/* ######### END COOKIES! ########## */
1017
1018
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001019var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -07001020var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001021var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -07001022/* Sets the vertical scoll position at which the sticky bar should appear.
1023 This method is called to reset the position when search results appear or hide */
1024function setStickyTop() {
1025 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
1026}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001027
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001028/*
Scott Mainb16376f2014-05-21 20:35:47 -07001029 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -07001030 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001031$(window).scroll(function(event) {
1032
1033 setStickyTop();
1034 var hiding = false;
1035 var $stickyEl = $('#sticky-header');
1036 var $menuEl = $('.menu-container');
1037 // Exit if there's no sidenav
1038 if ($('#side-nav').length == 0) return;
1039 // Exit if the mouse target is a DIV, because that means the event is coming
1040 // from a scrollable div and so there's no need to make adjustments to our layout
1041 if ($(event.target).nodeName == "DIV") {
1042 return;
1043 }
1044
1045 var top = $(window).scrollTop();
1046 // we set the navbar fixed when the scroll position is beyond the height of the site header...
1047 var shouldBeSticky = top >= stickyTop;
1048 // ... except if the document content is shorter than the sidenav height.
1049 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
1050 if ($("#doc-col").height() < $("#side-nav").height()) {
1051 shouldBeSticky = false;
1052 }
Scott Mainf5257812014-05-22 17:26:38 -07001053 // Account for horizontal scroll
1054 var scrollLeft = $(window).scrollLeft();
1055 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
1056 if (sticky && (scrollLeft != prevScrollLeft)) {
1057 updateSideNavPosition();
1058 prevScrollLeft = scrollLeft;
1059 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001060
1061 // Don't continue if the header is sufficently far away
1062 // (to avoid intensive resizing that slows scrolling)
1063 if (sticky == shouldBeSticky) {
1064 return;
1065 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001066
1067 // If sticky header visible and position is now near top, hide sticky
1068 if (sticky && !shouldBeSticky) {
1069 sticky = false;
1070 hiding = true;
1071 // make the sidenav static again
1072 $('#devdoc-nav')
1073 .removeClass('fixed')
1074 .css({'width':'auto','margin':''})
1075 .prependTo('#side-nav');
1076 // delay hide the sticky
1077 $menuEl.removeClass('sticky-menu');
1078 $stickyEl.fadeOut(250);
1079 hiding = false;
1080
1081 // update the sidenaav position for side scrolling
1082 updateSideNavPosition();
1083 } else if (!sticky && shouldBeSticky) {
1084 sticky = true;
1085 $stickyEl.fadeIn(10);
1086 $menuEl.addClass('sticky-menu');
1087
1088 // make the sidenav fixed
1089 var width = $('#devdoc-nav').width();
1090 $('#devdoc-nav')
1091 .addClass('fixed')
1092 .css({'width':width+'px'})
1093 .prependTo('#body-content');
1094
1095 // update the sidenaav position for side scrolling
1096 updateSideNavPosition();
1097
1098 } else if (hiding && top < 15) {
1099 $menuEl.removeClass('sticky-menu');
1100 $stickyEl.hide();
1101 hiding = false;
1102 }
1103 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
1104});
1105
1106/*
1107 * Manages secion card states and nav resize to conclude loading
1108 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001109(function() {
1110 $(document).ready(function() {
1111
Dirk Doughertyc3921652014-05-13 16:55:26 -07001112 // Stack hover states
1113 $('.section-card-menu').each(function(index, el) {
1114 var height = $(el).height();
1115 $(el).css({height:height+'px', position:'relative'});
1116 var $cardInfo = $(el).find('.card-info');
1117
1118 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1119 });
1120
Dirk Doughertyc3921652014-05-13 16:55:26 -07001121 });
1122
1123})();
1124
Scott Maine4d8f1b2012-06-21 18:03:05 -07001125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
Scott Maind7026f72013-06-17 15:08:49 -07001138/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001139
1140
1141
1142
1143
1144function toggle(obj, slide) {
1145 var ul = $("ul:first", obj);
1146 var li = ul.parent();
1147 if (li.hasClass("closed")) {
1148 if (slide) {
1149 ul.slideDown("fast");
1150 } else {
1151 ul.show();
1152 }
1153 li.removeClass("closed");
1154 li.addClass("open");
1155 $(".toggle-img", li).attr("title", "hide pages");
1156 } else {
1157 ul.slideUp("fast");
1158 li.removeClass("open");
1159 li.addClass("closed");
1160 $(".toggle-img", li).attr("title", "show pages");
1161 }
1162}
1163
1164
Scott Maine4d8f1b2012-06-21 18:03:05 -07001165function buildToggleLists() {
1166 $(".toggle-list").each(
1167 function(i) {
1168 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1169 $(this).addClass("closed");
1170 });
1171}
1172
1173
1174
Scott Maind7026f72013-06-17 15:08:49 -07001175function hideNestedItems(list, toggle) {
1176 $list = $(list);
1177 // hide nested lists
1178 if($list.hasClass('showing')) {
1179 $("li ol", $list).hide('fast');
1180 $list.removeClass('showing');
1181 // show nested lists
1182 } else {
1183 $("li ol", $list).show('fast');
1184 $list.addClass('showing');
1185 }
1186 $(".more,.less",$(toggle)).toggle();
1187}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001188
1189
smain@google.com95948b82014-06-16 19:24:25 -07001190/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1191function setupIdeDocToggle() {
1192 $( "select.ide" ).change(function() {
1193 var selected = $(this).find("option:selected").attr("value");
1194 $(".select-ide").hide();
1195 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001196
smain@google.com95948b82014-06-16 19:24:25 -07001197 $("select.ide").val(selected);
1198 });
1199}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224/* REFERENCE NAV SWAP */
1225
1226
1227function getNavPref() {
1228 var v = readCookie('reference_nav');
1229 if (v != NAV_PREF_TREE) {
1230 v = NAV_PREF_PANELS;
1231 }
1232 return v;
1233}
1234
1235function chooseDefaultNav() {
1236 nav_pref = getNavPref();
1237 if (nav_pref == NAV_PREF_TREE) {
1238 $("#nav-panels").toggle();
1239 $("#panel-link").toggle();
1240 $("#nav-tree").toggle();
1241 $("#tree-link").toggle();
1242 }
1243}
1244
1245function swapNav() {
1246 if (nav_pref == NAV_PREF_TREE) {
1247 nav_pref = NAV_PREF_PANELS;
1248 } else {
1249 nav_pref = NAV_PREF_TREE;
1250 init_default_navtree(toRoot);
1251 }
smain@google.com6bdcb982014-11-14 11:53:07 -08001252 writeCookie("nav", nav_pref, "reference");
Scott Maine4d8f1b2012-06-21 18:03:05 -07001253
1254 $("#nav-panels").toggle();
1255 $("#panel-link").toggle();
1256 $("#nav-tree").toggle();
1257 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001258
Scott Maine4d8f1b2012-06-21 18:03:05 -07001259 resizeNav();
1260
1261 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1262 $("#nav-tree .jspContainer:visible")
1263 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1264 // Another nasty hack to make the scrollbar appear now that we have height
1265 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001266
Scott Maine4d8f1b2012-06-21 18:03:05 -07001267 if ($("#nav-tree").is(':visible')) {
1268 scrollIntoView("nav-tree");
1269 } else {
1270 scrollIntoView("packages-nav");
1271 scrollIntoView("classes-nav");
1272 }
1273}
1274
1275
1276
Scott Mainf5089842012-08-14 16:31:07 -07001277/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001278/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001279/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001280
1281function getBaseUri(uri) {
1282 var intlUrl = (uri.substring(0,6) == "/intl/");
1283 if (intlUrl) {
1284 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1285 base = base.substring(base.indexOf('/')+1, base.length);
1286 //alert("intl, returning base url: /" + base);
1287 return ("/" + base);
1288 } else {
1289 //alert("not intl, returning uri as found.");
1290 return uri;
1291 }
1292}
1293
1294function requestAppendHL(uri) {
1295//append "?hl=<lang> to an outgoing request (such as to blog)
1296 var lang = getLangPref();
1297 if (lang) {
1298 var q = 'hl=' + lang;
1299 uri += '?' + q;
1300 window.location = uri;
1301 return false;
1302 } else {
1303 return true;
1304 }
1305}
1306
1307
Scott Maine4d8f1b2012-06-21 18:03:05 -07001308function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001309 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1310 $links.each(function(i){ // for each link with a translation
1311 var $link = $(this);
1312 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1313 // put the desired language from the attribute as the text
1314 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001315 }
Scott Main6eb95f12012-10-02 17:12:23 -07001316 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001317}
1318
Scott Main015d6162013-01-29 09:01:52 -08001319function changeLangPref(lang, submit) {
smain@google.com6bdcb982014-11-14 11:53:07 -08001320 writeCookie("pref_lang", lang, null);
Scott Main015d6162013-01-29 09:01:52 -08001321
1322 // ####### TODO: Remove this condition once we're stable on devsite #######
1323 // This condition is only needed if we still need to support legacy GAE server
1324 if (devsite) {
1325 // Switch language when on Devsite server
1326 if (submit) {
1327 $("#setlang").submit();
1328 }
1329 } else {
1330 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001331 if (submit) {
1332 window.location = getBaseUri(location.pathname);
1333 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001334 }
1335}
1336
1337function loadLangPref() {
1338 var lang = readCookie("pref_lang");
1339 if (lang != 0) {
1340 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1341 }
1342}
1343
1344function getLangPref() {
1345 var lang = $("#language").find(":selected").attr("value");
1346 if (!lang) {
1347 lang = readCookie("pref_lang");
1348 }
1349 return (lang != 0) ? lang : 'en';
1350}
1351
1352/* ########## END LOCALIZATION ############ */
1353
1354
1355
1356
1357
1358
1359/* Used to hide and reveal supplemental content, such as long code samples.
1360 See the companion CSS in android-developer-docs.css */
1361function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001362 var div = $(obj).closest(".toggle-content");
1363 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001364 if (div.hasClass("closed")) { // if it's closed, open it
1365 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001366 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001367 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001368 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001369 + "assets/images/triangle-opened.png");
1370 } else { // if it's open, close it
1371 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001372 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001373 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001374 div.find(".toggle-content").removeClass("open").addClass("closed")
1375 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001376 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001377 + "assets/images/triangle-closed.png");
1378 });
1379 }
1380 return false;
1381}
Scott Mainf5089842012-08-14 16:31:07 -07001382
1383
Scott Maindb3678b2012-10-23 14:13:41 -07001384/* New version of expandable content */
1385function toggleExpandable(link,id) {
1386 if($(id).is(':visible')) {
1387 $(id).slideUp();
1388 $(link).removeClass('expanded');
1389 } else {
1390 $(id).slideDown();
1391 $(link).addClass('expanded');
1392 }
1393}
1394
1395function hideExpandable(ids) {
1396 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001397 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001398}
1399
Scott Mainf5089842012-08-14 16:31:07 -07001400
1401
1402
1403
Scott Main3b90aff2013-08-01 18:09:35 -07001404/*
Scott Mainf5089842012-08-14 16:31:07 -07001405 * Slideshow 1.0
1406 * Used on /index.html and /develop/index.html for carousel
1407 *
1408 * Sample usage:
1409 * HTML -
1410 * <div class="slideshow-container">
1411 * <a href="" class="slideshow-prev">Prev</a>
1412 * <a href="" class="slideshow-next">Next</a>
1413 * <ul>
1414 * <li class="item"><img src="images/marquee1.jpg"></li>
1415 * <li class="item"><img src="images/marquee2.jpg"></li>
1416 * <li class="item"><img src="images/marquee3.jpg"></li>
1417 * <li class="item"><img src="images/marquee4.jpg"></li>
1418 * </ul>
1419 * </div>
1420 *
1421 * <script type="text/javascript">
1422 * $('.slideshow-container').dacSlideshow({
1423 * auto: true,
1424 * btnPrev: '.slideshow-prev',
1425 * btnNext: '.slideshow-next'
1426 * });
1427 * </script>
1428 *
1429 * Options:
1430 * btnPrev: optional identifier for previous button
1431 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001432 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001433 * auto: whether or not to auto-proceed
1434 * speed: animation speed
1435 * autoTime: time between auto-rotation
1436 * easing: easing function for transition
1437 * start: item to select by default
1438 * scroll: direction to scroll in
1439 * pagination: whether or not to include dotted pagination
1440 *
1441 */
1442
1443 (function($) {
1444 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001445
Scott Mainf5089842012-08-14 16:31:07 -07001446 //Options - see above
1447 o = $.extend({
1448 btnPrev: null,
1449 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001450 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001451 auto: true,
1452 speed: 500,
1453 autoTime: 12000,
1454 easing: null,
1455 start: 0,
1456 scroll: 1,
1457 pagination: true
1458
1459 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001460
1461 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001462 return this.each(function() {
1463
1464 var running = false;
1465 var animCss = o.vertical ? "top" : "left";
1466 var sizeCss = o.vertical ? "height" : "width";
1467 var div = $(this);
1468 var ul = $("ul", div);
1469 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001470 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001471 var timer = null;
1472
1473 var li = $("li", ul);
1474 var itemLength = li.size();
1475 var curr = o.start;
1476
1477 li.css({float: o.vertical ? "none" : "left"});
1478 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1479 div.css({position: "relative", "z-index": "2", left: "0px"});
1480
1481 var liSize = o.vertical ? height(li) : width(li);
1482 var ulSize = liSize * itemLength;
1483 var divSize = liSize;
1484
1485 li.css({width: li.width(), height: li.height()});
1486 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1487
1488 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001489
Scott Mainf5089842012-08-14 16:31:07 -07001490 //Pagination
1491 if (o.pagination) {
1492 var pagination = $("<div class='pagination'></div>");
1493 var pag_ul = $("<ul></ul>");
1494 if (tl > 1) {
1495 for (var i=0;i<tl;i++) {
1496 var li = $("<li>"+i+"</li>");
1497 pag_ul.append(li);
1498 if (i==o.start) li.addClass('active');
1499 li.click(function() {
1500 go(parseInt($(this).text()));
1501 })
1502 }
1503 pagination.append(pag_ul);
1504 div.append(pagination);
1505 }
1506 }
Scott Main3b90aff2013-08-01 18:09:35 -07001507
Scott Mainf5089842012-08-14 16:31:07 -07001508 //Previous button
1509 if(o.btnPrev)
1510 $(o.btnPrev).click(function(e) {
1511 e.preventDefault();
1512 return go(curr-o.scroll);
1513 });
1514
1515 //Next button
1516 if(o.btnNext)
1517 $(o.btnNext).click(function(e) {
1518 e.preventDefault();
1519 return go(curr+o.scroll);
1520 });
Scott Maineb410352013-01-14 19:03:40 -08001521
1522 //Pause button
1523 if(o.btnPause)
1524 $(o.btnPause).click(function(e) {
1525 e.preventDefault();
1526 if ($(this).hasClass('paused')) {
1527 startRotateTimer();
1528 } else {
1529 pauseRotateTimer();
1530 }
1531 });
Scott Main3b90aff2013-08-01 18:09:35 -07001532
Scott Mainf5089842012-08-14 16:31:07 -07001533 //Auto rotation
1534 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001535
Scott Mainf5089842012-08-14 16:31:07 -07001536 function startRotateTimer() {
1537 clearInterval(timer);
1538 timer = setInterval(function() {
1539 if (curr == tl-1) {
1540 go(0);
1541 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001542 go(curr+o.scroll);
1543 }
Scott Mainf5089842012-08-14 16:31:07 -07001544 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001545 $(o.btnPause).removeClass('paused');
1546 }
1547
1548 function pauseRotateTimer() {
1549 clearInterval(timer);
1550 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001551 }
1552
1553 //Go to an item
1554 function go(to) {
1555 if(!running) {
1556
1557 if(to<0) {
1558 to = itemLength-1;
1559 } else if (to>itemLength-1) {
1560 to = 0;
1561 }
1562 curr = to;
1563
1564 running = true;
1565
1566 ul.animate(
1567 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1568 function() {
1569 running = false;
1570 }
1571 );
1572
1573 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1574 $( (curr-o.scroll<0 && o.btnPrev)
1575 ||
1576 (curr+o.scroll > itemLength && o.btnNext)
1577 ||
1578 []
1579 ).addClass("disabled");
1580
Scott Main3b90aff2013-08-01 18:09:35 -07001581
Scott Mainf5089842012-08-14 16:31:07 -07001582 var nav_items = $('li', pagination);
1583 nav_items.removeClass('active');
1584 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001585
Scott Mainf5089842012-08-14 16:31:07 -07001586
1587 }
1588 if(o.auto) startRotateTimer();
1589 return false;
1590 };
1591 });
1592 };
1593
1594 function css(el, prop) {
1595 return parseInt($.css(el[0], prop)) || 0;
1596 };
1597 function width(el) {
1598 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1599 };
1600 function height(el) {
1601 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1602 };
1603
1604 })(jQuery);
1605
1606
Scott Main3b90aff2013-08-01 18:09:35 -07001607/*
Scott Mainf5089842012-08-14 16:31:07 -07001608 * dacSlideshow 1.0
1609 * Used on develop/index.html for side-sliding tabs
1610 *
1611 * Sample usage:
1612 * HTML -
1613 * <div class="slideshow-container">
1614 * <a href="" class="slideshow-prev">Prev</a>
1615 * <a href="" class="slideshow-next">Next</a>
1616 * <ul>
1617 * <li class="item"><img src="images/marquee1.jpg"></li>
1618 * <li class="item"><img src="images/marquee2.jpg"></li>
1619 * <li class="item"><img src="images/marquee3.jpg"></li>
1620 * <li class="item"><img src="images/marquee4.jpg"></li>
1621 * </ul>
1622 * </div>
1623 *
1624 * <script type="text/javascript">
1625 * $('.slideshow-container').dacSlideshow({
1626 * auto: true,
1627 * btnPrev: '.slideshow-prev',
1628 * btnNext: '.slideshow-next'
1629 * });
1630 * </script>
1631 *
1632 * Options:
1633 * btnPrev: optional identifier for previous button
1634 * btnNext: optional identifier for next button
1635 * auto: whether or not to auto-proceed
1636 * speed: animation speed
1637 * autoTime: time between auto-rotation
1638 * easing: easing function for transition
1639 * start: item to select by default
1640 * scroll: direction to scroll in
1641 * pagination: whether or not to include dotted pagination
1642 *
1643 */
1644 (function($) {
1645 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001646
Scott Mainf5089842012-08-14 16:31:07 -07001647 //Options - see above
1648 o = $.extend({
1649 speed : 250,
1650 easing: null,
1651 nav_id: null,
1652 frame_id: null
1653 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001654
1655 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001656 return this.each(function() {
1657
1658 var curr = 0;
1659 var running = false;
1660 var animCss = "margin-left";
1661 var sizeCss = "width";
1662 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001663
Scott Mainf5089842012-08-14 16:31:07 -07001664 var nav = $(o.nav_id, div);
1665 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001666 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001667 var frame = div.find(o.frame_id);
1668 var content_width = $(frame).find('ul').width();
1669 //Buttons
1670 $(nav_li).click(function(e) {
1671 go($(nav_li).index($(this)));
1672 })
Scott Main3b90aff2013-08-01 18:09:35 -07001673
Scott Mainf5089842012-08-14 16:31:07 -07001674 //Go to an item
1675 function go(to) {
1676 if(!running) {
1677 curr = to;
1678 running = true;
1679
1680 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1681 function() {
1682 running = false;
1683 }
1684 );
1685
Scott Main3b90aff2013-08-01 18:09:35 -07001686
Scott Mainf5089842012-08-14 16:31:07 -07001687 nav_li.removeClass('active');
1688 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001689
Scott Mainf5089842012-08-14 16:31:07 -07001690
1691 }
1692 return false;
1693 };
1694 });
1695 };
1696
1697 function css(el, prop) {
1698 return parseInt($.css(el[0], prop)) || 0;
1699 };
1700 function width(el) {
1701 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1702 };
1703 function height(el) {
1704 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1705 };
1706
1707 })(jQuery);
1708
1709
1710
1711
1712
1713/* ######################################################## */
1714/* ################ SEARCH SUGGESTIONS ################## */
1715/* ######################################################## */
1716
1717
Scott Main7e447ed2013-02-19 17:22:37 -08001718
Scott Main0e76e7e2013-03-12 10:24:07 -07001719var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1720var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1721
Scott Mainf5089842012-08-14 16:31:07 -07001722var gMatches = new Array();
1723var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001724var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001725var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1726var gListLength = 0;
1727
1728
1729var gGoogleMatches = new Array();
1730var ROW_COUNT_GOOGLE = 15; // max number of results in list
1731var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001732
Scott Main0e76e7e2013-03-12 10:24:07 -07001733var gDocsMatches = new Array();
1734var ROW_COUNT_DOCS = 100; // max number of results in list
1735var gDocsListLength = 0;
1736
Scott Mainde295272013-03-25 15:48:35 -07001737function onSuggestionClick(link) {
1738 // When user clicks a suggested document, track it
smain@google.comed677d72014-12-12 10:19:17 -08001739 ga('send', 'event', 'Suggestion Click', 'clicked: ' + $(link).attr('href'),
1740 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07001741}
1742
Scott Mainf5089842012-08-14 16:31:07 -07001743function set_item_selected($li, selected)
1744{
1745 if (selected) {
1746 $li.attr('class','jd-autocomplete jd-selected');
1747 } else {
1748 $li.attr('class','jd-autocomplete');
1749 }
1750}
1751
1752function set_item_values(toroot, $li, match)
1753{
1754 var $link = $('a',$li);
1755 $link.html(match.__hilabel || match.label);
1756 $link.attr('href',toroot + match.link);
1757}
1758
Scott Main719acb42013-12-05 16:05:09 -08001759function set_item_values_jd(toroot, $li, match)
1760{
1761 var $link = $('a',$li);
1762 $link.html(match.title);
1763 $link.attr('href',toroot + match.url);
1764}
1765
Scott Main0e76e7e2013-03-12 10:24:07 -07001766function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001767 var $li = $("<li class='jd-autocomplete'></li>");
1768 $list.append($li);
1769
1770 $li.mousedown(function() {
1771 window.location = this.firstChild.getAttribute("href");
1772 });
1773 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001774 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001775 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001776 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1777 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001778 });
Scott Mainde295272013-03-25 15:48:35 -07001779 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001780 $li.attr('class','show-item');
1781 return $li;
1782}
1783
Scott Mainf5089842012-08-14 16:31:07 -07001784function sync_selection_table(toroot)
1785{
Scott Mainf5089842012-08-14 16:31:07 -07001786 var $li; //list item jquery object
1787 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001788
Scott Main0e76e7e2013-03-12 10:24:07 -07001789 // if there are NO results at all, hide all columns
1790 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1791 $('.suggest-card').hide(300);
1792 return;
1793 }
1794
1795 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001796 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001797 // reveal suggestion list
1798 $('.suggest-card.dummy').show();
1799 $('.suggest-card.reference').show();
1800 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001801
Scott Main0e76e7e2013-03-12 10:24:07 -07001802 // reset the lists
1803 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001804
Scott Main0e76e7e2013-03-12 10:24:07 -07001805 // ########### ANDROID RESULTS #############
1806 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001807
Scott Main0e76e7e2013-03-12 10:24:07 -07001808 // determine android results to show
1809 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1810 gMatches.length : ROW_COUNT_FRAMEWORK;
1811 for (i=0; i<gListLength; i++) {
1812 var $li = new_suggestion($(".suggest-card.reference ul"));
1813 set_item_values(toroot, $li, gMatches[i]);
1814 set_item_selected($li, i == gSelectedIndex);
1815 }
1816 }
Scott Main7e447ed2013-02-19 17:22:37 -08001817
Scott Main0e76e7e2013-03-12 10:24:07 -07001818 // ########### GOOGLE RESULTS #############
1819 if (gGoogleMatches.length > 0) {
1820 // show header for list
1821 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001822
Scott Main0e76e7e2013-03-12 10:24:07 -07001823 // determine google results to show
1824 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1825 for (i=0; i<gGoogleListLength; i++) {
1826 var $li = new_suggestion($(".suggest-card.reference ul"));
1827 set_item_values(toroot, $li, gGoogleMatches[i]);
1828 set_item_selected($li, i == gSelectedIndex);
1829 }
1830 }
Scott Mainf5089842012-08-14 16:31:07 -07001831 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001832 $('.suggest-card.reference').hide();
1833 $('.suggest-card.dummy').hide();
1834 }
1835
1836 // ########### JD DOC RESULTS #############
1837 if (gDocsMatches.length > 0) {
1838 // reset the lists
1839 $(".search_filtered_wrapper.docs li").remove();
1840
1841 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001842 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1843 // The order must match the reverse order that each section appears as a card in
1844 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001845 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1846 for (i=0; i<gDocsListLength; i++) {
1847 var sugg = gDocsMatches[i];
1848 var $li;
1849 if (sugg.type == "design") {
1850 $li = new_suggestion($(".suggest-card.design ul"));
1851 } else
1852 if (sugg.type == "distribute") {
1853 $li = new_suggestion($(".suggest-card.distribute ul"));
1854 } else
Scott Main719acb42013-12-05 16:05:09 -08001855 if (sugg.type == "samples") {
1856 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1857 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001858 if (sugg.type == "training") {
1859 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1860 } else
Scott Main719acb42013-12-05 16:05:09 -08001861 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001862 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1863 } else {
1864 continue;
1865 }
1866
Scott Main719acb42013-12-05 16:05:09 -08001867 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001868 set_item_selected($li, i == gSelectedIndex);
1869 }
1870
1871 // add heading and show or hide card
1872 if ($(".suggest-card.design li").length > 0) {
1873 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1874 $(".suggest-card.design").show(300);
1875 } else {
1876 $('.suggest-card.design').hide(300);
1877 }
1878 if ($(".suggest-card.distribute li").length > 0) {
1879 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1880 $(".suggest-card.distribute").show(300);
1881 } else {
1882 $('.suggest-card.distribute').hide(300);
1883 }
1884 if ($(".child-card.guides li").length > 0) {
1885 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1886 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1887 }
1888 if ($(".child-card.training li").length > 0) {
1889 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1890 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1891 }
Scott Main719acb42013-12-05 16:05:09 -08001892 if ($(".child-card.samples li").length > 0) {
1893 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1894 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1895 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001896
1897 if ($(".suggest-card.develop li").length > 0) {
1898 $(".suggest-card.develop").show(300);
1899 } else {
1900 $('.suggest-card.develop').hide(300);
1901 }
1902
1903 } else {
1904 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001905 }
1906}
1907
Scott Main0e76e7e2013-03-12 10:24:07 -07001908/** Called by the search input's onkeydown and onkeyup events.
1909 * Handles navigation with keyboard arrows, Enter key to invoke search,
1910 * otherwise invokes search suggestions on key-up event.
1911 * @param e The JS event
1912 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001913 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001914 * @returns True if the event should bubble up
1915 */
Scott Mainf5089842012-08-14 16:31:07 -07001916function search_changed(e, kd, toroot)
1917{
Scott Main719acb42013-12-05 16:05:09 -08001918 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001919 var search = document.getElementById("search_autocomplete");
1920 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001921 // get the ul hosting the currently selected item
1922 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1923 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1924 var $selectedUl = $columns[gSelectedColumn];
1925
Scott Mainf5089842012-08-14 16:31:07 -07001926 // show/hide the close button
1927 if (text != '') {
1928 $(".search .close").removeClass("hide");
1929 } else {
1930 $(".search .close").addClass("hide");
1931 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001932 // 27 = esc
1933 if (e.keyCode == 27) {
1934 // close all search results
1935 if (kd) $('.search .close').trigger('click');
1936 return true;
1937 }
Scott Mainf5089842012-08-14 16:31:07 -07001938 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001939 else if (e.keyCode == 13) {
1940 if (gSelectedIndex < 0) {
1941 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001942 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1943 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001944 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001945 return true;
1946 } else {
1947 // otherwise, results are already showing, so allow ajax to auto refresh the results
1948 // and ignore this Enter press to avoid the reload.
1949 return false;
1950 }
1951 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001952 // click the link corresponding to selected item
1953 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001954 return false;
1955 }
1956 }
Scott Mainb16376f2014-05-21 20:35:47 -07001957 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001958 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001959 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001960 if ((sticky ) && (search.value != "")) {
1961 $('body,html').animate({scrollTop:0}, '500', 'swing');
1962 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001963 return true;
1964 }
1965 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001966 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001967 // if the next item is a header, skip it
1968 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001969 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001970 }
1971 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001972 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001973 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001974 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1975 // If user reaches top, reset selected column
1976 if (gSelectedIndex < 0) {
1977 gSelectedColumn = -1;
1978 }
Scott Mainf5089842012-08-14 16:31:07 -07001979 }
1980 return false;
1981 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001982 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001983 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001984 // if the next item is a header, skip it
1985 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001986 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001987 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001988 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1989 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1990 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001991 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001992 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001993 }
1994 return false;
1995 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001996 // Consider left/right arrow navigation
1997 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1998 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1999 // 37 LEFT ARROW
2000 // go left only if current column is not left-most column (last column)
2001 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
2002 $('li', $selectedUl).removeClass('jd-selected');
2003 gSelectedColumn++;
2004 $selectedUl = $columns[gSelectedColumn];
2005 // keep or reset the selected item to last item as appropriate
2006 gSelectedIndex = gSelectedIndex >
2007 $("li", $selectedUl).length-1 ?
2008 $("li", $selectedUl).length-1 : gSelectedIndex;
2009 // if the corresponding item is a header, move down
2010 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2011 gSelectedIndex++;
2012 }
Scott Main3b90aff2013-08-01 18:09:35 -07002013 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002014 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2015 return false;
2016 }
2017 // 39 RIGHT ARROW
2018 // go right only if current column is not the right-most column (first column)
2019 else if (e.keyCode == 39 && gSelectedColumn > 0) {
2020 $('li', $selectedUl).removeClass('jd-selected');
2021 gSelectedColumn--;
2022 $selectedUl = $columns[gSelectedColumn];
2023 // keep or reset the selected item to last item as appropriate
2024 gSelectedIndex = gSelectedIndex >
2025 $("li", $selectedUl).length-1 ?
2026 $("li", $selectedUl).length-1 : gSelectedIndex;
2027 // if the corresponding item is a header, move down
2028 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2029 gSelectedIndex++;
2030 }
Scott Main3b90aff2013-08-01 18:09:35 -07002031 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002032 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2033 return false;
2034 }
2035 }
2036
Scott Main719acb42013-12-05 16:05:09 -08002037 // if key-up event and not arrow down/up/left/right,
2038 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07002039 else if (!kd && (e.keyCode != 40)
2040 && (e.keyCode != 38)
2041 && (e.keyCode != 37)
2042 && (e.keyCode != 39)) {
2043 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07002044 gMatches = new Array();
2045 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002046 gGoogleMatches = new Array();
2047 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07002048 gDocsMatches = new Array();
2049 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002050
2051 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07002052 for (var i=0; i<DATA.length; i++) {
2053 var s = DATA[i];
2054 if (text.length != 0 &&
2055 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2056 gMatches[matchedCount] = s;
2057 matchedCount++;
2058 }
2059 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002060 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07002061 for (var i=0; i<gMatches.length; i++) {
2062 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08002063 }
2064
2065
2066 // Search for Google matches
2067 for (var i=0; i<GOOGLE_DATA.length; i++) {
2068 var s = GOOGLE_DATA[i];
2069 if (text.length != 0 &&
2070 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2071 gGoogleMatches[matchedCountGoogle] = s;
2072 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07002073 }
2074 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002075 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08002076 for (var i=0; i<gGoogleMatches.length; i++) {
2077 var s = gGoogleMatches[i];
2078 }
2079
Scott Mainf5089842012-08-14 16:31:07 -07002080 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07002081
2082
2083
Scott Main719acb42013-12-05 16:05:09 -08002084 // Search for matching JD docs
Dirk Dougherty9b7f8f22014-11-01 17:08:56 -07002085 if (text.length >= 2) {
Scott Main719acb42013-12-05 16:05:09 -08002086 // Regex to match only the beginning of a word
2087 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
2088
2089
2090 // Search for Training classes
2091 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002092 // current search comparison, with counters for tag and title,
2093 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002094 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002095 s.matched_tag = 0;
2096 s.matched_title = 0;
2097 var matched = false;
2098
2099 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08002100 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002101 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08002102 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002103 matched = true;
2104 s.matched_tag = j + 1; // add 1 to index position
2105 }
2106 }
Scott Main719acb42013-12-05 16:05:09 -08002107 // Don't consider doc title for lessons (only for class landing pages),
2108 // unless the lesson has a tag that already matches
2109 if ((s.lang == currentLang) &&
2110 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002111 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08002112 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002113 matched = true;
2114 s.matched_title = 1;
2115 }
2116 }
2117 if (matched) {
2118 gDocsMatches[matchedCountDocs] = s;
2119 matchedCountDocs++;
2120 }
2121 }
Scott Main719acb42013-12-05 16:05:09 -08002122
2123
2124 // Search for API Guides
2125 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2126 // current search comparison, with counters for tag and title,
2127 // used later to improve ranking
2128 var s = GUIDE_RESOURCES[i];
2129 s.matched_tag = 0;
2130 s.matched_title = 0;
2131 var matched = false;
2132
2133 // Check if query matches any tags; work backwards toward 1 to assist ranking
2134 for (var j = s.keywords.length - 1; j >= 0; j--) {
2135 // it matches a tag
2136 if (s.keywords[j].toLowerCase().match(textRegex)) {
2137 matched = true;
2138 s.matched_tag = j + 1; // add 1 to index position
2139 }
2140 }
2141 // Check if query matches the doc title, but only for current language
2142 if (s.lang == currentLang) {
2143 // if query matches the doc title
2144 if (s.title.toLowerCase().match(textRegex)) {
2145 matched = true;
2146 s.matched_title = 1;
2147 }
2148 }
2149 if (matched) {
2150 gDocsMatches[matchedCountDocs] = s;
2151 matchedCountDocs++;
2152 }
2153 }
2154
2155
2156 // Search for Tools Guides
2157 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2158 // current search comparison, with counters for tag and title,
2159 // used later to improve ranking
2160 var s = TOOLS_RESOURCES[i];
2161 s.matched_tag = 0;
2162 s.matched_title = 0;
2163 var matched = false;
2164
2165 // Check if query matches any tags; work backwards toward 1 to assist ranking
2166 for (var j = s.keywords.length - 1; j >= 0; j--) {
2167 // it matches a tag
2168 if (s.keywords[j].toLowerCase().match(textRegex)) {
2169 matched = true;
2170 s.matched_tag = j + 1; // add 1 to index position
2171 }
2172 }
2173 // Check if query matches the doc title, but only for current language
2174 if (s.lang == currentLang) {
2175 // if query matches the doc title
2176 if (s.title.toLowerCase().match(textRegex)) {
2177 matched = true;
2178 s.matched_title = 1;
2179 }
2180 }
2181 if (matched) {
2182 gDocsMatches[matchedCountDocs] = s;
2183 matchedCountDocs++;
2184 }
2185 }
2186
2187
2188 // Search for About docs
2189 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2190 // current search comparison, with counters for tag and title,
2191 // used later to improve ranking
2192 var s = ABOUT_RESOURCES[i];
2193 s.matched_tag = 0;
2194 s.matched_title = 0;
2195 var matched = false;
2196
2197 // Check if query matches any tags; work backwards toward 1 to assist ranking
2198 for (var j = s.keywords.length - 1; j >= 0; j--) {
2199 // it matches a tag
2200 if (s.keywords[j].toLowerCase().match(textRegex)) {
2201 matched = true;
2202 s.matched_tag = j + 1; // add 1 to index position
2203 }
2204 }
2205 // Check if query matches the doc title, but only for current language
2206 if (s.lang == currentLang) {
2207 // if query matches the doc title
2208 if (s.title.toLowerCase().match(textRegex)) {
2209 matched = true;
2210 s.matched_title = 1;
2211 }
2212 }
2213 if (matched) {
2214 gDocsMatches[matchedCountDocs] = s;
2215 matchedCountDocs++;
2216 }
2217 }
2218
2219
2220 // Search for Design guides
2221 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2222 // current search comparison, with counters for tag and title,
2223 // used later to improve ranking
2224 var s = DESIGN_RESOURCES[i];
2225 s.matched_tag = 0;
2226 s.matched_title = 0;
2227 var matched = false;
2228
2229 // Check if query matches any tags; work backwards toward 1 to assist ranking
2230 for (var j = s.keywords.length - 1; j >= 0; j--) {
2231 // it matches a tag
2232 if (s.keywords[j].toLowerCase().match(textRegex)) {
2233 matched = true;
2234 s.matched_tag = j + 1; // add 1 to index position
2235 }
2236 }
2237 // Check if query matches the doc title, but only for current language
2238 if (s.lang == currentLang) {
2239 // if query matches the doc title
2240 if (s.title.toLowerCase().match(textRegex)) {
2241 matched = true;
2242 s.matched_title = 1;
2243 }
2244 }
2245 if (matched) {
2246 gDocsMatches[matchedCountDocs] = s;
2247 matchedCountDocs++;
2248 }
2249 }
2250
2251
2252 // Search for Distribute guides
2253 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2254 // current search comparison, with counters for tag and title,
2255 // used later to improve ranking
2256 var s = DISTRIBUTE_RESOURCES[i];
2257 s.matched_tag = 0;
2258 s.matched_title = 0;
2259 var matched = false;
2260
2261 // Check if query matches any tags; work backwards toward 1 to assist ranking
2262 for (var j = s.keywords.length - 1; j >= 0; j--) {
2263 // it matches a tag
2264 if (s.keywords[j].toLowerCase().match(textRegex)) {
2265 matched = true;
2266 s.matched_tag = j + 1; // add 1 to index position
2267 }
2268 }
2269 // Check if query matches the doc title, but only for current language
2270 if (s.lang == currentLang) {
2271 // if query matches the doc title
2272 if (s.title.toLowerCase().match(textRegex)) {
2273 matched = true;
2274 s.matched_title = 1;
2275 }
2276 }
2277 if (matched) {
2278 gDocsMatches[matchedCountDocs] = s;
2279 matchedCountDocs++;
2280 }
2281 }
2282
2283
2284 // Search for Google guides
2285 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2286 // current search comparison, with counters for tag and title,
2287 // used later to improve ranking
2288 var s = GOOGLE_RESOURCES[i];
2289 s.matched_tag = 0;
2290 s.matched_title = 0;
2291 var matched = false;
2292
2293 // Check if query matches any tags; work backwards toward 1 to assist ranking
2294 for (var j = s.keywords.length - 1; j >= 0; j--) {
2295 // it matches a tag
2296 if (s.keywords[j].toLowerCase().match(textRegex)) {
2297 matched = true;
2298 s.matched_tag = j + 1; // add 1 to index position
2299 }
2300 }
2301 // Check if query matches the doc title, but only for current language
2302 if (s.lang == currentLang) {
2303 // if query matches the doc title
2304 if (s.title.toLowerCase().match(textRegex)) {
2305 matched = true;
2306 s.matched_title = 1;
2307 }
2308 }
2309 if (matched) {
2310 gDocsMatches[matchedCountDocs] = s;
2311 matchedCountDocs++;
2312 }
2313 }
2314
2315
2316 // Search for Samples
2317 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2318 // current search comparison, with counters for tag and title,
2319 // used later to improve ranking
2320 var s = SAMPLES_RESOURCES[i];
2321 s.matched_tag = 0;
2322 s.matched_title = 0;
2323 var matched = false;
2324 // Check if query matches any tags; work backwards toward 1 to assist ranking
2325 for (var j = s.keywords.length - 1; j >= 0; j--) {
2326 // it matches a tag
2327 if (s.keywords[j].toLowerCase().match(textRegex)) {
2328 matched = true;
2329 s.matched_tag = j + 1; // add 1 to index position
2330 }
2331 }
2332 // Check if query matches the doc title, but only for current language
2333 if (s.lang == currentLang) {
2334 // if query matches the doc title.t
2335 if (s.title.toLowerCase().match(textRegex)) {
2336 matched = true;
2337 s.matched_title = 1;
2338 }
2339 }
2340 if (matched) {
2341 gDocsMatches[matchedCountDocs] = s;
2342 matchedCountDocs++;
2343 }
2344 }
2345
2346 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002347 rank_autocomplete_doc_results(text, gDocsMatches);
2348 }
2349
2350 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002351 sync_selection_table(toroot);
2352 return true; // allow the event to bubble up to the search api
2353 }
2354}
2355
Scott Main0e76e7e2013-03-12 10:24:07 -07002356/* Order the jd doc result list based on match quality */
2357function rank_autocomplete_doc_results(query, matches) {
2358 query = query || '';
2359 if (!matches || !matches.length)
2360 return;
2361
2362 var _resultScoreFn = function(match) {
2363 var score = 1.0;
2364
2365 // if the query matched a tag
2366 if (match.matched_tag > 0) {
2367 // multiply score by factor relative to position in tags list (max of 3)
2368 score *= 3 / match.matched_tag;
2369
2370 // if it also matched the title
2371 if (match.matched_title > 0) {
2372 score *= 2;
2373 }
2374 } else if (match.matched_title > 0) {
2375 score *= 3;
2376 }
2377
2378 return score;
2379 };
2380
2381 for (var i=0; i<matches.length; i++) {
2382 matches[i].__resultScore = _resultScoreFn(matches[i]);
2383 }
2384
2385 matches.sort(function(a,b){
2386 var n = b.__resultScore - a.__resultScore;
2387 if (n == 0) // lexicographical sort if scores are the same
2388 n = (a.label < b.label) ? -1 : 1;
2389 return n;
2390 });
2391}
2392
Scott Main7e447ed2013-02-19 17:22:37 -08002393/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002394function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002395 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002396 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002397 return;
2398
2399 // helper function that gets the last occurence index of the given regex
2400 // in the given string, or -1 if not found
2401 var _lastSearch = function(s, re) {
2402 if (s == '')
2403 return -1;
2404 var l = -1;
2405 var tmp;
2406 while ((tmp = s.search(re)) >= 0) {
2407 if (l < 0) l = 0;
2408 l += tmp;
2409 s = s.substr(tmp + 1);
2410 }
2411 return l;
2412 };
2413
2414 // helper function that counts the occurrences of a given character in
2415 // a given string
2416 var _countChar = function(s, c) {
2417 var n = 0;
2418 for (var i=0; i<s.length; i++)
2419 if (s.charAt(i) == c) ++n;
2420 return n;
2421 };
2422
2423 var queryLower = query.toLowerCase();
2424 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2425 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2426 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2427
2428 var _resultScoreFn = function(result) {
2429 // scores are calculated based on exact and prefix matches,
2430 // and then number of path separators (dots) from the last
2431 // match (i.e. favoring classes and deep package names)
2432 var score = 1.0;
2433 var labelLower = result.label.toLowerCase();
2434 var t;
2435 t = _lastSearch(labelLower, partExactAlnumRE);
2436 if (t >= 0) {
2437 // exact part match
2438 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2439 score *= 200 / (partsAfter + 1);
2440 } else {
2441 t = _lastSearch(labelLower, partPrefixAlnumRE);
2442 if (t >= 0) {
2443 // part prefix match
2444 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2445 score *= 20 / (partsAfter + 1);
2446 }
2447 }
2448
2449 return score;
2450 };
2451
Scott Main7e447ed2013-02-19 17:22:37 -08002452 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002453 // if the API is deprecated, default score is 0; otherwise, perform scoring
2454 if (matches[i].deprecated == "true") {
2455 matches[i].__resultScore = 0;
2456 } else {
2457 matches[i].__resultScore = _resultScoreFn(matches[i]);
2458 }
Scott Mainf5089842012-08-14 16:31:07 -07002459 }
2460
Scott Main7e447ed2013-02-19 17:22:37 -08002461 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002462 var n = b.__resultScore - a.__resultScore;
2463 if (n == 0) // lexicographical sort if scores are the same
2464 n = (a.label < b.label) ? -1 : 1;
2465 return n;
2466 });
2467}
2468
Scott Main7e447ed2013-02-19 17:22:37 -08002469/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002470function highlight_autocomplete_result_labels(query) {
2471 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002472 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002473 return;
2474
2475 var queryLower = query.toLowerCase();
2476 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2477 var queryRE = new RegExp(
2478 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2479 for (var i=0; i<gMatches.length; i++) {
2480 gMatches[i].__hilabel = gMatches[i].label.replace(
2481 queryRE, '<b>$1</b>');
2482 }
Scott Main7e447ed2013-02-19 17:22:37 -08002483 for (var i=0; i<gGoogleMatches.length; i++) {
2484 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2485 queryRE, '<b>$1</b>');
2486 }
Scott Mainf5089842012-08-14 16:31:07 -07002487}
2488
2489function search_focus_changed(obj, focused)
2490{
Scott Main3b90aff2013-08-01 18:09:35 -07002491 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002492 if(obj.value == ""){
2493 $(".search .close").addClass("hide");
2494 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002495 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002496 }
2497}
2498
2499function submit_search() {
2500 var query = document.getElementById('search_autocomplete').value;
2501 location.hash = 'q=' + query;
2502 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002503 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002504 return false;
2505}
2506
2507
2508function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002509 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002510 $(".search .close").addClass("hide");
2511 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002512
Scott Mainf5089842012-08-14 16:31:07 -07002513 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002514
Scott Mainf5089842012-08-14 16:31:07 -07002515 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2516 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002517
2518 // forcefully regain key-up event control (previously jacked by search api)
2519 $("#search_autocomplete").keyup(function(event) {
2520 return search_changed(event, false, toRoot);
2521 });
2522
Scott Mainf5089842012-08-14 16:31:07 -07002523 return false;
2524}
2525
2526
2527
2528/* ########################################################## */
2529/* ################ CUSTOM SEARCH ENGINE ################## */
2530/* ########################################################## */
2531
Scott Mainf5089842012-08-14 16:31:07 -07002532var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002533google.load('search', '1', {"callback" : function() {
2534 searchControl = new google.search.SearchControl();
2535 } });
Scott Mainf5089842012-08-14 16:31:07 -07002536
2537function loadSearchResults() {
2538 document.getElementById("search_autocomplete").style.color = "#000";
2539
Scott Mainf5089842012-08-14 16:31:07 -07002540 searchControl = new google.search.SearchControl();
2541
2542 // use our existing search form and use tabs when multiple searchers are used
2543 drawOptions = new google.search.DrawOptions();
2544 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2545 drawOptions.setInput(document.getElementById("search_autocomplete"));
2546
2547 // configure search result options
2548 searchOptions = new google.search.SearcherOptions();
2549 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2550
2551 // configure each of the searchers, for each tab
2552 devSiteSearcher = new google.search.WebSearch();
2553 devSiteSearcher.setUserDefinedLabel("All");
2554 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2555
2556 designSearcher = new google.search.WebSearch();
2557 designSearcher.setUserDefinedLabel("Design");
2558 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2559
2560 trainingSearcher = new google.search.WebSearch();
2561 trainingSearcher.setUserDefinedLabel("Training");
2562 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2563
2564 guidesSearcher = new google.search.WebSearch();
2565 guidesSearcher.setUserDefinedLabel("Guides");
2566 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2567
2568 referenceSearcher = new google.search.WebSearch();
2569 referenceSearcher.setUserDefinedLabel("Reference");
2570 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2571
Scott Maindf08ada2012-12-03 08:54:37 -08002572 googleSearcher = new google.search.WebSearch();
2573 googleSearcher.setUserDefinedLabel("Google Services");
2574 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2575
Scott Mainf5089842012-08-14 16:31:07 -07002576 blogSearcher = new google.search.WebSearch();
2577 blogSearcher.setUserDefinedLabel("Blog");
2578 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2579
2580 // add each searcher to the search control
2581 searchControl.addSearcher(devSiteSearcher, searchOptions);
2582 searchControl.addSearcher(designSearcher, searchOptions);
2583 searchControl.addSearcher(trainingSearcher, searchOptions);
2584 searchControl.addSearcher(guidesSearcher, searchOptions);
2585 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002586 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002587 searchControl.addSearcher(blogSearcher, searchOptions);
2588
2589 // configure result options
2590 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2591 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2592 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2593 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2594
2595 // upon ajax search, refresh the url and search title
2596 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2597 updateResultTitle(query);
2598 var query = document.getElementById('search_autocomplete').value;
2599 location.hash = 'q=' + query;
2600 });
2601
Scott Mainde295272013-03-25 15:48:35 -07002602 // once search results load, set up click listeners
2603 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2604 addResultClickListeners();
2605 });
2606
Scott Mainf5089842012-08-14 16:31:07 -07002607 // draw the search results box
2608 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2609
2610 // get query and execute the search
2611 searchControl.execute(decodeURI(getQuery(location.hash)));
2612
2613 document.getElementById("search_autocomplete").focus();
2614 addTabListeners();
2615}
2616// End of loadSearchResults
2617
2618
2619google.setOnLoadCallback(function(){
2620 if (location.hash.indexOf("q=") == -1) {
2621 // if there's no query in the url, don't search and make sure results are hidden
2622 $('#searchResults').hide();
2623 return;
2624 } else {
2625 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002626 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002627 $(".search .close").removeClass("hide");
2628 loadSearchResults();
2629 }
2630}, true);
2631
smain@google.com9a818f52014-10-03 09:25:59 -07002632/* Adjust the scroll position to account for sticky header, only if the hash matches an id.
2633 This does not handle <a name=""> tags. Some CSS fixes those, but only for reference docs. */
Scott Mainb16376f2014-05-21 20:35:47 -07002634function offsetScrollForSticky() {
smain@google.com11fc0092014-10-16 22:10:00 -07002635 // Ignore if there's no search bar (some special pages have no header)
2636 if ($("#search-container").length < 1) return;
2637
smain@google.com3b77ab52014-06-17 11:57:27 -07002638 var hash = escape(location.hash.substr(1));
2639 var $matchingElement = $("#"+hash);
smain@google.com08f336ea2014-10-03 17:40:00 -07002640 // Sanity check that there's an element with that ID on the page
2641 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002642 // If the position of the target element is near the top of the page (<20px, where we expect it
2643 // to be because we need to move it down 60px to become in view), then move it down 60px
2644 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2645 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002646 }
2647 }
2648}
2649
Scott Mainf5089842012-08-14 16:31:07 -07002650// when an event on the browser history occurs (back, forward, load) requery hash and do search
2651$(window).hashchange( function(){
smain@google.com2f077192014-10-09 18:04:10 -07002652 // Ignore if there's no search bar (some special pages have no header)
2653 if ($("#search-container").length < 1) return;
2654
Dirk Doughertyc3921652014-05-13 16:55:26 -07002655 // If the hash isn't a search query or there's an error in the query,
2656 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002657 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2658 // If the results pane is open, close it.
2659 if (!$("#searchResults").is(":hidden")) {
2660 hideResults();
2661 }
Scott Mainb16376f2014-05-21 20:35:47 -07002662 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002663 return;
2664 }
2665
2666 // Otherwise, we have a search to do
2667 var query = decodeURI(getQuery(location.hash));
2668 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002669 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002670 $("#search_autocomplete").focus();
2671 $(".search .close").removeClass("hide");
2672
2673 updateResultTitle(query);
2674});
2675
2676function updateResultTitle(query) {
2677 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2678}
2679
2680// forcefully regain key-up event control (previously jacked by search api)
2681$("#search_autocomplete").keyup(function(event) {
2682 return search_changed(event, false, toRoot);
2683});
2684
2685// add event listeners to each tab so we can track the browser history
2686function addTabListeners() {
2687 var tabHeaders = $(".gsc-tabHeader");
2688 for (var i = 0; i < tabHeaders.length; i++) {
2689 $(tabHeaders[i]).attr("id",i).click(function() {
2690 /*
2691 // make a copy of the page numbers for the search left pane
2692 setTimeout(function() {
2693 // remove any residual page numbers
2694 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002695 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002696 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002697 // and because we're going to remove it (previous line),
2698 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002699 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2700 .clone().appendTo('#searchResults .gsc-tabsArea');
2701 }, 200);
2702 */
2703 });
2704 }
2705 setTimeout(function(){$(tabHeaders[0]).click()},200);
2706}
2707
Scott Mainde295272013-03-25 15:48:35 -07002708// add analytics tracking events to each result link
2709function addResultClickListeners() {
2710 $("#searchResults a.gs-title").each(function(index, link) {
2711 // When user clicks enter for Google search results, track it
2712 $(link).click(function() {
smain@google.comed677d72014-12-12 10:19:17 -08002713 ga('send', 'event', 'Google Click', 'clicked: ' + $(this).attr('href'),
2714 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07002715 });
2716 });
2717}
2718
Scott Mainf5089842012-08-14 16:31:07 -07002719
2720function getQuery(hash) {
2721 var queryParts = hash.split('=');
2722 return queryParts[1];
2723}
2724
2725/* returns the given string with all HTML brackets converted to entities
2726 TODO: move this to the site's JS library */
2727function escapeHTML(string) {
2728 return string.replace(/</g,"&lt;")
2729 .replace(/>/g,"&gt;");
2730}
2731
2732
2733
2734
2735
2736
2737
2738/* ######################################################## */
2739/* ################# JAVADOC REFERENCE ################### */
2740/* ######################################################## */
2741
Scott Main65511c02012-09-07 15:51:32 -07002742/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002743if (location.pathname.indexOf("/reference") == 0) {
2744 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2745 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2746 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002747 $(document).ready(function() {
2748 // init available apis based on user pref
2749 changeApiLevel();
2750 initSidenavHeightResize()
2751 });
2752 }
Scott Main65511c02012-09-07 15:51:32 -07002753}
Scott Mainf5089842012-08-14 16:31:07 -07002754
2755var API_LEVEL_COOKIE = "api_level";
2756var minLevel = 1;
2757var maxLevel = 1;
2758
2759/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002760
Scott Mainf5089842012-08-14 16:31:07 -07002761 function initSidenavHeightResize() {
2762 // Change the drag bar size to nicely fit the scrollbar positions
2763 var $dragBar = $(".ui-resizable-s");
2764 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002765
2766 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002767 containment: "#nav-panels",
2768 handles: "s",
2769 alsoResize: "#packages-nav",
2770 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2771 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2772 });
Scott Main3b90aff2013-08-01 18:09:35 -07002773
Scott Mainf5089842012-08-14 16:31:07 -07002774 }
Scott Main3b90aff2013-08-01 18:09:35 -07002775
Scott Mainf5089842012-08-14 16:31:07 -07002776function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002777 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002778 $('#devdoc-nav').css({
2779 'width' : $('#side-nav').css('width'),
2780 'margin' : $('#side-nav').css('margin')
2781 });
2782 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002783
Scott Mainf5089842012-08-14 16:31:07 -07002784 initSidenavHeightResize();
2785}
2786
2787function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002788 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002789 $('#devdoc-nav').css({
2790 'width' : $('#side-nav').css('width'),
2791 'margin' : $('#side-nav').css('margin')
2792 });
2793 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002794
Scott Mainf5089842012-08-14 16:31:07 -07002795 initSidenavHeightResize();
2796}
2797
2798function buildApiLevelSelector() {
2799 maxLevel = SINCE_DATA.length;
2800 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2801 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2802
2803 minLevel = parseInt($("#doc-api-level").attr("class"));
2804 // Handle provisional api levels; the provisional level will always be the highest possible level
2805 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2806 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2807 if (isNaN(minLevel) && minLevel.length) {
2808 minLevel = maxLevel;
2809 }
2810 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2811 for (var i = maxLevel-1; i >= 0; i--) {
2812 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2813 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2814 select.append(option);
2815 }
2816
2817 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2818 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2819 selectedLevelItem.setAttribute('selected',true);
2820}
2821
2822function changeApiLevel() {
2823 maxLevel = SINCE_DATA.length;
2824 var selectedLevel = maxLevel;
2825
2826 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2827 toggleVisisbleApis(selectedLevel, "body");
2828
smain@google.com6bdcb982014-11-14 11:53:07 -08002829 writeCookie(API_LEVEL_COOKIE, selectedLevel, null);
Scott Mainf5089842012-08-14 16:31:07 -07002830
2831 if (selectedLevel < minLevel) {
2832 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002833 $("#naMessage").show().html("<div><p><strong>This " + thing
2834 + " requires API level " + minLevel + " or higher.</strong></p>"
2835 + "<p>This document is hidden because your selected API level for the documentation is "
2836 + selectedLevel + ". You can change the documentation API level with the selector "
2837 + "above the left navigation.</p>"
2838 + "<p>For more information about specifying the API level your app requires, "
2839 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2840 + ">Supporting Different Platform Versions</a>.</p>"
2841 + "<input type='button' value='OK, make this page visible' "
2842 + "title='Change the API level to " + minLevel + "' "
2843 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2844 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002845 } else {
2846 $("#naMessage").hide();
2847 }
2848}
2849
2850function toggleVisisbleApis(selectedLevel, context) {
2851 var apis = $(".api",context);
2852 apis.each(function(i) {
2853 var obj = $(this);
2854 var className = obj.attr("class");
2855 var apiLevelIndex = className.lastIndexOf("-")+1;
2856 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2857 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2858 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2859 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2860 return;
2861 }
2862 apiLevel = parseInt(apiLevel);
2863
2864 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2865 var selectedLevelNum = parseInt(selectedLevel)
2866 var apiLevelNum = parseInt(apiLevel);
2867 if (isNaN(apiLevelNum)) {
2868 apiLevelNum = maxLevel;
2869 }
2870
2871 // Grey things out that aren't available and give a tooltip title
2872 if (apiLevelNum > selectedLevelNum) {
2873 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002874 + apiLevel + "\" or higher. To reveal, change the target API level "
2875 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002876 }
Scott Mainf5089842012-08-14 16:31:07 -07002877 else obj.removeClass("absent").removeAttr("title");
2878 });
2879}
2880
2881
2882
2883
2884/* ################# SIDENAV TREE VIEW ################### */
2885
2886function new_node(me, mom, text, link, children_data, api_level)
2887{
2888 var node = new Object();
2889 node.children = Array();
2890 node.children_data = children_data;
2891 node.depth = mom.depth + 1;
2892
2893 node.li = document.createElement("li");
2894 mom.get_children_ul().appendChild(node.li);
2895
2896 node.label_div = document.createElement("div");
2897 node.label_div.className = "label";
2898 if (api_level != null) {
2899 $(node.label_div).addClass("api");
2900 $(node.label_div).addClass("api-level-"+api_level);
2901 }
2902 node.li.appendChild(node.label_div);
2903
2904 if (children_data != null) {
2905 node.expand_toggle = document.createElement("a");
2906 node.expand_toggle.href = "javascript:void(0)";
2907 node.expand_toggle.onclick = function() {
2908 if (node.expanded) {
2909 $(node.get_children_ul()).slideUp("fast");
2910 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2911 node.expanded = false;
2912 } else {
2913 expand_node(me, node);
2914 }
2915 };
2916 node.label_div.appendChild(node.expand_toggle);
2917
2918 node.plus_img = document.createElement("img");
2919 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2920 node.plus_img.className = "plus";
2921 node.plus_img.width = "8";
2922 node.plus_img.border = "0";
2923 node.expand_toggle.appendChild(node.plus_img);
2924
2925 node.expanded = false;
2926 }
2927
2928 var a = document.createElement("a");
2929 node.label_div.appendChild(a);
2930 node.label = document.createTextNode(text);
2931 a.appendChild(node.label);
2932 if (link) {
2933 a.href = me.toroot + link;
2934 } else {
2935 if (children_data != null) {
2936 a.className = "nolink";
2937 a.href = "javascript:void(0)";
2938 a.onclick = node.expand_toggle.onclick;
2939 // This next line shouldn't be necessary. I'll buy a beer for the first
2940 // person who figures out how to remove this line and have the link
2941 // toggle shut on the first try. --joeo@android.com
2942 node.expanded = false;
2943 }
2944 }
Scott Main3b90aff2013-08-01 18:09:35 -07002945
Scott Mainf5089842012-08-14 16:31:07 -07002946
2947 node.children_ul = null;
2948 node.get_children_ul = function() {
2949 if (!node.children_ul) {
2950 node.children_ul = document.createElement("ul");
2951 node.children_ul.className = "children_ul";
2952 node.children_ul.style.display = "none";
2953 node.li.appendChild(node.children_ul);
2954 }
2955 return node.children_ul;
2956 };
2957
2958 return node;
2959}
2960
Robert Lyd2dd6e52012-11-29 21:28:48 -08002961
2962
2963
Scott Mainf5089842012-08-14 16:31:07 -07002964function expand_node(me, node)
2965{
2966 if (node.children_data && !node.expanded) {
2967 if (node.children_visited) {
2968 $(node.get_children_ul()).slideDown("fast");
2969 } else {
2970 get_node(me, node);
2971 if ($(node.label_div).hasClass("absent")) {
2972 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002973 }
Scott Mainf5089842012-08-14 16:31:07 -07002974 $(node.get_children_ul()).slideDown("fast");
2975 }
2976 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2977 node.expanded = true;
2978
2979 // perform api level toggling because new nodes are new to the DOM
2980 var selectedLevel = $("#apiLevelSelector option:selected").val();
2981 toggleVisisbleApis(selectedLevel, "#side-nav");
2982 }
2983}
2984
2985function get_node(me, mom)
2986{
2987 mom.children_visited = true;
2988 for (var i in mom.children_data) {
2989 var node_data = mom.children_data[i];
2990 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2991 node_data[2], node_data[3]);
2992 }
2993}
2994
2995function this_page_relative(toroot)
2996{
2997 var full = document.location.pathname;
2998 var file = "";
2999 if (toroot.substr(0, 1) == "/") {
3000 if (full.substr(0, toroot.length) == toroot) {
3001 return full.substr(toroot.length);
3002 } else {
3003 // the file isn't under toroot. Fail.
3004 return null;
3005 }
3006 } else {
3007 if (toroot != "./") {
3008 toroot = "./" + toroot;
3009 }
3010 do {
3011 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
3012 var pos = full.lastIndexOf("/");
3013 file = full.substr(pos) + file;
3014 full = full.substr(0, pos);
3015 toroot = toroot.substr(0, toroot.length-3);
3016 }
3017 } while (toroot != "" && toroot != "/");
3018 return file.substr(1);
3019 }
3020}
3021
3022function find_page(url, data)
3023{
3024 var nodes = data;
3025 var result = null;
3026 for (var i in nodes) {
3027 var d = nodes[i];
3028 if (d[1] == url) {
3029 return new Array(i);
3030 }
3031 else if (d[2] != null) {
3032 result = find_page(url, d[2]);
3033 if (result != null) {
3034 return (new Array(i).concat(result));
3035 }
3036 }
3037 }
3038 return null;
3039}
3040
Scott Mainf5089842012-08-14 16:31:07 -07003041function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07003042 // load json file for navtree data
3043 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
3044 // when the file is loaded, initialize the tree
3045 if(jqxhr.status === 200) {
3046 init_navtree("tree-list", toroot, NAVTREE_DATA);
3047 }
3048 });
Scott Main3b90aff2013-08-01 18:09:35 -07003049
Scott Mainf5089842012-08-14 16:31:07 -07003050 // perform api level toggling because because the whole tree is new to the DOM
3051 var selectedLevel = $("#apiLevelSelector option:selected").val();
3052 toggleVisisbleApis(selectedLevel, "#side-nav");
3053}
3054
3055function init_navtree(navtree_id, toroot, root_nodes)
3056{
3057 var me = new Object();
3058 me.toroot = toroot;
3059 me.node = new Object();
3060
3061 me.node.li = document.getElementById(navtree_id);
3062 me.node.children_data = root_nodes;
3063 me.node.children = new Array();
3064 me.node.children_ul = document.createElement("ul");
3065 me.node.get_children_ul = function() { return me.node.children_ul; };
3066 //me.node.children_ul.className = "children_ul";
3067 me.node.li.appendChild(me.node.children_ul);
3068 me.node.depth = 0;
3069
3070 get_node(me, me.node);
3071
3072 me.this_page = this_page_relative(toroot);
3073 me.breadcrumbs = find_page(me.this_page, root_nodes);
3074 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
3075 var mom = me.node;
3076 for (var i in me.breadcrumbs) {
3077 var j = me.breadcrumbs[i];
3078 mom = mom.children[j];
3079 expand_node(me, mom);
3080 }
3081 mom.label_div.className = mom.label_div.className + " selected";
3082 addLoadEvent(function() {
3083 scrollIntoView("nav-tree");
3084 });
3085 }
3086}
3087
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003088
3089
3090
3091
3092
3093
3094
Robert Lyd2dd6e52012-11-29 21:28:48 -08003095/* TODO: eliminate redundancy with non-google functions */
3096function init_google_navtree(navtree_id, toroot, root_nodes)
3097{
3098 var me = new Object();
3099 me.toroot = toroot;
3100 me.node = new Object();
3101
3102 me.node.li = document.getElementById(navtree_id);
3103 me.node.children_data = root_nodes;
3104 me.node.children = new Array();
3105 me.node.children_ul = document.createElement("ul");
3106 me.node.get_children_ul = function() { return me.node.children_ul; };
3107 //me.node.children_ul.className = "children_ul";
3108 me.node.li.appendChild(me.node.children_ul);
3109 me.node.depth = 0;
3110
3111 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003112}
3113
3114function new_google_node(me, mom, text, link, children_data, api_level)
3115{
3116 var node = new Object();
3117 var child;
3118 node.children = Array();
3119 node.children_data = children_data;
3120 node.depth = mom.depth + 1;
3121 node.get_children_ul = function() {
3122 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003123 node.children_ul = document.createElement("ul");
3124 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003125 node.li.appendChild(node.children_ul);
3126 }
3127 return node.children_ul;
3128 };
3129 node.li = document.createElement("li");
3130
3131 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003132
3133
Robert Lyd2dd6e52012-11-29 21:28:48 -08003134 if(link) {
3135 child = document.createElement("a");
3136
3137 }
3138 else {
3139 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003140 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003141
3142 }
3143 if (children_data != null) {
3144 node.li.className="nav-section";
3145 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003146 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003147 node.li.appendChild(node.label_div);
3148 get_google_node(me, node);
3149 node.label_div.appendChild(child);
3150 }
3151 else {
3152 node.li.appendChild(child);
3153 }
3154 if(link) {
3155 child.href = me.toroot + link;
3156 }
3157 node.label = document.createTextNode(text);
3158 child.appendChild(node.label);
3159
3160 node.children_ul = null;
3161
3162 return node;
3163}
3164
3165function get_google_node(me, mom)
3166{
3167 mom.children_visited = true;
3168 var linkText;
3169 for (var i in mom.children_data) {
3170 var node_data = mom.children_data[i];
3171 linkText = node_data[0];
3172
3173 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3174 linkText = linkText.substr(19, linkText.length);
3175 }
3176 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3177 node_data[2], node_data[3]);
3178 }
3179}
Scott Mainad08f072013-08-20 16:49:57 -07003180
3181
3182
3183
3184
3185
3186/****** NEW version of script to build google and sample navs dynamically ******/
3187// TODO: update Google reference docs to tolerate this new implementation
3188
Scott Maine624b3f2013-09-12 12:56:41 -07003189var NODE_NAME = 0;
3190var NODE_HREF = 1;
3191var NODE_GROUP = 2;
3192var NODE_TAGS = 3;
3193var NODE_CHILDREN = 4;
3194
Scott Mainad08f072013-08-20 16:49:57 -07003195function init_google_navtree2(navtree_id, data)
3196{
3197 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003198 for (var i in data) {
3199 var node_data = data[i];
3200 $containerUl.append(new_google_node2(node_data));
3201 }
3202
Scott Main70557ee2013-10-30 14:47:40 -07003203 // Make all third-generation list items 'sticky' to prevent them from collapsing
3204 $containerUl.find('li li li.nav-section').addClass('sticky');
3205
Scott Mainad08f072013-08-20 16:49:57 -07003206 initExpandableNavItems("#"+navtree_id);
3207}
3208
3209function new_google_node2(node_data)
3210{
Scott Maine624b3f2013-09-12 12:56:41 -07003211 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003212 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3213 linkText = linkText.substr(19, linkText.length);
3214 }
3215 var $li = $('<li>');
3216 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003217 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003218 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3219 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003220 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003221 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3222 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003223 }
3224 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003225 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003226 $li.addClass("nav-section");
3227 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003228 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003229
Scott Maine624b3f2013-09-12 12:56:41 -07003230 for (var i in node_data[NODE_CHILDREN]) {
3231 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003232 $childUl.append(new_google_node2(child_node_data));
3233 }
3234 $li.append($childUl);
3235 }
3236 $li.prepend($a);
3237
3238 return $li;
3239}
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
Robert Lyd2dd6e52012-11-29 21:28:48 -08003251function showGoogleRefTree() {
3252 init_default_google_navtree(toRoot);
3253 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003254}
3255
3256function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003257 // load json file for navtree data
3258 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3259 // when the file is loaded, initialize the tree
3260 if(jqxhr.status === 200) {
3261 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3262 highlightSidenav();
3263 resizeNav();
3264 }
3265 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003266}
3267
3268function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003269 // load json file for navtree data
3270 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3271 // when the file is loaded, initialize the tree
3272 if(jqxhr.status === 200) {
3273 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3274 highlightSidenav();
3275 resizeNav();
3276 }
3277 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003278}
3279
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003280function showSamplesRefTree() {
3281 init_default_samples_navtree(toRoot);
3282}
3283
3284function init_default_samples_navtree(toroot) {
3285 // load json file for navtree data
3286 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3287 // when the file is loaded, initialize the tree
3288 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003289 // hack to remove the "about the samples" link then put it back in
3290 // after we nuke the list to remove the dummy static list of samples
3291 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3292 $("#nav.samples-nav").empty();
3293 $("#nav.samples-nav").append($firstLi);
3294
Scott Mainad08f072013-08-20 16:49:57 -07003295 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003296 highlightSidenav();
3297 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003298 if ($("#jd-content #samples").length) {
3299 showSamples();
3300 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003301 }
3302 });
3303}
3304
Scott Mainf5089842012-08-14 16:31:07 -07003305/* TOGGLE INHERITED MEMBERS */
3306
3307/* Toggle an inherited class (arrow toggle)
3308 * @param linkObj The link that was clicked.
3309 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3310 * 'null' to simply toggle.
3311 */
3312function toggleInherited(linkObj, expand) {
3313 var base = linkObj.getAttribute("id");
3314 var list = document.getElementById(base + "-list");
3315 var summary = document.getElementById(base + "-summary");
3316 var trigger = document.getElementById(base + "-trigger");
3317 var a = $(linkObj);
3318 if ( (expand == null && a.hasClass("closed")) || expand ) {
3319 list.style.display = "none";
3320 summary.style.display = "block";
3321 trigger.src = toRoot + "assets/images/triangle-opened.png";
3322 a.removeClass("closed");
3323 a.addClass("opened");
3324 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3325 list.style.display = "block";
3326 summary.style.display = "none";
3327 trigger.src = toRoot + "assets/images/triangle-closed.png";
3328 a.removeClass("opened");
3329 a.addClass("closed");
3330 }
3331 return false;
3332}
3333
3334/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3335 * @param linkObj The link that was clicked.
3336 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3337 * 'null' to simply toggle.
3338 */
3339function toggleAllInherited(linkObj, expand) {
3340 var a = $(linkObj);
3341 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3342 var expandos = $(".jd-expando-trigger", table);
3343 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3344 expandos.each(function(i) {
3345 toggleInherited(this, true);
3346 });
3347 a.text("[Collapse]");
3348 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3349 expandos.each(function(i) {
3350 toggleInherited(this, false);
3351 });
3352 a.text("[Expand]");
3353 }
3354 return false;
3355}
3356
3357/* Toggle all inherited members in the class (link in the class title)
3358 */
3359function toggleAllClassInherited() {
3360 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3361 var toggles = $(".toggle-all", $("#body-content"));
3362 if (a.text() == "[Expand All]") {
3363 toggles.each(function(i) {
3364 toggleAllInherited(this, true);
3365 });
3366 a.text("[Collapse All]");
3367 } else {
3368 toggles.each(function(i) {
3369 toggleAllInherited(this, false);
3370 });
3371 a.text("[Expand All]");
3372 }
3373 return false;
3374}
3375
3376/* Expand all inherited members in the class. Used when initiating page search */
3377function ensureAllInheritedExpanded() {
3378 var toggles = $(".toggle-all", $("#body-content"));
3379 toggles.each(function(i) {
3380 toggleAllInherited(this, true);
3381 });
3382 $("#toggleAllClassInherited").text("[Collapse All]");
3383}
3384
3385
3386/* HANDLE KEY EVENTS
3387 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3388 */
3389var agent = navigator['userAgent'].toLowerCase();
3390var mac = agent.indexOf("macintosh") != -1;
3391
3392$(document).keydown( function(e) {
3393var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3394 if (control && e.which == 70) { // 70 is "F"
3395 ensureAllInheritedExpanded();
3396 }
3397});
Scott Main498d7102013-08-21 15:47:38 -07003398
3399
3400
3401
3402
3403
3404/* On-demand functions */
3405
3406/** Move sample code line numbers out of PRE block and into non-copyable column */
3407function initCodeLineNumbers() {
3408 var numbers = $("#codesample-block a.number");
3409 if (numbers.length) {
3410 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3411 }
3412
3413 $(document).ready(function() {
3414 // select entire line when clicked
3415 $("span.code-line").click(function() {
3416 if (!shifted) {
3417 selectText(this);
3418 }
3419 });
3420 // invoke line link on double click
3421 $(".code-line").dblclick(function() {
3422 document.location.hash = $(this).attr('id');
3423 });
3424 // highlight the line when hovering on the number
3425 $("#codesample-line-numbers a.number").mouseover(function() {
3426 var id = $(this).attr('href');
3427 $(id).css('background','#e7e7e7');
3428 });
3429 $("#codesample-line-numbers a.number").mouseout(function() {
3430 var id = $(this).attr('href');
3431 $(id).css('background','none');
3432 });
3433 });
3434}
3435
3436// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3437var shifted = false;
3438$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3439
3440// courtesy of jasonedelman.com
3441function selectText(element) {
3442 var doc = document
3443 , range, selection
3444 ;
3445 if (doc.body.createTextRange) { //ms
3446 range = doc.body.createTextRange();
3447 range.moveToElementText(element);
3448 range.select();
3449 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003450 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003451 range = doc.createRange();
3452 range.selectNodeContents(element);
3453 selection.removeAllRanges();
3454 selection.addRange(range);
3455 }
Scott Main285f0772013-08-22 23:22:09 +00003456}
Scott Main03aca9a2013-10-31 07:20:55 -07003457
3458
3459
3460
3461/** Display links and other information about samples that match the
3462 group specified by the URL */
3463function showSamples() {
3464 var group = $("#samples").attr('class');
3465 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3466
3467 var $ul = $("<ul>");
3468 $selectedLi = $("#nav li.selected");
3469
3470 $selectedLi.children("ul").children("li").each(function() {
3471 var $li = $("<li>").append($(this).find("a").first().clone());
3472 $ul.append($li);
3473 });
3474
3475 $("#samples").append($ul);
3476
3477}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003478
3479
3480
3481/* ########################################################## */
3482/* ################### RESOURCE CARDS ##################### */
3483/* ########################################################## */
3484
3485/** Handle resource queries, collections, and grids (sections). Requires
3486 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3487
3488(function() {
3489 // Prevent the same resource from being loaded more than once per page.
3490 var addedPageResources = {};
3491
3492 $(document).ready(function() {
3493 $('.resource-widget').each(function() {
3494 initResourceWidget(this);
3495 });
3496
3497 /* Pass the line height to ellipsisfade() to adjust the height of the
3498 text container to show the max number of lines possible, without
3499 showing lines that are cut off. This works with the css ellipsis
3500 classes to fade last text line and apply an ellipsis char. */
3501
Scott Mainb16376f2014-05-21 20:35:47 -07003502 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003503 var lineHeight = 15;
3504 $('.card-info .text').ellipsisfade(lineHeight);
3505 });
3506
3507 /*
3508 Three types of resource layouts:
3509 Flow - Uses a fixed row-height flow using float left style.
3510 Carousel - Single card slideshow all same dimension absolute.
3511 Stack - Uses fixed columns and flexible element height.
3512 */
3513 function initResourceWidget(widget) {
3514 var $widget = $(widget);
3515 var isFlow = $widget.hasClass('resource-flow-layout'),
3516 isCarousel = $widget.hasClass('resource-carousel-layout'),
3517 isStack = $widget.hasClass('resource-stack-layout');
3518
3519 // find size of widget by pulling out its class name
3520 var sizeCols = 1;
3521 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3522 if (m) {
3523 sizeCols = parseInt(m[1], 10);
3524 }
3525
3526 var opts = {
3527 cardSizes: ($widget.data('cardsizes') || '').split(','),
3528 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3529 itemsPerPage: $widget.data('itemsperpage'),
3530 sortOrder: $widget.data('sortorder'),
3531 query: $widget.data('query'),
3532 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003533 sizeCols: sizeCols,
3534 /* Added by LFL 6/6/14 */
3535 resourceStyle: $widget.data('resourcestyle') || 'card',
3536 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003537 };
3538
3539 // run the search for the set of resources to show
3540
3541 var resources = buildResourceList(opts);
3542
3543 if (isFlow) {
3544 drawResourcesFlowWidget($widget, opts, resources);
3545 } else if (isCarousel) {
3546 drawResourcesCarouselWidget($widget, opts, resources);
3547 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003548 /* Looks like this got removed and is not used, so repurposing for the
3549 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003550 Modified by LFL 6/6/14
3551 */
3552 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003553 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003554 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003555 }
3556 }
3557
3558 /* Initializes a Resource Carousel Widget */
3559 function drawResourcesCarouselWidget($widget, opts, resources) {
3560 $widget.empty();
3561 var plusone = true; //always show plusone on carousel
3562
3563 $widget.addClass('resource-card slideshow-container')
3564 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3565 .append($('<a>').addClass('slideshow-next').text('Next'));
3566
3567 var css = { 'width': $widget.width() + 'px',
3568 'height': $widget.height() + 'px' };
3569
3570 var $ul = $('<ul>');
3571
3572 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003573 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003574 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003575 .decorateResourceCard(resources[i],plusone);
3576
3577 $('<li>').css(css)
3578 .append($card)
3579 .appendTo($ul);
3580 }
3581
3582 $('<div>').addClass('frame')
3583 .append($ul)
3584 .appendTo($widget);
3585
3586 $widget.dacSlideshow({
3587 auto: true,
3588 btnPrev: '.slideshow-prev',
3589 btnNext: '.slideshow-next'
3590 });
3591 };
3592
Robert Lye7eeb402014-06-03 19:35:24 -07003593 /* Initializes a Resource Card Stack Widget (column-based layout)
3594 Modified by LFL 6/6/14
3595 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003596 function drawResourcesStackWidget($widget, opts, resources, sections) {
3597 // Don't empty widget, grab all items inside since they will be the first
3598 // items stacked, followed by the resource query
3599 var plusone = true; //by default show plusone on section cards
3600 var cards = $widget.find('.resource-card').detach().toArray();
3601 var numStacks = opts.numStacks || 1;
3602 var $stacks = [];
3603 var urlString;
3604
3605 for (var i = 0; i < numStacks; ++i) {
3606 $stacks[i] = $('<div>').addClass('resource-card-stack')
3607 .appendTo($widget);
3608 }
3609
3610 var sectionResources = [];
3611
3612 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003613 if (sections) {
3614 for (var i = 0; i < sections.length; ++i) {
3615 if (!sections[i].sections || !sections[i].sections.length) {
3616 // Render it as a resource card
3617 sectionResources.push(
3618 $('<a>')
3619 .addClass('resource-card section-card')
3620 .attr('href', cleanUrl(sections[i].resource.url))
3621 .decorateResourceCard(sections[i].resource,plusone)[0]
3622 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003623
Robert Lye7eeb402014-06-03 19:35:24 -07003624 } else {
3625 cards.push(
3626 $('<div>')
3627 .addClass('resource-card section-card-menu')
3628 .decorateResourceSection(sections[i],plusone)[0]
3629 );
3630 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003631 }
3632 }
3633
3634 cards = cards.concat(sectionResources);
3635
3636 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003637 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003638
Robert Lye7eeb402014-06-03 19:35:24 -07003639 if (opts.resourceStyle.indexOf('related') > -1) {
3640 $card.addClass('related-card');
3641 }
smain@google.com95948b82014-06-16 19:24:25 -07003642
Dirk Doughertyc3921652014-05-13 16:55:26 -07003643 cards.push($card[0]);
3644 }
3645
Robert Lye7eeb402014-06-03 19:35:24 -07003646 if (opts.stackSort != 'false') {
3647 for (var i = 0; i < cards.length; ++i) {
3648 // Find the stack with the shortest height, but give preference to
3649 // left to right order.
3650 var minHeight = $stacks[0].height();
3651 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003652
Robert Lye7eeb402014-06-03 19:35:24 -07003653 for (var j = 1; j < numStacks; ++j) {
3654 var height = $stacks[j].height();
3655 if (height < minHeight - 45) {
3656 minHeight = height;
3657 minIndex = j;
3658 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003659 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003660
Robert Lye7eeb402014-06-03 19:35:24 -07003661 $stacks[minIndex].append($(cards[i]));
3662 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003663 }
3664
3665 };
smain@google.com95948b82014-06-16 19:24:25 -07003666
3667 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003668 Create a resource card using the given resource object and a list of html
3669 configured options. Returns a jquery object containing the element.
3670 */
smain@google.com95948b82014-06-16 19:24:25 -07003671 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003672 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003673
Robert Lye7eeb402014-06-03 19:35:24 -07003674 // The difference here is that generic cards are not entirely clickable
3675 // so its a div instead of an a tag, also the generic one is not given
3676 // the resource-card class so it appears with a transparent background
3677 // and can be styled in whatever way the css setup.
3678 if (opts.resourceStyle == 'generic') {
3679 $el = $('<div>')
3680 .addClass('resource')
3681 .attr('href', cleanUrl(resource.url))
3682 .decorateResource(resource, opts);
3683 } else {
3684 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003685
Robert Lye7eeb402014-06-03 19:35:24 -07003686 $el = $('<a>')
3687 .addClass(cls)
3688 .attr('href', cleanUrl(resource.url))
3689 .decorateResourceCard(resource, plusone);
3690 }
smain@google.com95948b82014-06-16 19:24:25 -07003691
Robert Lye7eeb402014-06-03 19:35:24 -07003692 return $el;
3693 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003694
3695 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3696 function drawResourcesFlowWidget($widget, opts, resources) {
3697 $widget.empty();
3698 var cardSizes = opts.cardSizes || ['6x6'];
3699 var i = 0, j = 0;
3700 var plusone = true; // by default show plusone on resource cards
3701
3702 while (i < resources.length) {
3703 var cardSize = cardSizes[j++ % cardSizes.length];
3704 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003705 // Some card sizes do not get a plusone button, such as where space is constrained
3706 // or for cards commonly embedded in docs (to improve overall page speed).
3707 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3708 (cardSize == "9x2") || (cardSize == "9x3") ||
3709 (cardSize == "12x2") || (cardSize == "12x3"));
3710
3711 // A stack has a third dimension which is the number of stacked items
3712 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3713 var stackCount = 0;
3714 var $stackDiv = null;
3715
3716 if (isStack) {
3717 // Create a stack container which should have the dimensions defined
3718 // by the product of the items inside.
3719 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3720 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3721 }
3722
3723 // Build each stack item or just a single item
3724 do {
3725 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003726
Robert Lye7eeb402014-06-03 19:35:24 -07003727 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003728
3729 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003730 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003731
Dirk Doughertyc3921652014-05-13 16:55:26 -07003732 if (isStack) {
3733 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3734 if (++stackCount == parseInt(isStack[3])) {
3735 $card.addClass('resource-card-row-stack-last');
3736 stackCount = 0;
3737 }
3738 } else {
3739 stackCount = 0;
3740 }
3741
Robert Lye7eeb402014-06-03 19:35:24 -07003742 $card.appendTo($stackDiv || $widget);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003743
3744 } while (++i < resources.length && stackCount > 0);
3745 }
3746 }
3747
3748 /* Build a site map of resources using a section as a root. */
3749 function buildSectionList(opts) {
3750 if (opts.section && SECTION_BY_ID[opts.section]) {
3751 return SECTION_BY_ID[opts.section].sections || [];
3752 }
3753 return [];
3754 }
3755
3756 function buildResourceList(opts) {
3757 var maxResults = opts.maxResults || 100;
3758
3759 var query = opts.query || '';
3760 var expressions = parseResourceQuery(query);
3761 var addedResourceIndices = {};
3762 var results = [];
3763
3764 for (var i = 0; i < expressions.length; i++) {
3765 var clauses = expressions[i];
3766
3767 // build initial set of resources from first clause
3768 var firstClause = clauses[0];
3769 var resources = [];
3770 switch (firstClause.attr) {
3771 case 'type':
3772 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3773 break;
3774 case 'lang':
3775 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3776 break;
3777 case 'tag':
3778 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3779 break;
3780 case 'collection':
3781 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3782 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3783 break;
3784 case 'section':
3785 var urls = SITE_MAP[firstClause.value].sections || [];
3786 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3787 break;
3788 }
3789 // console.log(firstClause.attr + ':' + firstClause.value);
3790 resources = resources || [];
3791
3792 // use additional clauses to filter corpus
3793 if (clauses.length > 1) {
3794 var otherClauses = clauses.slice(1);
3795 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3796 }
3797
3798 // filter out resources already added
3799 if (i > 1) {
3800 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3801 }
3802
3803 // add to list of already added indices
3804 for (var j = 0; j < resources.length; j++) {
3805 // console.log(resources[j].title);
3806 addedResourceIndices[resources[j].index] = 1;
3807 }
3808
3809 // concat to final results list
3810 results = results.concat(resources);
3811 }
3812
3813 if (opts.sortOrder && results.length) {
3814 var attr = opts.sortOrder;
3815
3816 if (opts.sortOrder == 'random') {
3817 var i = results.length, j, temp;
3818 while (--i) {
3819 j = Math.floor(Math.random() * (i + 1));
3820 temp = results[i];
3821 results[i] = results[j];
3822 results[j] = temp;
3823 }
3824 } else {
3825 var desc = attr.charAt(0) == '-';
3826 if (desc) {
3827 attr = attr.substring(1);
3828 }
3829 results = results.sort(function(x,y) {
3830 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3831 });
3832 }
3833 }
3834
3835 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3836 results = results.slice(0, maxResults);
3837
3838 for (var j = 0; j < results.length; ++j) {
3839 addedPageResources[results[j].index] = 1;
3840 }
3841
3842 return results;
3843 }
3844
3845
3846 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3847 return function(resource) {
3848 return !addedResourceIndices[resource.index];
3849 };
3850 }
3851
3852
3853 function getResourceMatchesClausesFilter(clauses) {
3854 return function(resource) {
3855 return doesResourceMatchClauses(resource, clauses);
3856 };
3857 }
3858
3859
3860 function doesResourceMatchClauses(resource, clauses) {
3861 for (var i = 0; i < clauses.length; i++) {
3862 var map;
3863 switch (clauses[i].attr) {
3864 case 'type':
3865 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3866 break;
3867 case 'lang':
3868 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3869 break;
3870 case 'tag':
3871 map = IS_RESOURCE_TAGGED[clauses[i].value];
3872 break;
3873 }
3874
3875 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3876 return clauses[i].negative;
3877 }
3878 }
3879 return true;
3880 }
smain@google.com95948b82014-06-16 19:24:25 -07003881
Robert Lye7eeb402014-06-03 19:35:24 -07003882 function cleanUrl(url)
3883 {
3884 if (url && url.indexOf('//') === -1) {
3885 url = toRoot + url;
3886 }
smain@google.com95948b82014-06-16 19:24:25 -07003887
Robert Lye7eeb402014-06-03 19:35:24 -07003888 return url;
3889 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003890
3891
3892 function parseResourceQuery(query) {
3893 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3894 var expressions = [];
3895 var expressionStrs = query.split(',') || [];
3896 for (var i = 0; i < expressionStrs.length; i++) {
3897 var expr = expressionStrs[i] || '';
3898
3899 // Break expression into clauses (clause e.g. 'tag:foo')
3900 var clauses = [];
3901 var clauseStrs = expr.split(/(?=[\+\-])/);
3902 for (var j = 0; j < clauseStrs.length; j++) {
3903 var clauseStr = clauseStrs[j] || '';
3904
3905 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3906 var parts = clauseStr.split(':');
3907 var clause = {};
3908
3909 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3910 if (clause.attr) {
3911 if (clause.attr.charAt(0) == '+') {
3912 clause.attr = clause.attr.substring(1);
3913 } else if (clause.attr.charAt(0) == '-') {
3914 clause.negative = true;
3915 clause.attr = clause.attr.substring(1);
3916 }
3917 }
3918
3919 if (parts.length > 1) {
3920 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3921 }
3922
3923 clauses.push(clause);
3924 }
3925
3926 if (!clauses.length) {
3927 continue;
3928 }
3929
3930 expressions.push(clauses);
3931 }
3932
3933 return expressions;
3934 }
3935})();
3936
3937(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003938
smain@google.com95948b82014-06-16 19:24:25 -07003939 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003940 Utility method for creating dom for the description area of a card.
3941 Used in decorateResourceCard and decorateResource.
3942 */
3943 function buildResourceCardDescription(resource, plusone) {
3944 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07003945
Robert Lye7eeb402014-06-03 19:35:24 -07003946 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07003947
Robert Lye7eeb402014-06-03 19:35:24 -07003948 if (resource.cta) {
3949 $description.append($('<a>').addClass('cta').html(resource.cta));
3950 }
smain@google.com95948b82014-06-16 19:24:25 -07003951
Robert Lye7eeb402014-06-03 19:35:24 -07003952 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07003953 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07003954 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003955
Robert Lye7eeb402014-06-03 19:35:24 -07003956 $description.append($('<div>').addClass('util')
3957 .append($('<div>').addClass('g-plusone')
3958 .attr('data-size', 'small')
3959 .attr('data-align', 'right')
3960 .attr('data-href', plusurl)));
3961 }
smain@google.com95948b82014-06-16 19:24:25 -07003962
Robert Lye7eeb402014-06-03 19:35:24 -07003963 return $description;
3964 }
smain@google.com95948b82014-06-16 19:24:25 -07003965
3966
Dirk Doughertyc3921652014-05-13 16:55:26 -07003967 /* Simple jquery function to create dom for a standard resource card */
3968 $.fn.decorateResourceCard = function(resource,plusone) {
3969 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07003970 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003971 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07003972
Robert Lye7eeb402014-06-03 19:35:24 -07003973 if (imgUrl.indexOf('//') === -1) {
3974 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003975 }
Robert Lye7eeb402014-06-03 19:35:24 -07003976
3977 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07003978 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07003979 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07003980 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07003981
Robert Lye7eeb402014-06-03 19:35:24 -07003982 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3983 .append($('<div>').addClass('section').text(section))
3984 .append($('<div>').addClass('title').html(resource.title))
3985 .append(buildResourceCardDescription(resource, plusone))
3986 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003987
3988 return this;
3989 };
3990
3991 /* Simple jquery function to create dom for a resource section card (menu) */
3992 $.fn.decorateResourceSection = function(section,plusone) {
3993 var resource = section.resource;
3994 //keep url clean for matching and offline mode handling
3995 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3996 var $base = $('<a>')
3997 .addClass('card-bg')
3998 .attr('href', resource.url)
3999 .append($('<div>').addClass('card-section-icon')
4000 .append($('<div>').addClass('icon'))
4001 .append($('<div>').addClass('section').html(resource.title)))
4002 .appendTo(this);
4003
4004 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
4005
4006 if (section.sections && section.sections.length) {
4007 // Recurse the section sub-tree to find a resource image.
4008 var stack = [section];
4009
4010 while (stack.length) {
4011 if (stack[0].resource.image) {
4012 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
4013 break;
4014 }
4015
4016 if (stack[0].sections) {
4017 stack = stack.concat(stack[0].sections);
4018 }
4019
4020 stack.shift();
4021 }
4022
4023 var $ul = $('<ul>')
4024 .appendTo($cardInfo);
4025
4026 var max = section.sections.length > 3 ? 3 : section.sections.length;
4027
4028 for (var i = 0; i < max; ++i) {
4029
4030 var subResource = section.sections[i];
4031 if (!plusone) {
4032 $('<li>')
4033 .append($('<a>').attr('href', subResource.url)
4034 .append($('<div>').addClass('title').html(subResource.title))
4035 .append($('<div>').addClass('description ellipsis')
4036 .append($('<div>').addClass('text').html(subResource.summary))
4037 .append($('<div>').addClass('util'))))
4038 .appendTo($ul);
4039 } else {
4040 $('<li>')
4041 .append($('<a>').attr('href', subResource.url)
4042 .append($('<div>').addClass('title').html(subResource.title))
4043 .append($('<div>').addClass('description ellipsis')
4044 .append($('<div>').addClass('text').html(subResource.summary))
4045 .append($('<div>').addClass('util')
4046 .append($('<div>').addClass('g-plusone')
4047 .attr('data-size', 'small')
4048 .attr('data-align', 'right')
4049 .attr('data-href', resource.url)))))
4050 .appendTo($ul);
4051 }
4052 }
4053
4054 // Add a more row
4055 if (max < section.sections.length) {
4056 $('<li>')
4057 .append($('<a>').attr('href', resource.url)
4058 .append($('<div>')
4059 .addClass('title')
4060 .text('More')))
4061 .appendTo($ul);
4062 }
4063 } else {
4064 // No sub-resources, just render description?
4065 }
4066
4067 return this;
4068 };
smain@google.com95948b82014-06-16 19:24:25 -07004069
4070
4071
4072
Robert Lye7eeb402014-06-03 19:35:24 -07004073 /* Render other types of resource styles that are not cards. */
4074 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07004075 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004076 'assets/images/resource-card-default-android.jpg';
4077 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07004078
Robert Lye7eeb402014-06-03 19:35:24 -07004079 if (imgUrl.indexOf('//') === -1) {
4080 imgUrl = toRoot + imgUrl;
4081 }
smain@google.com95948b82014-06-16 19:24:25 -07004082
Robert Lye7eeb402014-06-03 19:35:24 -07004083 if (linkUrl && linkUrl.indexOf('//') === -1) {
4084 linkUrl = toRoot + linkUrl;
4085 }
4086
4087 $(this).append(
4088 $('<div>').addClass('image')
4089 .css('background-image', 'url(' + imgUrl + ')'),
4090 $('<div>').addClass('info').append(
4091 $('<h4>').addClass('title').html(resource.title),
4092 $('<p>').addClass('summary').html(resource.summary),
4093 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4094 )
4095 );
4096
4097 return this;
4098 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004099})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004100
4101
Dirk Doughertyc3921652014-05-13 16:55:26 -07004102/* Calculate the vertical area remaining */
4103(function($) {
4104 $.fn.ellipsisfade= function(lineHeight) {
4105 this.each(function() {
4106 // get element text
4107 var $this = $(this);
4108 var remainingHeight = $this.parent().parent().height();
4109 $this.parent().siblings().each(function ()
smain@google.comc91ecb72014-06-23 10:22:23 -07004110 {
smain@google.comcda1a9a2014-06-19 17:07:46 -07004111 if ($(this).is(":visible")) {
4112 var h = $(this).height();
4113 remainingHeight = remainingHeight - h;
4114 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07004115 });
4116
4117 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4118 $this.parent().css({'height': adjustedRemainingHeight});
4119 $this.css({'height': "auto"});
4120 });
4121
4122 return this;
4123 };
4124}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004125
4126/*
4127 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004128
Robert Lye7eeb402014-06-03 19:35:24 -07004129 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004130 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004131 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004132
Robert Lye7eeb402014-06-03 19:35:24 -07004133 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004134
Robert Lye7eeb402014-06-03 19:35:24 -07004135 <div class="fullscreen-carousel">
4136 <div class="fullscreen-carousel-content">
4137 <!-- content here -->
4138 </div>
4139 <div class="fullscreen-carousel-content">
4140 <!-- content here -->
4141 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004142
Robert Lye7eeb402014-06-03 19:35:24 -07004143 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004144
Robert Lye7eeb402014-06-03 19:35:24 -07004145 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004146
Robert Lye7eeb402014-06-03 19:35:24 -07004147 Control over how the carousel takes over the screen can mostly be defined in
4148 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004149 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004150 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004151 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004152 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004153
Robert Lye7eeb402014-06-03 19:35:24 -07004154 There is limited functionality for having multiple sections since that request
4155 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4156 scroll between multiple content areas.
4157*/
4158
4159(function() {
4160 $(document).ready(function() {
4161 $('.fullscreen-carousel').each(function() {
4162 initWidget(this);
4163 });
4164 });
4165
4166 function initWidget(widget) {
4167 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004168
Robert Lye7eeb402014-06-03 19:35:24 -07004169 var topOffset = $widget.offset().top;
4170 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4171 var maxHeight = 0;
4172 var minHeight = 0;
4173 var $content = $widget.find('.fullscreen-carousel-content');
4174 var $nextArrow = $widget.find('.next-arrow');
4175 var $prevArrow = $widget.find('.prev-arrow');
4176 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004177
Robert Lye7eeb402014-06-03 19:35:24 -07004178 if ($content.length <= 1) {
4179 $nextArrow.hide();
4180 $prevArrow.hide();
4181 } else {
4182 $nextArrow.click(function() {
4183 var index = ($content.index($curSection) + 1);
4184 $curSection.hide();
4185 $curSection = $($content[index >= $content.length ? 0 : index]);
4186 $curSection.show();
4187 });
smain@google.com95948b82014-06-16 19:24:25 -07004188
Robert Lye7eeb402014-06-03 19:35:24 -07004189 $prevArrow.click(function() {
4190 var index = ($content.index($curSection) - 1);
4191 $curSection.hide();
4192 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4193 $curSection.show();
4194 });
4195 }
4196
4197 // Just hide all content sections except first.
4198 $content.each(function(index) {
4199 if ($(this).height() > minHeight) minHeight = $(this).height();
4200 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4201 });
4202
4203 // Register for changes to window size, and trigger.
4204 $(window).resize(resizeWidget);
4205 resizeWidget();
4206
4207 function resizeWidget() {
4208 var height = $(window).height() - topOffset - padBottom;
4209 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004210 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004211 (maxHeight && height > maxHeight ? maxHeight : height));
4212 }
smain@google.com95948b82014-06-16 19:24:25 -07004213 }
Robert Lye7eeb402014-06-03 19:35:24 -07004214})();
4215
4216
4217
4218
4219
4220/*
4221 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004222
Robert Lye7eeb402014-06-03 19:35:24 -07004223 The following allows tab widgets to be installed via the html below. Each
4224 tab content section should have a data-tab attribute matching one of the
4225 nav items'. Also each tab content section should have a width matching the
4226 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004227
Robert Lye7eeb402014-06-03 19:35:24 -07004228 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004229
Robert Lye7eeb402014-06-03 19:35:24 -07004230 <div class="tab-carousel">
4231 <ul class="tab-nav">
4232 <li><a href="#" data-tab="handsets">Handsets</a>
4233 <li><a href="#" data-tab="wearable">Wearable</a>
4234 <li><a href="#" data-tab="tv">TV</a>
4235 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004236
Robert Lye7eeb402014-06-03 19:35:24 -07004237 <div class="tab-carousel-content">
4238 <div data-tab="handsets">
4239 <!--Full width content here-->
4240 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004241
Robert Lye7eeb402014-06-03 19:35:24 -07004242 <div data-tab="wearable">
4243 <!--Full width content here-->
4244 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004245
Robert Lye7eeb402014-06-03 19:35:24 -07004246 <div data-tab="tv">
4247 <!--Full width content here-->
4248 </div>
4249 </div>
4250 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004251
Robert Lye7eeb402014-06-03 19:35:24 -07004252*/
4253(function() {
4254 $(document).ready(function() {
4255 $('.tab-carousel').each(function() {
4256 initWidget(this);
4257 });
4258 });
4259
4260 function initWidget(widget) {
4261 var $widget = $(widget);
4262 var $nav = $widget.find('.tab-nav');
4263 var $anchors = $nav.find('[data-tab]');
4264 var $li = $nav.find('li');
4265 var $contentContainer = $widget.find('.tab-carousel-content');
4266 var $tabs = $contentContainer.find('[data-tab]');
4267 var $curTab = $($tabs[0]); // Current tab is first tab.
4268 var width = $widget.width();
4269
4270 // Setup nav interactivity.
4271 $anchors.click(function(evt) {
4272 evt.preventDefault();
4273 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004274 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004275 });
smain@google.com95948b82014-06-16 19:24:25 -07004276
Robert Lye7eeb402014-06-03 19:35:24 -07004277 // Add highlight for navigation on first item.
4278 var $highlight = $('<div>').addClass('highlight')
4279 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4280 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004281
Robert Lye7eeb402014-06-03 19:35:24 -07004282 // Store height since we will change contents to absolute.
4283 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004284
Robert Lye7eeb402014-06-03 19:35:24 -07004285 // Absolutely position tabs so they're ready for transition.
4286 $tabs.each(function(index) {
4287 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4288 });
smain@google.com95948b82014-06-16 19:24:25 -07004289
Robert Lye7eeb402014-06-03 19:35:24 -07004290 function transitionWidget($toTab) {
4291 if (!$curTab.is($toTab)) {
4292 var curIndex = $tabs.index($curTab[0]);
4293 var toIndex = $tabs.index($toTab[0]);
4294 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004295
Robert Lye7eeb402014-06-03 19:35:24 -07004296 // Animate content sections.
4297 $toTab.css({left:(width * dir) + 'px'});
4298 $curTab.animate({left:(width * -dir) + 'px'});
4299 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004300
Robert Lye7eeb402014-06-03 19:35:24 -07004301 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004302 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004303 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004304
Robert Lye7eeb402014-06-03 19:35:24 -07004305 // Store new current section.
4306 $curTab = $toTab;
4307 }
4308 }
smain@google.com95948b82014-06-16 19:24:25 -07004309 }
Dirk Doughertyb87e3002014-11-18 19:34:34 -08004310})();