blob: b0ee2791be14c3de0061f2d6ff2ee931d9f580c5 [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");
Dirk Dougherty529a1072014-12-15 15:10:06 -0800237 } else if (secondFrag == "analyze") {
238 $("#nav-x li.analyze a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700239 } else if (secondFrag == "tools") {
240 $("#nav-x li.disttools a").addClass("selected");
241 } else if (secondFrag == "stories") {
242 $("#nav-x li.stories a").addClass("selected");
243 } else if (secondFrag == "essentials") {
244 $("#nav-x li.essentials a").addClass("selected");
245 } else if (secondFrag == "googleplay") {
246 $("#nav-x li.googleplay a").addClass("selected");
247 }
248 } else if ($("body").hasClass("about")) {
249 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700250 }
Scott Mainac2aef52013-02-12 14:15:23 -0800251
Scott Mainf6145542013-04-01 16:38:11 -0700252 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
253 // and highlight the sidenav
254 mPagePath = pagePath;
255 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700256 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800257
Scott Mainf6145542013-04-01 16:38:11 -0700258 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700259 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700260 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700261 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800262 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700263
264 // set up prev links
265 var $prevLink = [];
266 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700267
Scott Maine4d8f1b2012-06-21 18:03:05 -0700268 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
269false; // navigate across topic boundaries only in design docs
270 if ($prevListItem.length) {
smain@google.comc91ecb72014-06-23 10:22:23 -0700271 if ($prevListItem.hasClass('nav-section') || crossBoundaries) {
Scott Main5a1123e2012-09-26 12:51:28 -0700272 // jump to last topic of previous section
273 $prevLink = $prevListItem.find('a:last');
274 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700275 // jump to previous topic in this section
276 $prevLink = $prevListItem.find('a:eq(0)');
277 }
278 } else {
279 // jump to this section's index page (if it exists)
280 var $parentListItem = $selListItem.parents('li');
281 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700282
Scott Maine4d8f1b2012-06-21 18:03:05 -0700283 // except if cross boundaries aren't allowed, and we're at the top of a section already
284 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700285 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700286 && $selListItem.hasClass('nav-section')) {
287 $prevLink = [];
288 }
289 }
290
Scott Maine4d8f1b2012-06-21 18:03:05 -0700291 // set up next links
292 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700293 var startClass = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700294 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700295
Scott Main1a00f7f2013-10-29 11:11:19 -0700296 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700297 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700298 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700299
300 // if there aren't any children, go to the next section (required for About pages)
301 if($nextLink.length == 0) {
302 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700303 } else if ($('.topic-start-link').length) {
304 // as long as there's a child link and there is a "topic start link" (we're on a landing)
305 // then set the landing page "start link" text to be the first doc title
306 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700307 }
Scott Main3b90aff2013-08-01 18:09:35 -0700308
Scott Main5a1123e2012-09-26 12:51:28 -0700309 // If the selected page has a description, then it's a class or article homepage
310 if ($selListItem.find('a[description]').length) {
311 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700312 startClass = true;
313 }
314 } else {
315 // jump to the next topic in this section (if it exists)
316 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700317 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700318 isCrossingBoundary = true;
319 // no more topics in this section, jump to the first topic in the next section
smain@google.comabf34112014-06-23 11:39:02 -0700320 $nextLink = $selListItem.parents('li:eq(0)').next('li').find('a:eq(0)');
Scott Main5a1123e2012-09-26 12:51:28 -0700321 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
322 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700323 if ($nextLink.length == 0) {
324 // if that doesn't work, we're at the end of the list, so disable NEXT link
325 $('.next-page-link').attr('href','').addClass("disabled")
326 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700327 // and completely hide the one in the footer
328 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700329 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700330 }
331 }
332 }
Scott Main5a1123e2012-09-26 12:51:28 -0700333
334 if (startClass) {
335 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
336
Scott Main3b90aff2013-08-01 18:09:35 -0700337 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700338 // then we need to add a bottom border to button
339 if (!$("#tb").length) {
340 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700341 }
Scott Main5a1123e2012-09-26 12:51:28 -0700342 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
343 $('.content-footer.next-class').show();
344 $('.next-page-link').attr('href','')
345 .removeClass("hide").addClass("disabled")
346 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700347 // and completely hide the one in the footer
348 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700349 if ($nextLink.length) {
350 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700351 .removeClass("hide")
352 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700353 $('.next-class-link').find('.new').empty();
354 }
Scott Main5a1123e2012-09-26 12:51:28 -0700355 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700356 $('.next-page-link').attr('href', $nextLink.attr('href'))
357 .removeClass("hide");
358 // for the footer link, also add the next page title
359 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700360 }
361
362 if (!startClass && $prevLink.length) {
363 var prevHref = $prevLink.attr('href');
364 if (prevHref == SITE_ROOT + 'index.html') {
365 // Don't show Previous when it leads to the homepage
366 } else {
367 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
368 }
Scott Main3b90aff2013-08-01 18:09:35 -0700369 }
Scott Main5a1123e2012-09-26 12:51:28 -0700370
Scott Maine4d8f1b2012-06-21 18:03:05 -0700371 }
Scott Main3b90aff2013-08-01 18:09:35 -0700372
373
374
Scott Main5a1123e2012-09-26 12:51:28 -0700375 // Set up the course landing pages for Training with class names and descriptions
376 if ($('body.trainingcourse').length) {
377 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700378
379 // create an array for all the class descriptions
380 var $classDescriptions = new Array($classLinks.length);
381 var lang = getLangPref();
382 $classLinks.each(function(index) {
383 var langDescr = $(this).attr(lang + "-description");
384 if (typeof langDescr !== 'undefined' && langDescr !== false) {
385 // if there's a class description in the selected language, use that
386 $classDescriptions[index] = langDescr;
387 } else {
388 // otherwise, use the default english description
389 $classDescriptions[index] = $(this).attr("description");
390 }
391 });
Scott Main3b90aff2013-08-01 18:09:35 -0700392
Scott Main5a1123e2012-09-26 12:51:28 -0700393 var $olClasses = $('<ol class="class-list"></ol>');
394 var $liClass;
395 var $imgIcon;
396 var $h2Title;
397 var $pSummary;
398 var $olLessons;
399 var $liLesson;
400 $classLinks.each(function(index) {
401 $liClass = $('<li></li>');
402 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700403 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700404
Scott Main5a1123e2012-09-26 12:51:28 -0700405 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700406
Scott Main5a1123e2012-09-26 12:51:28 -0700407 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700408
Scott Main5a1123e2012-09-26 12:51:28 -0700409 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700410 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
411 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700412 $lessons.each(function(index) {
413 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
414 });
415 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700416 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
417 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700418 $pSummary.addClass('article');
419 }
420
421 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
422 $olClasses.append($liClass);
423 });
424 $('.jd-descr').append($olClasses);
425 }
426
Scott Maine4d8f1b2012-06-21 18:03:05 -0700427 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700428 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700429
Scott Main3b90aff2013-08-01 18:09:35 -0700430
Scott Maine4d8f1b2012-06-21 18:03:05 -0700431 $(".scroll-pane").scroll(function(event) {
432 event.preventDefault();
433 return false;
434 });
435
436 /* Resize nav height when window height changes */
437 $(window).resize(function() {
438 if ($('#side-nav').length == 0) return;
439 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
440 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
441 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700442 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700443 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
444 updateSideNavPosition();
445 } else {
446 updateSidenavFullscreenWidth();
447 }
448 }
449 resizeNav();
450 });
451
452
Scott Maine4d8f1b2012-06-21 18:03:05 -0700453 var navBarLeftPos;
454 if ($('#devdoc-nav').length) {
455 setNavBarLeftPos();
456 }
457
458
Scott Maine4d8f1b2012-06-21 18:03:05 -0700459 // Set up play-on-hover <video> tags.
460 $('video.play-on-hover').bind('click', function(){
461 $(this).get(0).load(); // in case the video isn't seekable
462 $(this).get(0).play();
463 });
464
465 // Set up tooltips
466 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700467 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700468 var $target = $(this);
469 var $tooltip = $('<div>')
470 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700471 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700472 .hide()
473 .appendTo('body');
474 $target.removeAttr('title');
475
476 $target.hover(function() {
477 // in
478 var targetRect = $target.offset();
479 targetRect.width = $target.width();
480 targetRect.height = $target.height();
481
482 $tooltip.css({
483 left: targetRect.left,
484 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
485 });
486 $tooltip.addClass('below');
487 $tooltip.show();
488 }, function() {
489 // out
490 $tooltip.hide();
491 });
492 });
493
494 // Set up <h2> deeplinks
495 $('h2').click(function() {
496 var id = $(this).attr('id');
497 if (id) {
498 document.location.hash = id;
499 }
500 });
501
502 //Loads the +1 button
503 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
504 po.src = 'https://apis.google.com/js/plusone.js';
505 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
506
507
Scott Main3b90aff2013-08-01 18:09:35 -0700508 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700509 // which avoids the visible width from changing each time the bar appears
510 var $sidenav = $("#side-nav");
511 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700512
Scott Maine4d8f1b2012-06-21 18:03:05 -0700513 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
514
515
516 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700517
Scott Maine4d8f1b2012-06-21 18:03:05 -0700518 if ($(".scroll-pane").length > 1) {
519 // Check if there's a user preference for the panel heights
520 var cookieHeight = readCookie("reference_height");
521 if (cookieHeight) {
522 restoreHeight(cookieHeight);
523 }
524 }
Scott Main3b90aff2013-08-01 18:09:35 -0700525
Scott Main06f3f2c2014-05-30 11:23:00 -0700526 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700527 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700528 // Check if there's an anchor that we need to scroll into view.
529 // A delay is needed, because some browsers do not immediately scroll down to the anchor
530 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700531
Scott Main015d6162013-01-29 09:01:52 -0800532 /* init the language selector based on user cookie for lang */
533 loadLangPref();
534 changeNavLang(getLangPref());
535
536 /* setup event handlers to ensure the overflow menu is visible while picking lang */
537 $("#language select")
538 .mousedown(function() {
539 $("div.morehover").addClass("hover"); })
540 .blur(function() {
541 $("div.morehover").removeClass("hover"); });
542
543 /* some global variable setup */
544 resizePackagesNav = $("#resize-packages-nav");
545 classesNav = $("#classes-nav");
546 devdocNav = $("#devdoc-nav");
547
548 var cookiePath = "";
549 if (location.href.indexOf("/reference/") != -1) {
550 cookiePath = "reference_";
551 } else if (location.href.indexOf("/guide/") != -1) {
552 cookiePath = "guide_";
553 } else if (location.href.indexOf("/tools/") != -1) {
554 cookiePath = "tools_";
555 } else if (location.href.indexOf("/training/") != -1) {
556 cookiePath = "training_";
557 } else if (location.href.indexOf("/design/") != -1) {
558 cookiePath = "design_";
559 } else if (location.href.indexOf("/distribute/") != -1) {
560 cookiePath = "distribute_";
561 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700562
smain@google.com698fff02014-11-20 20:39:33 -0800563
564 /* setup shadowbox for any videos that want it */
smain@google.comf75ee212014-11-24 09:42:59 -0800565 var $videoLinks = $("a.video-shadowbox-button, a.notice-developers-video");
smain@google.com698fff02014-11-20 20:39:33 -0800566 if ($videoLinks.length) {
567 // if there's at least one, add the shadowbox HTML to the body
568 $('body').prepend(
569'<div id="video-container">'+
570 '<div id="video-frame">'+
571 '<div class="video-close">'+
572 '<span id="icon-video-close" onclick="closeVideo()">&nbsp;</span>'+
573 '</div>'+
574 '<div id="youTubePlayer"></div>'+
575 '</div>'+
576'</div>');
577
578 // loads the IFrame Player API code asynchronously.
579 $.getScript("https://www.youtube.com/iframe_api");
580
581 $videoLinks.each(function() {
582 var videoId = $(this).attr('href').split('?v=')[1];
583 $(this).click(function(event) {
584 event.preventDefault();
585 startYouTubePlayer(videoId);
586 });
587 });
smain@google.com698fff02014-11-20 20:39:33 -0800588 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700589});
Scott Main7e447ed2013-02-19 17:22:37 -0800590// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700591
592
smain@google.com698fff02014-11-20 20:39:33 -0800593var youTubePlayer;
594function onYouTubeIframeAPIReady() {
595}
596
smain@google.com3de83c12014-12-12 19:06:52 -0800597/* Returns the height the shadowbox video should be. It's based on the current
598 height of the "video-frame" element, which is 100% height for the window.
599 Then minus the margin so the video isn't actually the full window height. */
600function getVideoHeight() {
601 var frameHeight = $("#video-frame").height();
602 var marginTop = $("#video-frame").css('margin-top').split('px')[0];
603 return frameHeight - (marginTop * 2);
604}
605
smain@google.com698fff02014-11-20 20:39:33 -0800606function startYouTubePlayer(videoId) {
smain@google.com3de83c12014-12-12 19:06:52 -0800607 $("#video-container").show();
608 $("#video-frame").show();
609
610 // compute the size of the player so it's centered in window
611 var maxWidth = 940; // the width of the web site content
612 var videoAspect = .5625; // based on 1280x720 resolution
613 var maxHeight = maxWidth * videoAspect;
614 var videoHeight = getVideoHeight();
615 var videoWidth = videoHeight / videoAspect;
616 if (videoWidth > maxWidth) {
617 videoWidth = maxWidth;
618 videoHeight = maxHeight;
smain@google.com570c2212014-11-25 19:01:40 -0800619 }
smain@google.com3de83c12014-12-12 19:06:52 -0800620 $("#video-frame").css('width', videoWidth);
621
622 // check if we've already created this player
smain@google.com698fff02014-11-20 20:39:33 -0800623 if (youTubePlayer == null) {
smain@google.com3de83c12014-12-12 19:06:52 -0800624 // check if there's a start time specified
625 var idAndHash = videoId.split("#");
626 var startTime = 0;
627 if (idAndHash.length > 1) {
628 startTime = idAndHash[1].split("t=")[1] != undefined ? idAndHash[1].split("t=")[1] : 0;
629 }
630 // enable localized player
631 var lang = getLangPref();
632 var captionsOn = lang == 'en' ? 0 : 1;
633
smain@google.com698fff02014-11-20 20:39:33 -0800634 youTubePlayer = new YT.Player('youTubePlayer', {
smain@google.com3de83c12014-12-12 19:06:52 -0800635 height: videoHeight,
636 width: videoWidth,
smain@google.com570c2212014-11-25 19:01:40 -0800637 videoId: idAndHash[0],
smain@google.com481f15c2014-12-12 11:57:27 -0800638 playerVars: {start: startTime, hl: lang, cc_load_policy: captionsOn},
smain@google.com698fff02014-11-20 20:39:33 -0800639 events: {
smain@google.comf75ee212014-11-24 09:42:59 -0800640 'onReady': onPlayerReady,
641 'onStateChange': onPlayerStateChange
smain@google.com698fff02014-11-20 20:39:33 -0800642 }
643 });
644 } else {
smain@google.com3de83c12014-12-12 19:06:52 -0800645 // reset the size in case the user adjusted the window since last play
646 youTubePlayer.setSize(videoWidth, videoHeight);
smain@google.com94e0b532014-12-16 19:07:08 -0800647 // if a video different from the one already playing was requested, cue it up
648 if (videoId != youTubePlayer.getVideoUrl().split('?v=')[1]) {
649 youTubePlayer.cueVideoById(videoId);
650 }
smain@google.com698fff02014-11-20 20:39:33 -0800651 youTubePlayer.playVideo();
652 }
smain@google.com698fff02014-11-20 20:39:33 -0800653}
654
655function onPlayerReady(event) {
656 event.target.playVideo();
smain@google.comd24088c2014-12-12 11:31:13 -0800657 // track the start playing event so we know from which page the video was selected
658 ga('send', 'event', 'Videos', 'Start: ' +
659 youTubePlayer.getVideoUrl().split('?v=')[1], 'on: ' + document.location.href);
smain@google.com698fff02014-11-20 20:39:33 -0800660}
661
662function closeVideo() {
663 try {
smain@google.comf75ee212014-11-24 09:42:59 -0800664 youTubePlayer.pauseVideo();
smain@google.com698fff02014-11-20 20:39:33 -0800665 } catch(e) {
666 console.log('Video not available');
smain@google.com698fff02014-11-20 20:39:33 -0800667 }
smain@google.com3de83c12014-12-12 19:06:52 -0800668 $("#video-container").fadeOut(200);
smain@google.com698fff02014-11-20 20:39:33 -0800669}
670
smain@google.comf75ee212014-11-24 09:42:59 -0800671/* Track youtube playback for analytics */
672function onPlayerStateChange(event) {
673 // Video starts, send the video ID
674 if (event.data == YT.PlayerState.PLAYING) {
smain@google.comd24088c2014-12-12 11:31:13 -0800675 ga('send', 'event', 'Videos', 'Play',
676 youTubePlayer.getVideoUrl().split('?v=')[1]);
smain@google.comf75ee212014-11-24 09:42:59 -0800677 }
678 // Video paused, send video ID and video elapsed time
679 if (event.data == YT.PlayerState.PAUSED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800680 ga('send', 'event', 'Videos', 'Paused',
681 youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
smain@google.comf75ee212014-11-24 09:42:59 -0800682 }
683 // Video finished, send video ID and video elapsed time
684 if (event.data == YT.PlayerState.ENDED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800685 ga('send', 'event', 'Videos', 'Finished',
686 youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
smain@google.comf75ee212014-11-24 09:42:59 -0800687 }
688}
689
smain@google.com698fff02014-11-20 20:39:33 -0800690
691
Scott Mainad08f072013-08-20 16:49:57 -0700692function initExpandableNavItems(rootTag) {
693 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
694 var section = $(this).closest('li.nav-section');
695 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700696 /* hide me and descendants */
697 section.find('ul').slideUp(250, function() {
698 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700699 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700700 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700701 resizeNav();
702 });
703 } else {
704 /* show me */
705 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700706 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700707 $others.removeClass('expanded').children('ul').slideUp(250);
708
709 // now expand me
710 section.closest('li').addClass('expanded');
711 section.children('ul').slideDown(250, function() {
712 resizeNav();
713 });
714 }
715 });
Scott Mainf0093852013-08-22 11:37:11 -0700716
717 // Stop expand/collapse behavior when clicking on nav section links
718 // (since we're navigating away from the page)
719 // This selector captures the first instance of <a>, but not those with "#" as the href.
720 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
721 window.location.href = $(this).attr('href');
722 return false;
723 });
Scott Mainad08f072013-08-20 16:49:57 -0700724}
725
Dirk Doughertyc3921652014-05-13 16:55:26 -0700726
727/** Create the list of breadcrumb links in the sticky header */
728function buildBreadcrumbs() {
729 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
730 // Add the secondary horizontal nav item, if provided
731 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
732 if ($selectedSecondNav.length) {
733 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
734 }
735 // Add the primary horizontal nav
736 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
737 // If there's no header nav item, use the logo link and title from alt text
738 if ($selectedFirstNav.length < 1) {
739 $selectedFirstNav = $("<a>")
740 .attr('href', $("div#header .logo a").attr('href'))
741 .text($("div#header .logo img").attr('alt'));
742 }
743 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
744}
745
746
747
Scott Maine624b3f2013-09-12 12:56:41 -0700748/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700749function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700750 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
751 if ($("ul#nav li.selected").length) {
752 unHighlightSidenav();
753 }
754 // look for URL in sidenav, including the hash
755 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
756
757 // If the selNavLink is still empty, look for it without the hash
758 if ($selNavLink.length == 0) {
759 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
760 }
761
Scott Mainf6145542013-04-01 16:38:11 -0700762 var $selListItem;
763 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700764 // Find this page's <li> in sidenav and set selected
765 $selListItem = $selNavLink.closest('li');
766 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700767
Scott Mainf6145542013-04-01 16:38:11 -0700768 // Traverse up the tree and expand all parent nav-sections
769 $selNavLink.parents('li.nav-section').each(function() {
770 $(this).addClass('expanded');
771 $(this).children('ul').show();
772 });
773 }
774}
775
Scott Maine624b3f2013-09-12 12:56:41 -0700776function unHighlightSidenav() {
777 $("ul#nav li.selected").removeClass("selected");
778 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
779}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700780
781function toggleFullscreen(enable) {
782 var delay = 20;
783 var enabled = true;
784 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
785 if (enable) {
786 // Currently NOT USING fullscreen; enable fullscreen
787 stylesheet.removeAttr('disabled');
788 $('#nav-swap .fullscreen').removeClass('disabled');
789 $('#devdoc-nav').css({left:''});
790 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
791 enabled = true;
792 } else {
793 // Currently USING fullscreen; disable fullscreen
794 stylesheet.attr('disabled', 'disabled');
795 $('#nav-swap .fullscreen').addClass('disabled');
796 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
797 enabled = false;
798 }
smain@google.com6bdcb982014-11-14 11:53:07 -0800799 writeCookie("fullscreen", enabled, null);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700800 setNavBarLeftPos();
801 resizeNav(delay);
802 updateSideNavPosition();
803 setTimeout(initSidenavHeightResize,delay);
804}
805
806
807function setNavBarLeftPos() {
808 navBarLeftPos = $('#body-content').offset().left;
809}
810
811
812function updateSideNavPosition() {
813 var newLeft = $(window).scrollLeft() - navBarLeftPos;
814 $('#devdoc-nav').css({left: -newLeft});
815 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
816}
Scott Main3b90aff2013-08-01 18:09:35 -0700817
Scott Maine4d8f1b2012-06-21 18:03:05 -0700818// TODO: use $(document).ready instead
819function addLoadEvent(newfun) {
820 var current = window.onload;
821 if (typeof window.onload != 'function') {
822 window.onload = newfun;
823 } else {
824 window.onload = function() {
825 current();
826 newfun();
827 }
828 }
829}
830
831var agent = navigator['userAgent'].toLowerCase();
832// If a mobile phone, set flag and do mobile setup
833if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
834 (agent.indexOf("blackberry") != -1) ||
835 (agent.indexOf("webos") != -1) ||
836 (agent.indexOf("mini") != -1)) { // opera mini browsers
837 isMobile = true;
838}
839
840
Scott Main498d7102013-08-21 15:47:38 -0700841$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700842 $("pre:not(.no-pretty-print)").addClass("prettyprint");
843 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700844});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700845
Scott Maine4d8f1b2012-06-21 18:03:05 -0700846
847
848
849/* ######### RESIZE THE SIDENAV HEIGHT ########## */
850
851function resizeNav(delay) {
852 var $nav = $("#devdoc-nav");
853 var $window = $(window);
854 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700855
Scott Maine4d8f1b2012-06-21 18:03:05 -0700856 // Get the height of entire window and the total header height.
857 // Then figure out based on scroll position whether the header is visible
858 var windowHeight = $window.height();
859 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700860 var headerHeight = $('#header-wrapper').outerHeight();
861 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700862
863 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700864 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700865 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700866 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700867
Scott Maine4d8f1b2012-06-21 18:03:05 -0700868 // Depending on whether the header is visible, set the side nav's height.
869 if (headerVisible) {
870 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700871 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700872 } else {
873 // Once header is off screen, the nav height is almost full window height
874 navHeight = windowHeight - topMargin;
875 }
Scott Main3b90aff2013-08-01 18:09:35 -0700876
877
878
Scott Maine4d8f1b2012-06-21 18:03:05 -0700879 $scrollPanes = $(".scroll-pane");
880 if ($scrollPanes.length > 1) {
881 // subtract the height of the api level widget and nav swapper from the available nav height
882 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700883
Scott Maine4d8f1b2012-06-21 18:03:05 -0700884 $("#swapper").css({height:navHeight + "px"});
885 if ($("#nav-tree").is(":visible")) {
886 $("#nav-tree").css({height:navHeight});
887 }
Scott Main3b90aff2013-08-01 18:09:35 -0700888
889 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700890 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700891
892 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700893 // then the package panel should begin to shrink
894 if (parseInt(classesHeight) <= 0) {
895 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
896 $("#packages-nav").css({height:navHeight - 10});
897 }
Scott Main3b90aff2013-08-01 18:09:35 -0700898
Scott Maine4d8f1b2012-06-21 18:03:05 -0700899 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
900 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700901
902
Scott Maine4d8f1b2012-06-21 18:03:05 -0700903 } else {
904 $nav.height(navHeight);
905 }
Scott Main3b90aff2013-08-01 18:09:35 -0700906
Scott Maine4d8f1b2012-06-21 18:03:05 -0700907 if (delay) {
908 updateFromResize = true;
909 delayedReInitScrollbars(delay);
910 } else {
911 reInitScrollbars();
912 }
Scott Main3b90aff2013-08-01 18:09:35 -0700913
Scott Maine4d8f1b2012-06-21 18:03:05 -0700914}
915
916var updateScrollbars = false;
917var updateFromResize = false;
918
919/* Re-initialize the scrollbars to account for changed nav size.
920 * This method postpones the actual update by a 1/4 second in order to optimize the
921 * scroll performance while the header is still visible, because re-initializing the
922 * scroll panes is an intensive process.
923 */
924function delayedReInitScrollbars(delay) {
925 // If we're scheduled for an update, but have received another resize request
926 // before the scheduled resize has occured, just ignore the new request
927 // (and wait for the scheduled one).
928 if (updateScrollbars && updateFromResize) {
929 updateFromResize = false;
930 return;
931 }
Scott Main3b90aff2013-08-01 18:09:35 -0700932
Scott Maine4d8f1b2012-06-21 18:03:05 -0700933 // We're scheduled for an update and the update request came from this method's setTimeout
934 if (updateScrollbars && !updateFromResize) {
935 reInitScrollbars();
936 updateScrollbars = false;
937 } else {
938 updateScrollbars = true;
939 updateFromResize = false;
940 setTimeout('delayedReInitScrollbars()',delay);
941 }
942}
943
944/* Re-initialize the scrollbars to account for changed nav size. */
945function reInitScrollbars() {
946 var pane = $(".scroll-pane").each(function(){
947 var api = $(this).data('jsp');
948 if (!api) { setTimeout(reInitScrollbars,300); return;}
949 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700950 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700951 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
952}
953
954
955/* Resize the height of the nav panels in the reference,
956 * and save the new size to a cookie */
957function saveNavPanels() {
958 var basePath = getBaseUri(location.pathname);
959 var section = basePath.substring(1,basePath.indexOf("/",1));
smain@google.com6bdcb982014-11-14 11:53:07 -0800960 writeCookie("height", resizePackagesNav.css("height"), section);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700961}
962
963
964
965function restoreHeight(packageHeight) {
966 $("#resize-packages-nav").height(packageHeight);
967 $("#packages-nav").height(packageHeight);
968 // var classesHeight = navHeight - packageHeight;
969 // $("#classes-nav").css({height:classesHeight});
970 // $("#classes-nav .jspContainer").css({height:classesHeight});
971}
972
973
974
975/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
976
977
978
979
980
Scott Main3b90aff2013-08-01 18:09:35 -0700981/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700982 This is called when the page finished loading. */
983function scrollIntoView(nav) {
984 var $nav = $("#"+nav);
985 var element = $nav.jScrollPane({/* ...settings... */});
986 var api = element.data('jsp');
987
988 if ($nav.is(':visible')) {
989 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700990 if ($selected.length == 0) {
991 // If no selected item found, exit
992 return;
993 }
Scott Main52dd2062013-08-15 12:22:28 -0700994 // get the selected item's offset from its container nav by measuring the item's offset
995 // relative to the document then subtract the container nav's offset relative to the document
996 var selectedOffset = $selected.offset().top - $nav.offset().top;
997 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
998 // if it's more than 80% down the nav
999 // scroll the item up by an amount equal to 80% the container nav's height
1000 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001001 }
1002 }
1003}
1004
1005
1006
1007
1008
1009
1010/* Show popup dialogs */
1011function showDialog(id) {
1012 $dialog = $("#"+id);
1013 $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>');
1014 $dialog.wrapInner('<div/>');
1015 $dialog.removeClass("hide");
1016}
1017
1018
1019
1020
1021
1022/* ######### COOKIES! ########## */
1023
1024function readCookie(cookie) {
1025 var myCookie = cookie_namespace+"_"+cookie+"=";
1026 if (document.cookie) {
1027 var index = document.cookie.indexOf(myCookie);
1028 if (index != -1) {
1029 var valStart = index + myCookie.length;
1030 var valEnd = document.cookie.indexOf(";", valStart);
1031 if (valEnd == -1) {
1032 valEnd = document.cookie.length;
1033 }
1034 var val = document.cookie.substring(valStart, valEnd);
1035 return val;
1036 }
1037 }
1038 return 0;
1039}
1040
smain@google.com6bdcb982014-11-14 11:53:07 -08001041function writeCookie(cookie, val, section) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001042 if (val==undefined) return;
1043 section = section == null ? "_" : "_"+section+"_";
smain@google.com6bdcb982014-11-14 11:53:07 -08001044 var age = 2*365*24*60*60; // set max-age to 2 years
Scott Main3b90aff2013-08-01 18:09:35 -07001045 var cookieValue = cookie_namespace + section + cookie + "=" + val
smain@google.com80e38f42014-11-03 10:47:12 -08001046 + "; max-age=" + age +"; path=/";
Scott Maine4d8f1b2012-06-21 18:03:05 -07001047 document.cookie = cookieValue;
1048}
1049
1050/* ######### END COOKIES! ########## */
1051
1052
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001053var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -07001054var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001055var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -07001056/* Sets the vertical scoll position at which the sticky bar should appear.
1057 This method is called to reset the position when search results appear or hide */
1058function setStickyTop() {
1059 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
1060}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001061
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001062/*
Scott Mainb16376f2014-05-21 20:35:47 -07001063 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -07001064 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001065$(window).scroll(function(event) {
1066
1067 setStickyTop();
1068 var hiding = false;
1069 var $stickyEl = $('#sticky-header');
1070 var $menuEl = $('.menu-container');
1071 // Exit if there's no sidenav
1072 if ($('#side-nav').length == 0) return;
1073 // Exit if the mouse target is a DIV, because that means the event is coming
1074 // from a scrollable div and so there's no need to make adjustments to our layout
1075 if ($(event.target).nodeName == "DIV") {
1076 return;
1077 }
1078
1079 var top = $(window).scrollTop();
1080 // we set the navbar fixed when the scroll position is beyond the height of the site header...
1081 var shouldBeSticky = top >= stickyTop;
1082 // ... except if the document content is shorter than the sidenav height.
1083 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
1084 if ($("#doc-col").height() < $("#side-nav").height()) {
1085 shouldBeSticky = false;
1086 }
Scott Mainf5257812014-05-22 17:26:38 -07001087 // Account for horizontal scroll
1088 var scrollLeft = $(window).scrollLeft();
1089 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
1090 if (sticky && (scrollLeft != prevScrollLeft)) {
1091 updateSideNavPosition();
1092 prevScrollLeft = scrollLeft;
1093 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001094
1095 // Don't continue if the header is sufficently far away
1096 // (to avoid intensive resizing that slows scrolling)
1097 if (sticky == shouldBeSticky) {
1098 return;
1099 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001100
1101 // If sticky header visible and position is now near top, hide sticky
1102 if (sticky && !shouldBeSticky) {
1103 sticky = false;
1104 hiding = true;
1105 // make the sidenav static again
1106 $('#devdoc-nav')
1107 .removeClass('fixed')
1108 .css({'width':'auto','margin':''})
1109 .prependTo('#side-nav');
1110 // delay hide the sticky
1111 $menuEl.removeClass('sticky-menu');
1112 $stickyEl.fadeOut(250);
1113 hiding = false;
1114
1115 // update the sidenaav position for side scrolling
1116 updateSideNavPosition();
1117 } else if (!sticky && shouldBeSticky) {
1118 sticky = true;
1119 $stickyEl.fadeIn(10);
1120 $menuEl.addClass('sticky-menu');
1121
1122 // make the sidenav fixed
1123 var width = $('#devdoc-nav').width();
1124 $('#devdoc-nav')
1125 .addClass('fixed')
1126 .css({'width':width+'px'})
1127 .prependTo('#body-content');
1128
1129 // update the sidenaav position for side scrolling
1130 updateSideNavPosition();
1131
1132 } else if (hiding && top < 15) {
1133 $menuEl.removeClass('sticky-menu');
1134 $stickyEl.hide();
1135 hiding = false;
1136 }
1137 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
1138});
1139
1140/*
1141 * Manages secion card states and nav resize to conclude loading
1142 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001143(function() {
1144 $(document).ready(function() {
1145
Dirk Doughertyc3921652014-05-13 16:55:26 -07001146 // Stack hover states
1147 $('.section-card-menu').each(function(index, el) {
1148 var height = $(el).height();
1149 $(el).css({height:height+'px', position:'relative'});
1150 var $cardInfo = $(el).find('.card-info');
1151
1152 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1153 });
1154
Dirk Doughertyc3921652014-05-13 16:55:26 -07001155 });
1156
1157})();
1158
Scott Maine4d8f1b2012-06-21 18:03:05 -07001159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
Scott Maind7026f72013-06-17 15:08:49 -07001172/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001173
1174
1175
1176
1177
1178function toggle(obj, slide) {
1179 var ul = $("ul:first", obj);
1180 var li = ul.parent();
1181 if (li.hasClass("closed")) {
1182 if (slide) {
1183 ul.slideDown("fast");
1184 } else {
1185 ul.show();
1186 }
1187 li.removeClass("closed");
1188 li.addClass("open");
1189 $(".toggle-img", li).attr("title", "hide pages");
1190 } else {
1191 ul.slideUp("fast");
1192 li.removeClass("open");
1193 li.addClass("closed");
1194 $(".toggle-img", li).attr("title", "show pages");
1195 }
1196}
1197
1198
Scott Maine4d8f1b2012-06-21 18:03:05 -07001199function buildToggleLists() {
1200 $(".toggle-list").each(
1201 function(i) {
1202 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1203 $(this).addClass("closed");
1204 });
1205}
1206
1207
1208
Scott Maind7026f72013-06-17 15:08:49 -07001209function hideNestedItems(list, toggle) {
1210 $list = $(list);
1211 // hide nested lists
1212 if($list.hasClass('showing')) {
1213 $("li ol", $list).hide('fast');
1214 $list.removeClass('showing');
1215 // show nested lists
1216 } else {
1217 $("li ol", $list).show('fast');
1218 $list.addClass('showing');
1219 }
1220 $(".more,.less",$(toggle)).toggle();
1221}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001222
1223
smain@google.com95948b82014-06-16 19:24:25 -07001224/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1225function setupIdeDocToggle() {
1226 $( "select.ide" ).change(function() {
1227 var selected = $(this).find("option:selected").attr("value");
1228 $(".select-ide").hide();
1229 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001230
smain@google.com95948b82014-06-16 19:24:25 -07001231 $("select.ide").val(selected);
1232 });
1233}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258/* REFERENCE NAV SWAP */
1259
1260
1261function getNavPref() {
1262 var v = readCookie('reference_nav');
1263 if (v != NAV_PREF_TREE) {
1264 v = NAV_PREF_PANELS;
1265 }
1266 return v;
1267}
1268
1269function chooseDefaultNav() {
1270 nav_pref = getNavPref();
1271 if (nav_pref == NAV_PREF_TREE) {
1272 $("#nav-panels").toggle();
1273 $("#panel-link").toggle();
1274 $("#nav-tree").toggle();
1275 $("#tree-link").toggle();
1276 }
1277}
1278
1279function swapNav() {
1280 if (nav_pref == NAV_PREF_TREE) {
1281 nav_pref = NAV_PREF_PANELS;
1282 } else {
1283 nav_pref = NAV_PREF_TREE;
1284 init_default_navtree(toRoot);
1285 }
smain@google.com6bdcb982014-11-14 11:53:07 -08001286 writeCookie("nav", nav_pref, "reference");
Scott Maine4d8f1b2012-06-21 18:03:05 -07001287
1288 $("#nav-panels").toggle();
1289 $("#panel-link").toggle();
1290 $("#nav-tree").toggle();
1291 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001292
Scott Maine4d8f1b2012-06-21 18:03:05 -07001293 resizeNav();
1294
1295 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1296 $("#nav-tree .jspContainer:visible")
1297 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1298 // Another nasty hack to make the scrollbar appear now that we have height
1299 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001300
Scott Maine4d8f1b2012-06-21 18:03:05 -07001301 if ($("#nav-tree").is(':visible')) {
1302 scrollIntoView("nav-tree");
1303 } else {
1304 scrollIntoView("packages-nav");
1305 scrollIntoView("classes-nav");
1306 }
1307}
1308
1309
1310
Scott Mainf5089842012-08-14 16:31:07 -07001311/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001312/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001313/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001314
1315function getBaseUri(uri) {
1316 var intlUrl = (uri.substring(0,6) == "/intl/");
1317 if (intlUrl) {
1318 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1319 base = base.substring(base.indexOf('/')+1, base.length);
1320 //alert("intl, returning base url: /" + base);
1321 return ("/" + base);
1322 } else {
1323 //alert("not intl, returning uri as found.");
1324 return uri;
1325 }
1326}
1327
1328function requestAppendHL(uri) {
1329//append "?hl=<lang> to an outgoing request (such as to blog)
1330 var lang = getLangPref();
1331 if (lang) {
1332 var q = 'hl=' + lang;
1333 uri += '?' + q;
1334 window.location = uri;
1335 return false;
1336 } else {
1337 return true;
1338 }
1339}
1340
1341
Scott Maine4d8f1b2012-06-21 18:03:05 -07001342function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001343 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1344 $links.each(function(i){ // for each link with a translation
1345 var $link = $(this);
1346 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1347 // put the desired language from the attribute as the text
1348 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001349 }
Scott Main6eb95f12012-10-02 17:12:23 -07001350 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001351}
1352
Scott Main015d6162013-01-29 09:01:52 -08001353function changeLangPref(lang, submit) {
smain@google.com6bdcb982014-11-14 11:53:07 -08001354 writeCookie("pref_lang", lang, null);
Scott Main015d6162013-01-29 09:01:52 -08001355
1356 // ####### TODO: Remove this condition once we're stable on devsite #######
1357 // This condition is only needed if we still need to support legacy GAE server
1358 if (devsite) {
1359 // Switch language when on Devsite server
1360 if (submit) {
1361 $("#setlang").submit();
1362 }
1363 } else {
1364 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001365 if (submit) {
1366 window.location = getBaseUri(location.pathname);
1367 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001368 }
1369}
1370
1371function loadLangPref() {
1372 var lang = readCookie("pref_lang");
1373 if (lang != 0) {
1374 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1375 }
1376}
1377
1378function getLangPref() {
1379 var lang = $("#language").find(":selected").attr("value");
1380 if (!lang) {
1381 lang = readCookie("pref_lang");
1382 }
1383 return (lang != 0) ? lang : 'en';
1384}
1385
1386/* ########## END LOCALIZATION ############ */
1387
1388
1389
1390
1391
1392
1393/* Used to hide and reveal supplemental content, such as long code samples.
1394 See the companion CSS in android-developer-docs.css */
1395function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001396 var div = $(obj).closest(".toggle-content");
1397 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001398 if (div.hasClass("closed")) { // if it's closed, open it
1399 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001400 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001401 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001402 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001403 + "assets/images/triangle-opened.png");
1404 } else { // if it's open, close it
1405 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001406 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001407 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001408 div.find(".toggle-content").removeClass("open").addClass("closed")
1409 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001410 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001411 + "assets/images/triangle-closed.png");
1412 });
1413 }
1414 return false;
1415}
Scott Mainf5089842012-08-14 16:31:07 -07001416
1417
Scott Maindb3678b2012-10-23 14:13:41 -07001418/* New version of expandable content */
1419function toggleExpandable(link,id) {
1420 if($(id).is(':visible')) {
1421 $(id).slideUp();
1422 $(link).removeClass('expanded');
1423 } else {
1424 $(id).slideDown();
1425 $(link).addClass('expanded');
1426 }
1427}
1428
1429function hideExpandable(ids) {
1430 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001431 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001432}
1433
Scott Mainf5089842012-08-14 16:31:07 -07001434
1435
1436
1437
Scott Main3b90aff2013-08-01 18:09:35 -07001438/*
Scott Mainf5089842012-08-14 16:31:07 -07001439 * Slideshow 1.0
1440 * Used on /index.html and /develop/index.html for carousel
1441 *
1442 * Sample usage:
1443 * HTML -
1444 * <div class="slideshow-container">
1445 * <a href="" class="slideshow-prev">Prev</a>
1446 * <a href="" class="slideshow-next">Next</a>
1447 * <ul>
1448 * <li class="item"><img src="images/marquee1.jpg"></li>
1449 * <li class="item"><img src="images/marquee2.jpg"></li>
1450 * <li class="item"><img src="images/marquee3.jpg"></li>
1451 * <li class="item"><img src="images/marquee4.jpg"></li>
1452 * </ul>
1453 * </div>
1454 *
1455 * <script type="text/javascript">
1456 * $('.slideshow-container').dacSlideshow({
1457 * auto: true,
1458 * btnPrev: '.slideshow-prev',
1459 * btnNext: '.slideshow-next'
1460 * });
1461 * </script>
1462 *
1463 * Options:
1464 * btnPrev: optional identifier for previous button
1465 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001466 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001467 * auto: whether or not to auto-proceed
1468 * speed: animation speed
1469 * autoTime: time between auto-rotation
1470 * easing: easing function for transition
1471 * start: item to select by default
1472 * scroll: direction to scroll in
1473 * pagination: whether or not to include dotted pagination
1474 *
1475 */
1476
1477 (function($) {
1478 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001479
Scott Mainf5089842012-08-14 16:31:07 -07001480 //Options - see above
1481 o = $.extend({
1482 btnPrev: null,
1483 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001484 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001485 auto: true,
1486 speed: 500,
1487 autoTime: 12000,
1488 easing: null,
1489 start: 0,
1490 scroll: 1,
1491 pagination: true
1492
1493 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001494
1495 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001496 return this.each(function() {
1497
1498 var running = false;
1499 var animCss = o.vertical ? "top" : "left";
1500 var sizeCss = o.vertical ? "height" : "width";
1501 var div = $(this);
1502 var ul = $("ul", div);
1503 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001504 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001505 var timer = null;
1506
1507 var li = $("li", ul);
1508 var itemLength = li.size();
1509 var curr = o.start;
1510
1511 li.css({float: o.vertical ? "none" : "left"});
1512 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1513 div.css({position: "relative", "z-index": "2", left: "0px"});
1514
1515 var liSize = o.vertical ? height(li) : width(li);
1516 var ulSize = liSize * itemLength;
1517 var divSize = liSize;
1518
1519 li.css({width: li.width(), height: li.height()});
1520 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1521
1522 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001523
Scott Mainf5089842012-08-14 16:31:07 -07001524 //Pagination
1525 if (o.pagination) {
1526 var pagination = $("<div class='pagination'></div>");
1527 var pag_ul = $("<ul></ul>");
1528 if (tl > 1) {
1529 for (var i=0;i<tl;i++) {
1530 var li = $("<li>"+i+"</li>");
1531 pag_ul.append(li);
1532 if (i==o.start) li.addClass('active');
1533 li.click(function() {
1534 go(parseInt($(this).text()));
1535 })
1536 }
1537 pagination.append(pag_ul);
1538 div.append(pagination);
1539 }
1540 }
Scott Main3b90aff2013-08-01 18:09:35 -07001541
Scott Mainf5089842012-08-14 16:31:07 -07001542 //Previous button
1543 if(o.btnPrev)
1544 $(o.btnPrev).click(function(e) {
1545 e.preventDefault();
1546 return go(curr-o.scroll);
1547 });
1548
1549 //Next button
1550 if(o.btnNext)
1551 $(o.btnNext).click(function(e) {
1552 e.preventDefault();
1553 return go(curr+o.scroll);
1554 });
Scott Maineb410352013-01-14 19:03:40 -08001555
1556 //Pause button
1557 if(o.btnPause)
1558 $(o.btnPause).click(function(e) {
1559 e.preventDefault();
1560 if ($(this).hasClass('paused')) {
1561 startRotateTimer();
1562 } else {
1563 pauseRotateTimer();
1564 }
1565 });
Scott Main3b90aff2013-08-01 18:09:35 -07001566
Scott Mainf5089842012-08-14 16:31:07 -07001567 //Auto rotation
1568 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001569
Scott Mainf5089842012-08-14 16:31:07 -07001570 function startRotateTimer() {
1571 clearInterval(timer);
1572 timer = setInterval(function() {
1573 if (curr == tl-1) {
1574 go(0);
1575 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001576 go(curr+o.scroll);
1577 }
Scott Mainf5089842012-08-14 16:31:07 -07001578 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001579 $(o.btnPause).removeClass('paused');
1580 }
1581
1582 function pauseRotateTimer() {
1583 clearInterval(timer);
1584 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001585 }
1586
1587 //Go to an item
1588 function go(to) {
1589 if(!running) {
1590
1591 if(to<0) {
1592 to = itemLength-1;
1593 } else if (to>itemLength-1) {
1594 to = 0;
1595 }
1596 curr = to;
1597
1598 running = true;
1599
1600 ul.animate(
1601 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1602 function() {
1603 running = false;
1604 }
1605 );
1606
1607 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1608 $( (curr-o.scroll<0 && o.btnPrev)
1609 ||
1610 (curr+o.scroll > itemLength && o.btnNext)
1611 ||
1612 []
1613 ).addClass("disabled");
1614
Scott Main3b90aff2013-08-01 18:09:35 -07001615
Scott Mainf5089842012-08-14 16:31:07 -07001616 var nav_items = $('li', pagination);
1617 nav_items.removeClass('active');
1618 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001619
Scott Mainf5089842012-08-14 16:31:07 -07001620
1621 }
1622 if(o.auto) startRotateTimer();
1623 return false;
1624 };
1625 });
1626 };
1627
1628 function css(el, prop) {
1629 return parseInt($.css(el[0], prop)) || 0;
1630 };
1631 function width(el) {
1632 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1633 };
1634 function height(el) {
1635 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1636 };
1637
1638 })(jQuery);
1639
1640
Scott Main3b90aff2013-08-01 18:09:35 -07001641/*
Scott Mainf5089842012-08-14 16:31:07 -07001642 * dacSlideshow 1.0
1643 * Used on develop/index.html for side-sliding tabs
1644 *
1645 * Sample usage:
1646 * HTML -
1647 * <div class="slideshow-container">
1648 * <a href="" class="slideshow-prev">Prev</a>
1649 * <a href="" class="slideshow-next">Next</a>
1650 * <ul>
1651 * <li class="item"><img src="images/marquee1.jpg"></li>
1652 * <li class="item"><img src="images/marquee2.jpg"></li>
1653 * <li class="item"><img src="images/marquee3.jpg"></li>
1654 * <li class="item"><img src="images/marquee4.jpg"></li>
1655 * </ul>
1656 * </div>
1657 *
1658 * <script type="text/javascript">
1659 * $('.slideshow-container').dacSlideshow({
1660 * auto: true,
1661 * btnPrev: '.slideshow-prev',
1662 * btnNext: '.slideshow-next'
1663 * });
1664 * </script>
1665 *
1666 * Options:
1667 * btnPrev: optional identifier for previous button
1668 * btnNext: optional identifier for next button
1669 * auto: whether or not to auto-proceed
1670 * speed: animation speed
1671 * autoTime: time between auto-rotation
1672 * easing: easing function for transition
1673 * start: item to select by default
1674 * scroll: direction to scroll in
1675 * pagination: whether or not to include dotted pagination
1676 *
1677 */
1678 (function($) {
1679 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001680
Scott Mainf5089842012-08-14 16:31:07 -07001681 //Options - see above
1682 o = $.extend({
1683 speed : 250,
1684 easing: null,
1685 nav_id: null,
1686 frame_id: null
1687 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001688
1689 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001690 return this.each(function() {
1691
1692 var curr = 0;
1693 var running = false;
1694 var animCss = "margin-left";
1695 var sizeCss = "width";
1696 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001697
Scott Mainf5089842012-08-14 16:31:07 -07001698 var nav = $(o.nav_id, div);
1699 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001700 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001701 var frame = div.find(o.frame_id);
1702 var content_width = $(frame).find('ul').width();
1703 //Buttons
1704 $(nav_li).click(function(e) {
1705 go($(nav_li).index($(this)));
1706 })
Scott Main3b90aff2013-08-01 18:09:35 -07001707
Scott Mainf5089842012-08-14 16:31:07 -07001708 //Go to an item
1709 function go(to) {
1710 if(!running) {
1711 curr = to;
1712 running = true;
1713
1714 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1715 function() {
1716 running = false;
1717 }
1718 );
1719
Scott Main3b90aff2013-08-01 18:09:35 -07001720
Scott Mainf5089842012-08-14 16:31:07 -07001721 nav_li.removeClass('active');
1722 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001723
Scott Mainf5089842012-08-14 16:31:07 -07001724
1725 }
1726 return false;
1727 };
1728 });
1729 };
1730
1731 function css(el, prop) {
1732 return parseInt($.css(el[0], prop)) || 0;
1733 };
1734 function width(el) {
1735 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1736 };
1737 function height(el) {
1738 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1739 };
1740
1741 })(jQuery);
1742
1743
1744
1745
1746
1747/* ######################################################## */
1748/* ################ SEARCH SUGGESTIONS ################## */
1749/* ######################################################## */
1750
1751
Scott Main7e447ed2013-02-19 17:22:37 -08001752
Scott Main0e76e7e2013-03-12 10:24:07 -07001753var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1754var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1755
Scott Mainf5089842012-08-14 16:31:07 -07001756var gMatches = new Array();
1757var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001758var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001759var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1760var gListLength = 0;
1761
1762
1763var gGoogleMatches = new Array();
1764var ROW_COUNT_GOOGLE = 15; // max number of results in list
1765var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001766
Scott Main0e76e7e2013-03-12 10:24:07 -07001767var gDocsMatches = new Array();
1768var ROW_COUNT_DOCS = 100; // max number of results in list
1769var gDocsListLength = 0;
1770
Scott Mainde295272013-03-25 15:48:35 -07001771function onSuggestionClick(link) {
1772 // When user clicks a suggested document, track it
smain@google.comed677d72014-12-12 10:19:17 -08001773 ga('send', 'event', 'Suggestion Click', 'clicked: ' + $(link).attr('href'),
1774 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07001775}
1776
Scott Mainf5089842012-08-14 16:31:07 -07001777function set_item_selected($li, selected)
1778{
1779 if (selected) {
1780 $li.attr('class','jd-autocomplete jd-selected');
1781 } else {
1782 $li.attr('class','jd-autocomplete');
1783 }
1784}
1785
1786function set_item_values(toroot, $li, match)
1787{
1788 var $link = $('a',$li);
1789 $link.html(match.__hilabel || match.label);
1790 $link.attr('href',toroot + match.link);
1791}
1792
Scott Main719acb42013-12-05 16:05:09 -08001793function set_item_values_jd(toroot, $li, match)
1794{
1795 var $link = $('a',$li);
1796 $link.html(match.title);
1797 $link.attr('href',toroot + match.url);
1798}
1799
Scott Main0e76e7e2013-03-12 10:24:07 -07001800function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001801 var $li = $("<li class='jd-autocomplete'></li>");
1802 $list.append($li);
1803
1804 $li.mousedown(function() {
1805 window.location = this.firstChild.getAttribute("href");
1806 });
1807 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001808 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001809 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001810 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1811 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001812 });
Scott Mainde295272013-03-25 15:48:35 -07001813 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001814 $li.attr('class','show-item');
1815 return $li;
1816}
1817
Scott Mainf5089842012-08-14 16:31:07 -07001818function sync_selection_table(toroot)
1819{
Scott Mainf5089842012-08-14 16:31:07 -07001820 var $li; //list item jquery object
1821 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001822
Scott Main0e76e7e2013-03-12 10:24:07 -07001823 // if there are NO results at all, hide all columns
1824 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1825 $('.suggest-card').hide(300);
1826 return;
1827 }
1828
1829 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001830 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001831 // reveal suggestion list
1832 $('.suggest-card.dummy').show();
1833 $('.suggest-card.reference').show();
1834 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001835
Scott Main0e76e7e2013-03-12 10:24:07 -07001836 // reset the lists
1837 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001838
Scott Main0e76e7e2013-03-12 10:24:07 -07001839 // ########### ANDROID RESULTS #############
1840 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001841
Scott Main0e76e7e2013-03-12 10:24:07 -07001842 // determine android results to show
1843 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1844 gMatches.length : ROW_COUNT_FRAMEWORK;
1845 for (i=0; i<gListLength; i++) {
1846 var $li = new_suggestion($(".suggest-card.reference ul"));
1847 set_item_values(toroot, $li, gMatches[i]);
1848 set_item_selected($li, i == gSelectedIndex);
1849 }
1850 }
Scott Main7e447ed2013-02-19 17:22:37 -08001851
Scott Main0e76e7e2013-03-12 10:24:07 -07001852 // ########### GOOGLE RESULTS #############
1853 if (gGoogleMatches.length > 0) {
1854 // show header for list
1855 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001856
Scott Main0e76e7e2013-03-12 10:24:07 -07001857 // determine google results to show
1858 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1859 for (i=0; i<gGoogleListLength; i++) {
1860 var $li = new_suggestion($(".suggest-card.reference ul"));
1861 set_item_values(toroot, $li, gGoogleMatches[i]);
1862 set_item_selected($li, i == gSelectedIndex);
1863 }
1864 }
Scott Mainf5089842012-08-14 16:31:07 -07001865 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001866 $('.suggest-card.reference').hide();
1867 $('.suggest-card.dummy').hide();
1868 }
1869
1870 // ########### JD DOC RESULTS #############
1871 if (gDocsMatches.length > 0) {
1872 // reset the lists
1873 $(".search_filtered_wrapper.docs li").remove();
1874
1875 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001876 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1877 // The order must match the reverse order that each section appears as a card in
1878 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001879 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1880 for (i=0; i<gDocsListLength; i++) {
1881 var sugg = gDocsMatches[i];
1882 var $li;
1883 if (sugg.type == "design") {
1884 $li = new_suggestion($(".suggest-card.design ul"));
1885 } else
1886 if (sugg.type == "distribute") {
1887 $li = new_suggestion($(".suggest-card.distribute ul"));
1888 } else
Scott Main719acb42013-12-05 16:05:09 -08001889 if (sugg.type == "samples") {
1890 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1891 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001892 if (sugg.type == "training") {
1893 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1894 } else
Scott Main719acb42013-12-05 16:05:09 -08001895 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001896 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1897 } else {
1898 continue;
1899 }
1900
Scott Main719acb42013-12-05 16:05:09 -08001901 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001902 set_item_selected($li, i == gSelectedIndex);
1903 }
1904
1905 // add heading and show or hide card
1906 if ($(".suggest-card.design li").length > 0) {
1907 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1908 $(".suggest-card.design").show(300);
1909 } else {
1910 $('.suggest-card.design').hide(300);
1911 }
1912 if ($(".suggest-card.distribute li").length > 0) {
1913 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1914 $(".suggest-card.distribute").show(300);
1915 } else {
1916 $('.suggest-card.distribute').hide(300);
1917 }
1918 if ($(".child-card.guides li").length > 0) {
1919 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1920 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1921 }
1922 if ($(".child-card.training li").length > 0) {
1923 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1924 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1925 }
Scott Main719acb42013-12-05 16:05:09 -08001926 if ($(".child-card.samples li").length > 0) {
1927 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1928 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1929 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001930
1931 if ($(".suggest-card.develop li").length > 0) {
1932 $(".suggest-card.develop").show(300);
1933 } else {
1934 $('.suggest-card.develop').hide(300);
1935 }
1936
1937 } else {
1938 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001939 }
1940}
1941
Scott Main0e76e7e2013-03-12 10:24:07 -07001942/** Called by the search input's onkeydown and onkeyup events.
1943 * Handles navigation with keyboard arrows, Enter key to invoke search,
1944 * otherwise invokes search suggestions on key-up event.
1945 * @param e The JS event
1946 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001947 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001948 * @returns True if the event should bubble up
1949 */
Scott Mainf5089842012-08-14 16:31:07 -07001950function search_changed(e, kd, toroot)
1951{
Scott Main719acb42013-12-05 16:05:09 -08001952 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001953 var search = document.getElementById("search_autocomplete");
1954 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001955 // get the ul hosting the currently selected item
1956 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1957 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1958 var $selectedUl = $columns[gSelectedColumn];
1959
Scott Mainf5089842012-08-14 16:31:07 -07001960 // show/hide the close button
1961 if (text != '') {
1962 $(".search .close").removeClass("hide");
1963 } else {
1964 $(".search .close").addClass("hide");
1965 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001966 // 27 = esc
1967 if (e.keyCode == 27) {
1968 // close all search results
1969 if (kd) $('.search .close').trigger('click');
1970 return true;
1971 }
Scott Mainf5089842012-08-14 16:31:07 -07001972 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001973 else if (e.keyCode == 13) {
1974 if (gSelectedIndex < 0) {
1975 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001976 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1977 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001978 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001979 return true;
1980 } else {
1981 // otherwise, results are already showing, so allow ajax to auto refresh the results
1982 // and ignore this Enter press to avoid the reload.
1983 return false;
1984 }
1985 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001986 // click the link corresponding to selected item
1987 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001988 return false;
1989 }
1990 }
Scott Mainb16376f2014-05-21 20:35:47 -07001991 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001992 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001993 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001994 if ((sticky ) && (search.value != "")) {
1995 $('body,html').animate({scrollTop:0}, '500', 'swing');
1996 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001997 return true;
1998 }
1999 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07002000 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002001 // if the next item is a header, skip it
2002 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07002003 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08002004 }
2005 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002006 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08002007 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07002008 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2009 // If user reaches top, reset selected column
2010 if (gSelectedIndex < 0) {
2011 gSelectedColumn = -1;
2012 }
Scott Mainf5089842012-08-14 16:31:07 -07002013 }
2014 return false;
2015 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002016 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07002017 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002018 // if the next item is a header, skip it
2019 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07002020 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08002021 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002022 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
2023 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
2024 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08002025 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07002026 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07002027 }
2028 return false;
2029 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002030 // Consider left/right arrow navigation
2031 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
2032 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
2033 // 37 LEFT ARROW
2034 // go left only if current column is not left-most column (last column)
2035 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
2036 $('li', $selectedUl).removeClass('jd-selected');
2037 gSelectedColumn++;
2038 $selectedUl = $columns[gSelectedColumn];
2039 // keep or reset the selected item to last item as appropriate
2040 gSelectedIndex = gSelectedIndex >
2041 $("li", $selectedUl).length-1 ?
2042 $("li", $selectedUl).length-1 : gSelectedIndex;
2043 // if the corresponding item is a header, move down
2044 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2045 gSelectedIndex++;
2046 }
Scott Main3b90aff2013-08-01 18:09:35 -07002047 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002048 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2049 return false;
2050 }
2051 // 39 RIGHT ARROW
2052 // go right only if current column is not the right-most column (first column)
2053 else if (e.keyCode == 39 && gSelectedColumn > 0) {
2054 $('li', $selectedUl).removeClass('jd-selected');
2055 gSelectedColumn--;
2056 $selectedUl = $columns[gSelectedColumn];
2057 // keep or reset the selected item to last item as appropriate
2058 gSelectedIndex = gSelectedIndex >
2059 $("li", $selectedUl).length-1 ?
2060 $("li", $selectedUl).length-1 : gSelectedIndex;
2061 // if the corresponding item is a header, move down
2062 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2063 gSelectedIndex++;
2064 }
Scott Main3b90aff2013-08-01 18:09:35 -07002065 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002066 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2067 return false;
2068 }
2069 }
2070
Scott Main719acb42013-12-05 16:05:09 -08002071 // if key-up event and not arrow down/up/left/right,
2072 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07002073 else if (!kd && (e.keyCode != 40)
2074 && (e.keyCode != 38)
2075 && (e.keyCode != 37)
2076 && (e.keyCode != 39)) {
2077 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07002078 gMatches = new Array();
2079 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002080 gGoogleMatches = new Array();
2081 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07002082 gDocsMatches = new Array();
2083 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002084
2085 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07002086 for (var i=0; i<DATA.length; i++) {
2087 var s = DATA[i];
2088 if (text.length != 0 &&
2089 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2090 gMatches[matchedCount] = s;
2091 matchedCount++;
2092 }
2093 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002094 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07002095 for (var i=0; i<gMatches.length; i++) {
2096 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08002097 }
2098
2099
2100 // Search for Google matches
2101 for (var i=0; i<GOOGLE_DATA.length; i++) {
2102 var s = GOOGLE_DATA[i];
2103 if (text.length != 0 &&
2104 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2105 gGoogleMatches[matchedCountGoogle] = s;
2106 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07002107 }
2108 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002109 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08002110 for (var i=0; i<gGoogleMatches.length; i++) {
2111 var s = gGoogleMatches[i];
2112 }
2113
Scott Mainf5089842012-08-14 16:31:07 -07002114 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07002115
2116
2117
Scott Main719acb42013-12-05 16:05:09 -08002118 // Search for matching JD docs
Dirk Dougherty9b7f8f22014-11-01 17:08:56 -07002119 if (text.length >= 2) {
Scott Main719acb42013-12-05 16:05:09 -08002120 // Regex to match only the beginning of a word
2121 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
2122
2123
2124 // Search for Training classes
2125 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002126 // current search comparison, with counters for tag and title,
2127 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002128 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002129 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
Scott Main719acb42013-12-05 16:05:09 -08002134 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002135 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08002136 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002137 matched = true;
2138 s.matched_tag = j + 1; // add 1 to index position
2139 }
2140 }
Scott Main719acb42013-12-05 16:05:09 -08002141 // Don't consider doc title for lessons (only for class landing pages),
2142 // unless the lesson has a tag that already matches
2143 if ((s.lang == currentLang) &&
2144 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002145 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08002146 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002147 matched = true;
2148 s.matched_title = 1;
2149 }
2150 }
2151 if (matched) {
2152 gDocsMatches[matchedCountDocs] = s;
2153 matchedCountDocs++;
2154 }
2155 }
Scott Main719acb42013-12-05 16:05:09 -08002156
2157
2158 // Search for API Guides
2159 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2160 // current search comparison, with counters for tag and title,
2161 // used later to improve ranking
2162 var s = GUIDE_RESOURCES[i];
2163 s.matched_tag = 0;
2164 s.matched_title = 0;
2165 var matched = false;
2166
2167 // Check if query matches any tags; work backwards toward 1 to assist ranking
2168 for (var j = s.keywords.length - 1; j >= 0; j--) {
2169 // it matches a tag
2170 if (s.keywords[j].toLowerCase().match(textRegex)) {
2171 matched = true;
2172 s.matched_tag = j + 1; // add 1 to index position
2173 }
2174 }
2175 // Check if query matches the doc title, but only for current language
2176 if (s.lang == currentLang) {
2177 // if query matches the doc title
2178 if (s.title.toLowerCase().match(textRegex)) {
2179 matched = true;
2180 s.matched_title = 1;
2181 }
2182 }
2183 if (matched) {
2184 gDocsMatches[matchedCountDocs] = s;
2185 matchedCountDocs++;
2186 }
2187 }
2188
2189
2190 // Search for Tools Guides
2191 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2192 // current search comparison, with counters for tag and title,
2193 // used later to improve ranking
2194 var s = TOOLS_RESOURCES[i];
2195 s.matched_tag = 0;
2196 s.matched_title = 0;
2197 var matched = false;
2198
2199 // Check if query matches any tags; work backwards toward 1 to assist ranking
2200 for (var j = s.keywords.length - 1; j >= 0; j--) {
2201 // it matches a tag
2202 if (s.keywords[j].toLowerCase().match(textRegex)) {
2203 matched = true;
2204 s.matched_tag = j + 1; // add 1 to index position
2205 }
2206 }
2207 // Check if query matches the doc title, but only for current language
2208 if (s.lang == currentLang) {
2209 // if query matches the doc title
2210 if (s.title.toLowerCase().match(textRegex)) {
2211 matched = true;
2212 s.matched_title = 1;
2213 }
2214 }
2215 if (matched) {
2216 gDocsMatches[matchedCountDocs] = s;
2217 matchedCountDocs++;
2218 }
2219 }
2220
2221
2222 // Search for About docs
2223 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2224 // current search comparison, with counters for tag and title,
2225 // used later to improve ranking
2226 var s = ABOUT_RESOURCES[i];
2227 s.matched_tag = 0;
2228 s.matched_title = 0;
2229 var matched = false;
2230
2231 // Check if query matches any tags; work backwards toward 1 to assist ranking
2232 for (var j = s.keywords.length - 1; j >= 0; j--) {
2233 // it matches a tag
2234 if (s.keywords[j].toLowerCase().match(textRegex)) {
2235 matched = true;
2236 s.matched_tag = j + 1; // add 1 to index position
2237 }
2238 }
2239 // Check if query matches the doc title, but only for current language
2240 if (s.lang == currentLang) {
2241 // if query matches the doc title
2242 if (s.title.toLowerCase().match(textRegex)) {
2243 matched = true;
2244 s.matched_title = 1;
2245 }
2246 }
2247 if (matched) {
2248 gDocsMatches[matchedCountDocs] = s;
2249 matchedCountDocs++;
2250 }
2251 }
2252
2253
2254 // Search for Design guides
2255 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2256 // current search comparison, with counters for tag and title,
2257 // used later to improve ranking
2258 var s = DESIGN_RESOURCES[i];
2259 s.matched_tag = 0;
2260 s.matched_title = 0;
2261 var matched = false;
2262
2263 // Check if query matches any tags; work backwards toward 1 to assist ranking
2264 for (var j = s.keywords.length - 1; j >= 0; j--) {
2265 // it matches a tag
2266 if (s.keywords[j].toLowerCase().match(textRegex)) {
2267 matched = true;
2268 s.matched_tag = j + 1; // add 1 to index position
2269 }
2270 }
2271 // Check if query matches the doc title, but only for current language
2272 if (s.lang == currentLang) {
2273 // if query matches the doc title
2274 if (s.title.toLowerCase().match(textRegex)) {
2275 matched = true;
2276 s.matched_title = 1;
2277 }
2278 }
2279 if (matched) {
2280 gDocsMatches[matchedCountDocs] = s;
2281 matchedCountDocs++;
2282 }
2283 }
2284
2285
2286 // Search for Distribute guides
2287 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2288 // current search comparison, with counters for tag and title,
2289 // used later to improve ranking
2290 var s = DISTRIBUTE_RESOURCES[i];
2291 s.matched_tag = 0;
2292 s.matched_title = 0;
2293 var matched = false;
2294
2295 // Check if query matches any tags; work backwards toward 1 to assist ranking
2296 for (var j = s.keywords.length - 1; j >= 0; j--) {
2297 // it matches a tag
2298 if (s.keywords[j].toLowerCase().match(textRegex)) {
2299 matched = true;
2300 s.matched_tag = j + 1; // add 1 to index position
2301 }
2302 }
2303 // Check if query matches the doc title, but only for current language
2304 if (s.lang == currentLang) {
2305 // if query matches the doc title
2306 if (s.title.toLowerCase().match(textRegex)) {
2307 matched = true;
2308 s.matched_title = 1;
2309 }
2310 }
2311 if (matched) {
2312 gDocsMatches[matchedCountDocs] = s;
2313 matchedCountDocs++;
2314 }
2315 }
2316
2317
2318 // Search for Google guides
2319 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2320 // current search comparison, with counters for tag and title,
2321 // used later to improve ranking
2322 var s = GOOGLE_RESOURCES[i];
2323 s.matched_tag = 0;
2324 s.matched_title = 0;
2325 var matched = false;
2326
2327 // Check if query matches any tags; work backwards toward 1 to assist ranking
2328 for (var j = s.keywords.length - 1; j >= 0; j--) {
2329 // it matches a tag
2330 if (s.keywords[j].toLowerCase().match(textRegex)) {
2331 matched = true;
2332 s.matched_tag = j + 1; // add 1 to index position
2333 }
2334 }
2335 // Check if query matches the doc title, but only for current language
2336 if (s.lang == currentLang) {
2337 // if query matches the doc title
2338 if (s.title.toLowerCase().match(textRegex)) {
2339 matched = true;
2340 s.matched_title = 1;
2341 }
2342 }
2343 if (matched) {
2344 gDocsMatches[matchedCountDocs] = s;
2345 matchedCountDocs++;
2346 }
2347 }
2348
2349
2350 // Search for Samples
2351 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2352 // current search comparison, with counters for tag and title,
2353 // used later to improve ranking
2354 var s = SAMPLES_RESOURCES[i];
2355 s.matched_tag = 0;
2356 s.matched_title = 0;
2357 var matched = false;
2358 // Check if query matches any tags; work backwards toward 1 to assist ranking
2359 for (var j = s.keywords.length - 1; j >= 0; j--) {
2360 // it matches a tag
2361 if (s.keywords[j].toLowerCase().match(textRegex)) {
2362 matched = true;
2363 s.matched_tag = j + 1; // add 1 to index position
2364 }
2365 }
2366 // Check if query matches the doc title, but only for current language
2367 if (s.lang == currentLang) {
2368 // if query matches the doc title.t
2369 if (s.title.toLowerCase().match(textRegex)) {
2370 matched = true;
2371 s.matched_title = 1;
2372 }
2373 }
2374 if (matched) {
2375 gDocsMatches[matchedCountDocs] = s;
2376 matchedCountDocs++;
2377 }
2378 }
2379
2380 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002381 rank_autocomplete_doc_results(text, gDocsMatches);
2382 }
2383
2384 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002385 sync_selection_table(toroot);
2386 return true; // allow the event to bubble up to the search api
2387 }
2388}
2389
Scott Main0e76e7e2013-03-12 10:24:07 -07002390/* Order the jd doc result list based on match quality */
2391function rank_autocomplete_doc_results(query, matches) {
2392 query = query || '';
2393 if (!matches || !matches.length)
2394 return;
2395
2396 var _resultScoreFn = function(match) {
2397 var score = 1.0;
2398
2399 // if the query matched a tag
2400 if (match.matched_tag > 0) {
2401 // multiply score by factor relative to position in tags list (max of 3)
2402 score *= 3 / match.matched_tag;
2403
2404 // if it also matched the title
2405 if (match.matched_title > 0) {
2406 score *= 2;
2407 }
2408 } else if (match.matched_title > 0) {
2409 score *= 3;
2410 }
2411
2412 return score;
2413 };
2414
2415 for (var i=0; i<matches.length; i++) {
2416 matches[i].__resultScore = _resultScoreFn(matches[i]);
2417 }
2418
2419 matches.sort(function(a,b){
2420 var n = b.__resultScore - a.__resultScore;
2421 if (n == 0) // lexicographical sort if scores are the same
2422 n = (a.label < b.label) ? -1 : 1;
2423 return n;
2424 });
2425}
2426
Scott Main7e447ed2013-02-19 17:22:37 -08002427/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002428function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002429 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002430 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002431 return;
2432
2433 // helper function that gets the last occurence index of the given regex
2434 // in the given string, or -1 if not found
2435 var _lastSearch = function(s, re) {
2436 if (s == '')
2437 return -1;
2438 var l = -1;
2439 var tmp;
2440 while ((tmp = s.search(re)) >= 0) {
2441 if (l < 0) l = 0;
2442 l += tmp;
2443 s = s.substr(tmp + 1);
2444 }
2445 return l;
2446 };
2447
2448 // helper function that counts the occurrences of a given character in
2449 // a given string
2450 var _countChar = function(s, c) {
2451 var n = 0;
2452 for (var i=0; i<s.length; i++)
2453 if (s.charAt(i) == c) ++n;
2454 return n;
2455 };
2456
2457 var queryLower = query.toLowerCase();
2458 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2459 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2460 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2461
2462 var _resultScoreFn = function(result) {
2463 // scores are calculated based on exact and prefix matches,
2464 // and then number of path separators (dots) from the last
2465 // match (i.e. favoring classes and deep package names)
2466 var score = 1.0;
2467 var labelLower = result.label.toLowerCase();
2468 var t;
2469 t = _lastSearch(labelLower, partExactAlnumRE);
2470 if (t >= 0) {
2471 // exact part match
2472 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2473 score *= 200 / (partsAfter + 1);
2474 } else {
2475 t = _lastSearch(labelLower, partPrefixAlnumRE);
2476 if (t >= 0) {
2477 // part prefix match
2478 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2479 score *= 20 / (partsAfter + 1);
2480 }
2481 }
2482
2483 return score;
2484 };
2485
Scott Main7e447ed2013-02-19 17:22:37 -08002486 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002487 // if the API is deprecated, default score is 0; otherwise, perform scoring
2488 if (matches[i].deprecated == "true") {
2489 matches[i].__resultScore = 0;
2490 } else {
2491 matches[i].__resultScore = _resultScoreFn(matches[i]);
2492 }
Scott Mainf5089842012-08-14 16:31:07 -07002493 }
2494
Scott Main7e447ed2013-02-19 17:22:37 -08002495 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002496 var n = b.__resultScore - a.__resultScore;
2497 if (n == 0) // lexicographical sort if scores are the same
2498 n = (a.label < b.label) ? -1 : 1;
2499 return n;
2500 });
2501}
2502
Scott Main7e447ed2013-02-19 17:22:37 -08002503/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002504function highlight_autocomplete_result_labels(query) {
2505 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002506 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002507 return;
2508
2509 var queryLower = query.toLowerCase();
2510 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2511 var queryRE = new RegExp(
2512 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2513 for (var i=0; i<gMatches.length; i++) {
2514 gMatches[i].__hilabel = gMatches[i].label.replace(
2515 queryRE, '<b>$1</b>');
2516 }
Scott Main7e447ed2013-02-19 17:22:37 -08002517 for (var i=0; i<gGoogleMatches.length; i++) {
2518 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2519 queryRE, '<b>$1</b>');
2520 }
Scott Mainf5089842012-08-14 16:31:07 -07002521}
2522
2523function search_focus_changed(obj, focused)
2524{
Scott Main3b90aff2013-08-01 18:09:35 -07002525 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002526 if(obj.value == ""){
2527 $(".search .close").addClass("hide");
2528 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002529 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002530 }
2531}
2532
2533function submit_search() {
2534 var query = document.getElementById('search_autocomplete').value;
2535 location.hash = 'q=' + query;
2536 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002537 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002538 return false;
2539}
2540
2541
2542function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002543 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002544 $(".search .close").addClass("hide");
2545 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002546
Scott Mainf5089842012-08-14 16:31:07 -07002547 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002548
Scott Mainf5089842012-08-14 16:31:07 -07002549 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2550 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002551
2552 // forcefully regain key-up event control (previously jacked by search api)
2553 $("#search_autocomplete").keyup(function(event) {
2554 return search_changed(event, false, toRoot);
2555 });
2556
Scott Mainf5089842012-08-14 16:31:07 -07002557 return false;
2558}
2559
2560
2561
2562/* ########################################################## */
2563/* ################ CUSTOM SEARCH ENGINE ################## */
2564/* ########################################################## */
2565
Scott Mainf5089842012-08-14 16:31:07 -07002566var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002567google.load('search', '1', {"callback" : function() {
2568 searchControl = new google.search.SearchControl();
2569 } });
Scott Mainf5089842012-08-14 16:31:07 -07002570
2571function loadSearchResults() {
2572 document.getElementById("search_autocomplete").style.color = "#000";
2573
Scott Mainf5089842012-08-14 16:31:07 -07002574 searchControl = new google.search.SearchControl();
2575
2576 // use our existing search form and use tabs when multiple searchers are used
2577 drawOptions = new google.search.DrawOptions();
2578 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2579 drawOptions.setInput(document.getElementById("search_autocomplete"));
2580
2581 // configure search result options
2582 searchOptions = new google.search.SearcherOptions();
2583 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2584
2585 // configure each of the searchers, for each tab
2586 devSiteSearcher = new google.search.WebSearch();
2587 devSiteSearcher.setUserDefinedLabel("All");
2588 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2589
2590 designSearcher = new google.search.WebSearch();
2591 designSearcher.setUserDefinedLabel("Design");
2592 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2593
2594 trainingSearcher = new google.search.WebSearch();
2595 trainingSearcher.setUserDefinedLabel("Training");
2596 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2597
2598 guidesSearcher = new google.search.WebSearch();
2599 guidesSearcher.setUserDefinedLabel("Guides");
2600 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2601
2602 referenceSearcher = new google.search.WebSearch();
2603 referenceSearcher.setUserDefinedLabel("Reference");
2604 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2605
Scott Maindf08ada2012-12-03 08:54:37 -08002606 googleSearcher = new google.search.WebSearch();
2607 googleSearcher.setUserDefinedLabel("Google Services");
2608 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2609
Scott Mainf5089842012-08-14 16:31:07 -07002610 blogSearcher = new google.search.WebSearch();
2611 blogSearcher.setUserDefinedLabel("Blog");
2612 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2613
2614 // add each searcher to the search control
2615 searchControl.addSearcher(devSiteSearcher, searchOptions);
2616 searchControl.addSearcher(designSearcher, searchOptions);
2617 searchControl.addSearcher(trainingSearcher, searchOptions);
2618 searchControl.addSearcher(guidesSearcher, searchOptions);
2619 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002620 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002621 searchControl.addSearcher(blogSearcher, searchOptions);
2622
2623 // configure result options
2624 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2625 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2626 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2627 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2628
2629 // upon ajax search, refresh the url and search title
2630 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2631 updateResultTitle(query);
2632 var query = document.getElementById('search_autocomplete').value;
2633 location.hash = 'q=' + query;
2634 });
2635
Scott Mainde295272013-03-25 15:48:35 -07002636 // once search results load, set up click listeners
2637 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2638 addResultClickListeners();
2639 });
2640
Scott Mainf5089842012-08-14 16:31:07 -07002641 // draw the search results box
2642 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2643
2644 // get query and execute the search
2645 searchControl.execute(decodeURI(getQuery(location.hash)));
2646
2647 document.getElementById("search_autocomplete").focus();
2648 addTabListeners();
2649}
2650// End of loadSearchResults
2651
2652
2653google.setOnLoadCallback(function(){
2654 if (location.hash.indexOf("q=") == -1) {
2655 // if there's no query in the url, don't search and make sure results are hidden
2656 $('#searchResults').hide();
2657 return;
2658 } else {
2659 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002660 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002661 $(".search .close").removeClass("hide");
2662 loadSearchResults();
2663 }
2664}, true);
2665
smain@google.com9a818f52014-10-03 09:25:59 -07002666/* Adjust the scroll position to account for sticky header, only if the hash matches an id.
2667 This does not handle <a name=""> tags. Some CSS fixes those, but only for reference docs. */
Scott Mainb16376f2014-05-21 20:35:47 -07002668function offsetScrollForSticky() {
smain@google.com11fc0092014-10-16 22:10:00 -07002669 // Ignore if there's no search bar (some special pages have no header)
2670 if ($("#search-container").length < 1) return;
2671
smain@google.com3b77ab52014-06-17 11:57:27 -07002672 var hash = escape(location.hash.substr(1));
2673 var $matchingElement = $("#"+hash);
smain@google.com08f336ea2014-10-03 17:40:00 -07002674 // Sanity check that there's an element with that ID on the page
2675 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002676 // If the position of the target element is near the top of the page (<20px, where we expect it
2677 // to be because we need to move it down 60px to become in view), then move it down 60px
2678 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2679 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002680 }
2681 }
2682}
2683
Scott Mainf5089842012-08-14 16:31:07 -07002684// when an event on the browser history occurs (back, forward, load) requery hash and do search
2685$(window).hashchange( function(){
smain@google.com2f077192014-10-09 18:04:10 -07002686 // Ignore if there's no search bar (some special pages have no header)
2687 if ($("#search-container").length < 1) return;
2688
Dirk Doughertyc3921652014-05-13 16:55:26 -07002689 // If the hash isn't a search query or there's an error in the query,
2690 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002691 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2692 // If the results pane is open, close it.
2693 if (!$("#searchResults").is(":hidden")) {
2694 hideResults();
2695 }
Scott Mainb16376f2014-05-21 20:35:47 -07002696 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002697 return;
2698 }
2699
2700 // Otherwise, we have a search to do
2701 var query = decodeURI(getQuery(location.hash));
2702 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002703 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002704 $("#search_autocomplete").focus();
2705 $(".search .close").removeClass("hide");
2706
2707 updateResultTitle(query);
2708});
2709
2710function updateResultTitle(query) {
2711 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2712}
2713
2714// forcefully regain key-up event control (previously jacked by search api)
2715$("#search_autocomplete").keyup(function(event) {
2716 return search_changed(event, false, toRoot);
2717});
2718
2719// add event listeners to each tab so we can track the browser history
2720function addTabListeners() {
2721 var tabHeaders = $(".gsc-tabHeader");
2722 for (var i = 0; i < tabHeaders.length; i++) {
2723 $(tabHeaders[i]).attr("id",i).click(function() {
2724 /*
2725 // make a copy of the page numbers for the search left pane
2726 setTimeout(function() {
2727 // remove any residual page numbers
2728 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002729 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002730 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002731 // and because we're going to remove it (previous line),
2732 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002733 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2734 .clone().appendTo('#searchResults .gsc-tabsArea');
2735 }, 200);
2736 */
2737 });
2738 }
2739 setTimeout(function(){$(tabHeaders[0]).click()},200);
2740}
2741
Scott Mainde295272013-03-25 15:48:35 -07002742// add analytics tracking events to each result link
2743function addResultClickListeners() {
2744 $("#searchResults a.gs-title").each(function(index, link) {
2745 // When user clicks enter for Google search results, track it
2746 $(link).click(function() {
smain@google.comed677d72014-12-12 10:19:17 -08002747 ga('send', 'event', 'Google Click', 'clicked: ' + $(this).attr('href'),
2748 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07002749 });
2750 });
2751}
2752
Scott Mainf5089842012-08-14 16:31:07 -07002753
2754function getQuery(hash) {
2755 var queryParts = hash.split('=');
2756 return queryParts[1];
2757}
2758
2759/* returns the given string with all HTML brackets converted to entities
2760 TODO: move this to the site's JS library */
2761function escapeHTML(string) {
2762 return string.replace(/</g,"&lt;")
2763 .replace(/>/g,"&gt;");
2764}
2765
2766
2767
2768
2769
2770
2771
2772/* ######################################################## */
2773/* ################# JAVADOC REFERENCE ################### */
2774/* ######################################################## */
2775
Scott Main65511c02012-09-07 15:51:32 -07002776/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002777if (location.pathname.indexOf("/reference") == 0) {
2778 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2779 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2780 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002781 $(document).ready(function() {
2782 // init available apis based on user pref
2783 changeApiLevel();
2784 initSidenavHeightResize()
2785 });
2786 }
Scott Main65511c02012-09-07 15:51:32 -07002787}
Scott Mainf5089842012-08-14 16:31:07 -07002788
2789var API_LEVEL_COOKIE = "api_level";
2790var minLevel = 1;
2791var maxLevel = 1;
2792
2793/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002794
Scott Mainf5089842012-08-14 16:31:07 -07002795 function initSidenavHeightResize() {
2796 // Change the drag bar size to nicely fit the scrollbar positions
2797 var $dragBar = $(".ui-resizable-s");
2798 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002799
2800 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002801 containment: "#nav-panels",
2802 handles: "s",
2803 alsoResize: "#packages-nav",
2804 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2805 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2806 });
Scott Main3b90aff2013-08-01 18:09:35 -07002807
Scott Mainf5089842012-08-14 16:31:07 -07002808 }
Scott Main3b90aff2013-08-01 18:09:35 -07002809
Scott Mainf5089842012-08-14 16:31:07 -07002810function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002811 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002812 $('#devdoc-nav').css({
2813 'width' : $('#side-nav').css('width'),
2814 'margin' : $('#side-nav').css('margin')
2815 });
2816 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002817
Scott Mainf5089842012-08-14 16:31:07 -07002818 initSidenavHeightResize();
2819}
2820
2821function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002822 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002823 $('#devdoc-nav').css({
2824 'width' : $('#side-nav').css('width'),
2825 'margin' : $('#side-nav').css('margin')
2826 });
2827 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002828
Scott Mainf5089842012-08-14 16:31:07 -07002829 initSidenavHeightResize();
2830}
2831
2832function buildApiLevelSelector() {
2833 maxLevel = SINCE_DATA.length;
2834 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2835 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2836
2837 minLevel = parseInt($("#doc-api-level").attr("class"));
2838 // Handle provisional api levels; the provisional level will always be the highest possible level
2839 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2840 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2841 if (isNaN(minLevel) && minLevel.length) {
2842 minLevel = maxLevel;
2843 }
2844 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2845 for (var i = maxLevel-1; i >= 0; i--) {
2846 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2847 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2848 select.append(option);
2849 }
2850
2851 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2852 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2853 selectedLevelItem.setAttribute('selected',true);
2854}
2855
2856function changeApiLevel() {
2857 maxLevel = SINCE_DATA.length;
2858 var selectedLevel = maxLevel;
2859
2860 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2861 toggleVisisbleApis(selectedLevel, "body");
2862
smain@google.com6bdcb982014-11-14 11:53:07 -08002863 writeCookie(API_LEVEL_COOKIE, selectedLevel, null);
Scott Mainf5089842012-08-14 16:31:07 -07002864
2865 if (selectedLevel < minLevel) {
2866 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002867 $("#naMessage").show().html("<div><p><strong>This " + thing
2868 + " requires API level " + minLevel + " or higher.</strong></p>"
2869 + "<p>This document is hidden because your selected API level for the documentation is "
2870 + selectedLevel + ". You can change the documentation API level with the selector "
2871 + "above the left navigation.</p>"
2872 + "<p>For more information about specifying the API level your app requires, "
2873 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2874 + ">Supporting Different Platform Versions</a>.</p>"
2875 + "<input type='button' value='OK, make this page visible' "
2876 + "title='Change the API level to " + minLevel + "' "
2877 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2878 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002879 } else {
2880 $("#naMessage").hide();
2881 }
2882}
2883
2884function toggleVisisbleApis(selectedLevel, context) {
2885 var apis = $(".api",context);
2886 apis.each(function(i) {
2887 var obj = $(this);
2888 var className = obj.attr("class");
2889 var apiLevelIndex = className.lastIndexOf("-")+1;
2890 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2891 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2892 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2893 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2894 return;
2895 }
2896 apiLevel = parseInt(apiLevel);
2897
2898 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2899 var selectedLevelNum = parseInt(selectedLevel)
2900 var apiLevelNum = parseInt(apiLevel);
2901 if (isNaN(apiLevelNum)) {
2902 apiLevelNum = maxLevel;
2903 }
2904
2905 // Grey things out that aren't available and give a tooltip title
2906 if (apiLevelNum > selectedLevelNum) {
2907 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002908 + apiLevel + "\" or higher. To reveal, change the target API level "
2909 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002910 }
Scott Mainf5089842012-08-14 16:31:07 -07002911 else obj.removeClass("absent").removeAttr("title");
2912 });
2913}
2914
2915
2916
2917
2918/* ################# SIDENAV TREE VIEW ################### */
2919
2920function new_node(me, mom, text, link, children_data, api_level)
2921{
2922 var node = new Object();
2923 node.children = Array();
2924 node.children_data = children_data;
2925 node.depth = mom.depth + 1;
2926
2927 node.li = document.createElement("li");
2928 mom.get_children_ul().appendChild(node.li);
2929
2930 node.label_div = document.createElement("div");
2931 node.label_div.className = "label";
2932 if (api_level != null) {
2933 $(node.label_div).addClass("api");
2934 $(node.label_div).addClass("api-level-"+api_level);
2935 }
2936 node.li.appendChild(node.label_div);
2937
2938 if (children_data != null) {
2939 node.expand_toggle = document.createElement("a");
2940 node.expand_toggle.href = "javascript:void(0)";
2941 node.expand_toggle.onclick = function() {
2942 if (node.expanded) {
2943 $(node.get_children_ul()).slideUp("fast");
2944 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2945 node.expanded = false;
2946 } else {
2947 expand_node(me, node);
2948 }
2949 };
2950 node.label_div.appendChild(node.expand_toggle);
2951
2952 node.plus_img = document.createElement("img");
2953 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2954 node.plus_img.className = "plus";
2955 node.plus_img.width = "8";
2956 node.plus_img.border = "0";
2957 node.expand_toggle.appendChild(node.plus_img);
2958
2959 node.expanded = false;
2960 }
2961
2962 var a = document.createElement("a");
2963 node.label_div.appendChild(a);
2964 node.label = document.createTextNode(text);
2965 a.appendChild(node.label);
2966 if (link) {
2967 a.href = me.toroot + link;
2968 } else {
2969 if (children_data != null) {
2970 a.className = "nolink";
2971 a.href = "javascript:void(0)";
2972 a.onclick = node.expand_toggle.onclick;
2973 // This next line shouldn't be necessary. I'll buy a beer for the first
2974 // person who figures out how to remove this line and have the link
2975 // toggle shut on the first try. --joeo@android.com
2976 node.expanded = false;
2977 }
2978 }
Scott Main3b90aff2013-08-01 18:09:35 -07002979
Scott Mainf5089842012-08-14 16:31:07 -07002980
2981 node.children_ul = null;
2982 node.get_children_ul = function() {
2983 if (!node.children_ul) {
2984 node.children_ul = document.createElement("ul");
2985 node.children_ul.className = "children_ul";
2986 node.children_ul.style.display = "none";
2987 node.li.appendChild(node.children_ul);
2988 }
2989 return node.children_ul;
2990 };
2991
2992 return node;
2993}
2994
Robert Lyd2dd6e52012-11-29 21:28:48 -08002995
2996
2997
Scott Mainf5089842012-08-14 16:31:07 -07002998function expand_node(me, node)
2999{
3000 if (node.children_data && !node.expanded) {
3001 if (node.children_visited) {
3002 $(node.get_children_ul()).slideDown("fast");
3003 } else {
3004 get_node(me, node);
3005 if ($(node.label_div).hasClass("absent")) {
3006 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07003007 }
Scott Mainf5089842012-08-14 16:31:07 -07003008 $(node.get_children_ul()).slideDown("fast");
3009 }
3010 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
3011 node.expanded = true;
3012
3013 // perform api level toggling because new nodes are new to the DOM
3014 var selectedLevel = $("#apiLevelSelector option:selected").val();
3015 toggleVisisbleApis(selectedLevel, "#side-nav");
3016 }
3017}
3018
3019function get_node(me, mom)
3020{
3021 mom.children_visited = true;
3022 for (var i in mom.children_data) {
3023 var node_data = mom.children_data[i];
3024 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
3025 node_data[2], node_data[3]);
3026 }
3027}
3028
3029function this_page_relative(toroot)
3030{
3031 var full = document.location.pathname;
3032 var file = "";
3033 if (toroot.substr(0, 1) == "/") {
3034 if (full.substr(0, toroot.length) == toroot) {
3035 return full.substr(toroot.length);
3036 } else {
3037 // the file isn't under toroot. Fail.
3038 return null;
3039 }
3040 } else {
3041 if (toroot != "./") {
3042 toroot = "./" + toroot;
3043 }
3044 do {
3045 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
3046 var pos = full.lastIndexOf("/");
3047 file = full.substr(pos) + file;
3048 full = full.substr(0, pos);
3049 toroot = toroot.substr(0, toroot.length-3);
3050 }
3051 } while (toroot != "" && toroot != "/");
3052 return file.substr(1);
3053 }
3054}
3055
3056function find_page(url, data)
3057{
3058 var nodes = data;
3059 var result = null;
3060 for (var i in nodes) {
3061 var d = nodes[i];
3062 if (d[1] == url) {
3063 return new Array(i);
3064 }
3065 else if (d[2] != null) {
3066 result = find_page(url, d[2]);
3067 if (result != null) {
3068 return (new Array(i).concat(result));
3069 }
3070 }
3071 }
3072 return null;
3073}
3074
Scott Mainf5089842012-08-14 16:31:07 -07003075function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07003076 // load json file for navtree data
3077 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
3078 // when the file is loaded, initialize the tree
3079 if(jqxhr.status === 200) {
3080 init_navtree("tree-list", toroot, NAVTREE_DATA);
3081 }
3082 });
Scott Main3b90aff2013-08-01 18:09:35 -07003083
Scott Mainf5089842012-08-14 16:31:07 -07003084 // perform api level toggling because because the whole tree is new to the DOM
3085 var selectedLevel = $("#apiLevelSelector option:selected").val();
3086 toggleVisisbleApis(selectedLevel, "#side-nav");
3087}
3088
3089function init_navtree(navtree_id, toroot, root_nodes)
3090{
3091 var me = new Object();
3092 me.toroot = toroot;
3093 me.node = new Object();
3094
3095 me.node.li = document.getElementById(navtree_id);
3096 me.node.children_data = root_nodes;
3097 me.node.children = new Array();
3098 me.node.children_ul = document.createElement("ul");
3099 me.node.get_children_ul = function() { return me.node.children_ul; };
3100 //me.node.children_ul.className = "children_ul";
3101 me.node.li.appendChild(me.node.children_ul);
3102 me.node.depth = 0;
3103
3104 get_node(me, me.node);
3105
3106 me.this_page = this_page_relative(toroot);
3107 me.breadcrumbs = find_page(me.this_page, root_nodes);
3108 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
3109 var mom = me.node;
3110 for (var i in me.breadcrumbs) {
3111 var j = me.breadcrumbs[i];
3112 mom = mom.children[j];
3113 expand_node(me, mom);
3114 }
3115 mom.label_div.className = mom.label_div.className + " selected";
3116 addLoadEvent(function() {
3117 scrollIntoView("nav-tree");
3118 });
3119 }
3120}
3121
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003122
3123
3124
3125
3126
3127
3128
Robert Lyd2dd6e52012-11-29 21:28:48 -08003129/* TODO: eliminate redundancy with non-google functions */
3130function init_google_navtree(navtree_id, toroot, root_nodes)
3131{
3132 var me = new Object();
3133 me.toroot = toroot;
3134 me.node = new Object();
3135
3136 me.node.li = document.getElementById(navtree_id);
3137 me.node.children_data = root_nodes;
3138 me.node.children = new Array();
3139 me.node.children_ul = document.createElement("ul");
3140 me.node.get_children_ul = function() { return me.node.children_ul; };
3141 //me.node.children_ul.className = "children_ul";
3142 me.node.li.appendChild(me.node.children_ul);
3143 me.node.depth = 0;
3144
3145 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003146}
3147
3148function new_google_node(me, mom, text, link, children_data, api_level)
3149{
3150 var node = new Object();
3151 var child;
3152 node.children = Array();
3153 node.children_data = children_data;
3154 node.depth = mom.depth + 1;
3155 node.get_children_ul = function() {
3156 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003157 node.children_ul = document.createElement("ul");
3158 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003159 node.li.appendChild(node.children_ul);
3160 }
3161 return node.children_ul;
3162 };
3163 node.li = document.createElement("li");
3164
3165 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003166
3167
Robert Lyd2dd6e52012-11-29 21:28:48 -08003168 if(link) {
3169 child = document.createElement("a");
3170
3171 }
3172 else {
3173 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003174 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003175
3176 }
3177 if (children_data != null) {
3178 node.li.className="nav-section";
3179 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003180 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003181 node.li.appendChild(node.label_div);
3182 get_google_node(me, node);
3183 node.label_div.appendChild(child);
3184 }
3185 else {
3186 node.li.appendChild(child);
3187 }
3188 if(link) {
3189 child.href = me.toroot + link;
3190 }
3191 node.label = document.createTextNode(text);
3192 child.appendChild(node.label);
3193
3194 node.children_ul = null;
3195
3196 return node;
3197}
3198
3199function get_google_node(me, mom)
3200{
3201 mom.children_visited = true;
3202 var linkText;
3203 for (var i in mom.children_data) {
3204 var node_data = mom.children_data[i];
3205 linkText = node_data[0];
3206
3207 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3208 linkText = linkText.substr(19, linkText.length);
3209 }
3210 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3211 node_data[2], node_data[3]);
3212 }
3213}
Scott Mainad08f072013-08-20 16:49:57 -07003214
3215
3216
3217
3218
3219
3220/****** NEW version of script to build google and sample navs dynamically ******/
3221// TODO: update Google reference docs to tolerate this new implementation
3222
Scott Maine624b3f2013-09-12 12:56:41 -07003223var NODE_NAME = 0;
3224var NODE_HREF = 1;
3225var NODE_GROUP = 2;
3226var NODE_TAGS = 3;
3227var NODE_CHILDREN = 4;
3228
Scott Mainad08f072013-08-20 16:49:57 -07003229function init_google_navtree2(navtree_id, data)
3230{
3231 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003232 for (var i in data) {
3233 var node_data = data[i];
3234 $containerUl.append(new_google_node2(node_data));
3235 }
3236
Scott Main70557ee2013-10-30 14:47:40 -07003237 // Make all third-generation list items 'sticky' to prevent them from collapsing
3238 $containerUl.find('li li li.nav-section').addClass('sticky');
3239
Scott Mainad08f072013-08-20 16:49:57 -07003240 initExpandableNavItems("#"+navtree_id);
3241}
3242
3243function new_google_node2(node_data)
3244{
Scott Maine624b3f2013-09-12 12:56:41 -07003245 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003246 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3247 linkText = linkText.substr(19, linkText.length);
3248 }
3249 var $li = $('<li>');
3250 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003251 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003252 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3253 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003254 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003255 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3256 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003257 }
3258 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003259 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003260 $li.addClass("nav-section");
3261 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003262 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003263
Scott Maine624b3f2013-09-12 12:56:41 -07003264 for (var i in node_data[NODE_CHILDREN]) {
3265 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003266 $childUl.append(new_google_node2(child_node_data));
3267 }
3268 $li.append($childUl);
3269 }
3270 $li.prepend($a);
3271
3272 return $li;
3273}
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
Robert Lyd2dd6e52012-11-29 21:28:48 -08003285function showGoogleRefTree() {
3286 init_default_google_navtree(toRoot);
3287 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003288}
3289
3290function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003291 // load json file for navtree data
3292 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3293 // when the file is loaded, initialize the tree
3294 if(jqxhr.status === 200) {
3295 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3296 highlightSidenav();
3297 resizeNav();
3298 }
3299 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003300}
3301
3302function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003303 // load json file for navtree data
3304 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3305 // when the file is loaded, initialize the tree
3306 if(jqxhr.status === 200) {
3307 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3308 highlightSidenav();
3309 resizeNav();
3310 }
3311 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003312}
3313
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003314function showSamplesRefTree() {
3315 init_default_samples_navtree(toRoot);
3316}
3317
3318function init_default_samples_navtree(toroot) {
3319 // load json file for navtree data
3320 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3321 // when the file is loaded, initialize the tree
3322 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003323 // hack to remove the "about the samples" link then put it back in
3324 // after we nuke the list to remove the dummy static list of samples
3325 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3326 $("#nav.samples-nav").empty();
3327 $("#nav.samples-nav").append($firstLi);
3328
Scott Mainad08f072013-08-20 16:49:57 -07003329 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003330 highlightSidenav();
3331 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003332 if ($("#jd-content #samples").length) {
3333 showSamples();
3334 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003335 }
3336 });
3337}
3338
Scott Mainf5089842012-08-14 16:31:07 -07003339/* TOGGLE INHERITED MEMBERS */
3340
3341/* Toggle an inherited class (arrow toggle)
3342 * @param linkObj The link that was clicked.
3343 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3344 * 'null' to simply toggle.
3345 */
3346function toggleInherited(linkObj, expand) {
3347 var base = linkObj.getAttribute("id");
3348 var list = document.getElementById(base + "-list");
3349 var summary = document.getElementById(base + "-summary");
3350 var trigger = document.getElementById(base + "-trigger");
3351 var a = $(linkObj);
3352 if ( (expand == null && a.hasClass("closed")) || expand ) {
3353 list.style.display = "none";
3354 summary.style.display = "block";
3355 trigger.src = toRoot + "assets/images/triangle-opened.png";
3356 a.removeClass("closed");
3357 a.addClass("opened");
3358 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3359 list.style.display = "block";
3360 summary.style.display = "none";
3361 trigger.src = toRoot + "assets/images/triangle-closed.png";
3362 a.removeClass("opened");
3363 a.addClass("closed");
3364 }
3365 return false;
3366}
3367
3368/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3369 * @param linkObj The link that was clicked.
3370 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3371 * 'null' to simply toggle.
3372 */
3373function toggleAllInherited(linkObj, expand) {
3374 var a = $(linkObj);
3375 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3376 var expandos = $(".jd-expando-trigger", table);
3377 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3378 expandos.each(function(i) {
3379 toggleInherited(this, true);
3380 });
3381 a.text("[Collapse]");
3382 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3383 expandos.each(function(i) {
3384 toggleInherited(this, false);
3385 });
3386 a.text("[Expand]");
3387 }
3388 return false;
3389}
3390
3391/* Toggle all inherited members in the class (link in the class title)
3392 */
3393function toggleAllClassInherited() {
3394 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3395 var toggles = $(".toggle-all", $("#body-content"));
3396 if (a.text() == "[Expand All]") {
3397 toggles.each(function(i) {
3398 toggleAllInherited(this, true);
3399 });
3400 a.text("[Collapse All]");
3401 } else {
3402 toggles.each(function(i) {
3403 toggleAllInherited(this, false);
3404 });
3405 a.text("[Expand All]");
3406 }
3407 return false;
3408}
3409
3410/* Expand all inherited members in the class. Used when initiating page search */
3411function ensureAllInheritedExpanded() {
3412 var toggles = $(".toggle-all", $("#body-content"));
3413 toggles.each(function(i) {
3414 toggleAllInherited(this, true);
3415 });
3416 $("#toggleAllClassInherited").text("[Collapse All]");
3417}
3418
3419
3420/* HANDLE KEY EVENTS
3421 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3422 */
3423var agent = navigator['userAgent'].toLowerCase();
3424var mac = agent.indexOf("macintosh") != -1;
3425
3426$(document).keydown( function(e) {
3427var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3428 if (control && e.which == 70) { // 70 is "F"
3429 ensureAllInheritedExpanded();
3430 }
3431});
Scott Main498d7102013-08-21 15:47:38 -07003432
3433
3434
3435
3436
3437
3438/* On-demand functions */
3439
3440/** Move sample code line numbers out of PRE block and into non-copyable column */
3441function initCodeLineNumbers() {
3442 var numbers = $("#codesample-block a.number");
3443 if (numbers.length) {
3444 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3445 }
3446
3447 $(document).ready(function() {
3448 // select entire line when clicked
3449 $("span.code-line").click(function() {
3450 if (!shifted) {
3451 selectText(this);
3452 }
3453 });
3454 // invoke line link on double click
3455 $(".code-line").dblclick(function() {
3456 document.location.hash = $(this).attr('id');
3457 });
3458 // highlight the line when hovering on the number
3459 $("#codesample-line-numbers a.number").mouseover(function() {
3460 var id = $(this).attr('href');
3461 $(id).css('background','#e7e7e7');
3462 });
3463 $("#codesample-line-numbers a.number").mouseout(function() {
3464 var id = $(this).attr('href');
3465 $(id).css('background','none');
3466 });
3467 });
3468}
3469
3470// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3471var shifted = false;
3472$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3473
3474// courtesy of jasonedelman.com
3475function selectText(element) {
3476 var doc = document
3477 , range, selection
3478 ;
3479 if (doc.body.createTextRange) { //ms
3480 range = doc.body.createTextRange();
3481 range.moveToElementText(element);
3482 range.select();
3483 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003484 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003485 range = doc.createRange();
3486 range.selectNodeContents(element);
3487 selection.removeAllRanges();
3488 selection.addRange(range);
3489 }
Scott Main285f0772013-08-22 23:22:09 +00003490}
Scott Main03aca9a2013-10-31 07:20:55 -07003491
3492
3493
3494
3495/** Display links and other information about samples that match the
3496 group specified by the URL */
3497function showSamples() {
3498 var group = $("#samples").attr('class');
3499 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3500
3501 var $ul = $("<ul>");
3502 $selectedLi = $("#nav li.selected");
3503
3504 $selectedLi.children("ul").children("li").each(function() {
3505 var $li = $("<li>").append($(this).find("a").first().clone());
3506 $ul.append($li);
3507 });
3508
3509 $("#samples").append($ul);
3510
3511}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003512
3513
3514
3515/* ########################################################## */
3516/* ################### RESOURCE CARDS ##################### */
3517/* ########################################################## */
3518
3519/** Handle resource queries, collections, and grids (sections). Requires
3520 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3521
3522(function() {
3523 // Prevent the same resource from being loaded more than once per page.
3524 var addedPageResources = {};
3525
3526 $(document).ready(function() {
3527 $('.resource-widget').each(function() {
3528 initResourceWidget(this);
3529 });
3530
3531 /* Pass the line height to ellipsisfade() to adjust the height of the
3532 text container to show the max number of lines possible, without
3533 showing lines that are cut off. This works with the css ellipsis
3534 classes to fade last text line and apply an ellipsis char. */
3535
Scott Mainb16376f2014-05-21 20:35:47 -07003536 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003537 var lineHeight = 15;
3538 $('.card-info .text').ellipsisfade(lineHeight);
3539 });
3540
3541 /*
3542 Three types of resource layouts:
3543 Flow - Uses a fixed row-height flow using float left style.
3544 Carousel - Single card slideshow all same dimension absolute.
3545 Stack - Uses fixed columns and flexible element height.
3546 */
3547 function initResourceWidget(widget) {
3548 var $widget = $(widget);
3549 var isFlow = $widget.hasClass('resource-flow-layout'),
3550 isCarousel = $widget.hasClass('resource-carousel-layout'),
3551 isStack = $widget.hasClass('resource-stack-layout');
3552
3553 // find size of widget by pulling out its class name
3554 var sizeCols = 1;
3555 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3556 if (m) {
3557 sizeCols = parseInt(m[1], 10);
3558 }
3559
3560 var opts = {
3561 cardSizes: ($widget.data('cardsizes') || '').split(','),
3562 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3563 itemsPerPage: $widget.data('itemsperpage'),
3564 sortOrder: $widget.data('sortorder'),
3565 query: $widget.data('query'),
3566 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003567 sizeCols: sizeCols,
3568 /* Added by LFL 6/6/14 */
3569 resourceStyle: $widget.data('resourcestyle') || 'card',
3570 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003571 };
3572
3573 // run the search for the set of resources to show
3574
3575 var resources = buildResourceList(opts);
3576
3577 if (isFlow) {
3578 drawResourcesFlowWidget($widget, opts, resources);
3579 } else if (isCarousel) {
3580 drawResourcesCarouselWidget($widget, opts, resources);
3581 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003582 /* Looks like this got removed and is not used, so repurposing for the
3583 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003584 Modified by LFL 6/6/14
3585 */
3586 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003587 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003588 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003589 }
3590 }
3591
3592 /* Initializes a Resource Carousel Widget */
3593 function drawResourcesCarouselWidget($widget, opts, resources) {
3594 $widget.empty();
3595 var plusone = true; //always show plusone on carousel
3596
3597 $widget.addClass('resource-card slideshow-container')
3598 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3599 .append($('<a>').addClass('slideshow-next').text('Next'));
3600
3601 var css = { 'width': $widget.width() + 'px',
3602 'height': $widget.height() + 'px' };
3603
3604 var $ul = $('<ul>');
3605
3606 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003607 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003608 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003609 .decorateResourceCard(resources[i],plusone);
3610
3611 $('<li>').css(css)
3612 .append($card)
3613 .appendTo($ul);
3614 }
3615
3616 $('<div>').addClass('frame')
3617 .append($ul)
3618 .appendTo($widget);
3619
3620 $widget.dacSlideshow({
3621 auto: true,
3622 btnPrev: '.slideshow-prev',
3623 btnNext: '.slideshow-next'
3624 });
3625 };
3626
Robert Lye7eeb402014-06-03 19:35:24 -07003627 /* Initializes a Resource Card Stack Widget (column-based layout)
3628 Modified by LFL 6/6/14
3629 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003630 function drawResourcesStackWidget($widget, opts, resources, sections) {
3631 // Don't empty widget, grab all items inside since they will be the first
3632 // items stacked, followed by the resource query
3633 var plusone = true; //by default show plusone on section cards
3634 var cards = $widget.find('.resource-card').detach().toArray();
3635 var numStacks = opts.numStacks || 1;
3636 var $stacks = [];
3637 var urlString;
3638
3639 for (var i = 0; i < numStacks; ++i) {
3640 $stacks[i] = $('<div>').addClass('resource-card-stack')
3641 .appendTo($widget);
3642 }
3643
3644 var sectionResources = [];
3645
3646 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003647 if (sections) {
3648 for (var i = 0; i < sections.length; ++i) {
3649 if (!sections[i].sections || !sections[i].sections.length) {
3650 // Render it as a resource card
3651 sectionResources.push(
3652 $('<a>')
3653 .addClass('resource-card section-card')
3654 .attr('href', cleanUrl(sections[i].resource.url))
3655 .decorateResourceCard(sections[i].resource,plusone)[0]
3656 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003657
Robert Lye7eeb402014-06-03 19:35:24 -07003658 } else {
3659 cards.push(
3660 $('<div>')
3661 .addClass('resource-card section-card-menu')
3662 .decorateResourceSection(sections[i],plusone)[0]
3663 );
3664 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003665 }
3666 }
3667
3668 cards = cards.concat(sectionResources);
3669
3670 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003671 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003672
Robert Lye7eeb402014-06-03 19:35:24 -07003673 if (opts.resourceStyle.indexOf('related') > -1) {
3674 $card.addClass('related-card');
3675 }
smain@google.com95948b82014-06-16 19:24:25 -07003676
Dirk Doughertyc3921652014-05-13 16:55:26 -07003677 cards.push($card[0]);
3678 }
3679
Robert Lye7eeb402014-06-03 19:35:24 -07003680 if (opts.stackSort != 'false') {
3681 for (var i = 0; i < cards.length; ++i) {
3682 // Find the stack with the shortest height, but give preference to
3683 // left to right order.
3684 var minHeight = $stacks[0].height();
3685 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003686
Robert Lye7eeb402014-06-03 19:35:24 -07003687 for (var j = 1; j < numStacks; ++j) {
3688 var height = $stacks[j].height();
3689 if (height < minHeight - 45) {
3690 minHeight = height;
3691 minIndex = j;
3692 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003693 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003694
Robert Lye7eeb402014-06-03 19:35:24 -07003695 $stacks[minIndex].append($(cards[i]));
3696 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003697 }
3698
3699 };
smain@google.com95948b82014-06-16 19:24:25 -07003700
3701 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003702 Create a resource card using the given resource object and a list of html
3703 configured options. Returns a jquery object containing the element.
3704 */
smain@google.com95948b82014-06-16 19:24:25 -07003705 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003706 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003707
Robert Lye7eeb402014-06-03 19:35:24 -07003708 // The difference here is that generic cards are not entirely clickable
3709 // so its a div instead of an a tag, also the generic one is not given
3710 // the resource-card class so it appears with a transparent background
3711 // and can be styled in whatever way the css setup.
3712 if (opts.resourceStyle == 'generic') {
3713 $el = $('<div>')
3714 .addClass('resource')
3715 .attr('href', cleanUrl(resource.url))
3716 .decorateResource(resource, opts);
3717 } else {
3718 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003719
Robert Lye7eeb402014-06-03 19:35:24 -07003720 $el = $('<a>')
3721 .addClass(cls)
3722 .attr('href', cleanUrl(resource.url))
3723 .decorateResourceCard(resource, plusone);
3724 }
smain@google.com95948b82014-06-16 19:24:25 -07003725
Robert Lye7eeb402014-06-03 19:35:24 -07003726 return $el;
3727 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003728
3729 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3730 function drawResourcesFlowWidget($widget, opts, resources) {
3731 $widget.empty();
3732 var cardSizes = opts.cardSizes || ['6x6'];
3733 var i = 0, j = 0;
3734 var plusone = true; // by default show plusone on resource cards
3735
3736 while (i < resources.length) {
3737 var cardSize = cardSizes[j++ % cardSizes.length];
3738 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003739 // Some card sizes do not get a plusone button, such as where space is constrained
3740 // or for cards commonly embedded in docs (to improve overall page speed).
3741 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3742 (cardSize == "9x2") || (cardSize == "9x3") ||
3743 (cardSize == "12x2") || (cardSize == "12x3"));
3744
3745 // A stack has a third dimension which is the number of stacked items
3746 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3747 var stackCount = 0;
3748 var $stackDiv = null;
3749
3750 if (isStack) {
3751 // Create a stack container which should have the dimensions defined
3752 // by the product of the items inside.
3753 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3754 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3755 }
3756
3757 // Build each stack item or just a single item
3758 do {
3759 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003760
Robert Lye7eeb402014-06-03 19:35:24 -07003761 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003762
3763 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003764 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003765
Dirk Doughertyc3921652014-05-13 16:55:26 -07003766 if (isStack) {
3767 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3768 if (++stackCount == parseInt(isStack[3])) {
3769 $card.addClass('resource-card-row-stack-last');
3770 stackCount = 0;
3771 }
3772 } else {
3773 stackCount = 0;
3774 }
3775
Robert Lye7eeb402014-06-03 19:35:24 -07003776 $card.appendTo($stackDiv || $widget);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003777
3778 } while (++i < resources.length && stackCount > 0);
3779 }
3780 }
3781
3782 /* Build a site map of resources using a section as a root. */
3783 function buildSectionList(opts) {
3784 if (opts.section && SECTION_BY_ID[opts.section]) {
3785 return SECTION_BY_ID[opts.section].sections || [];
3786 }
3787 return [];
3788 }
3789
3790 function buildResourceList(opts) {
3791 var maxResults = opts.maxResults || 100;
3792
3793 var query = opts.query || '';
3794 var expressions = parseResourceQuery(query);
3795 var addedResourceIndices = {};
3796 var results = [];
3797
3798 for (var i = 0; i < expressions.length; i++) {
3799 var clauses = expressions[i];
3800
3801 // build initial set of resources from first clause
3802 var firstClause = clauses[0];
3803 var resources = [];
3804 switch (firstClause.attr) {
3805 case 'type':
3806 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3807 break;
3808 case 'lang':
3809 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3810 break;
3811 case 'tag':
3812 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3813 break;
3814 case 'collection':
3815 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3816 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3817 break;
3818 case 'section':
3819 var urls = SITE_MAP[firstClause.value].sections || [];
3820 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3821 break;
3822 }
3823 // console.log(firstClause.attr + ':' + firstClause.value);
3824 resources = resources || [];
3825
3826 // use additional clauses to filter corpus
3827 if (clauses.length > 1) {
3828 var otherClauses = clauses.slice(1);
3829 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3830 }
3831
3832 // filter out resources already added
3833 if (i > 1) {
3834 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3835 }
3836
3837 // add to list of already added indices
3838 for (var j = 0; j < resources.length; j++) {
3839 // console.log(resources[j].title);
3840 addedResourceIndices[resources[j].index] = 1;
3841 }
3842
3843 // concat to final results list
3844 results = results.concat(resources);
3845 }
3846
3847 if (opts.sortOrder && results.length) {
3848 var attr = opts.sortOrder;
3849
3850 if (opts.sortOrder == 'random') {
3851 var i = results.length, j, temp;
3852 while (--i) {
3853 j = Math.floor(Math.random() * (i + 1));
3854 temp = results[i];
3855 results[i] = results[j];
3856 results[j] = temp;
3857 }
3858 } else {
3859 var desc = attr.charAt(0) == '-';
3860 if (desc) {
3861 attr = attr.substring(1);
3862 }
3863 results = results.sort(function(x,y) {
3864 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3865 });
3866 }
3867 }
3868
3869 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3870 results = results.slice(0, maxResults);
3871
3872 for (var j = 0; j < results.length; ++j) {
3873 addedPageResources[results[j].index] = 1;
3874 }
3875
3876 return results;
3877 }
3878
3879
3880 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3881 return function(resource) {
3882 return !addedResourceIndices[resource.index];
3883 };
3884 }
3885
3886
3887 function getResourceMatchesClausesFilter(clauses) {
3888 return function(resource) {
3889 return doesResourceMatchClauses(resource, clauses);
3890 };
3891 }
3892
3893
3894 function doesResourceMatchClauses(resource, clauses) {
3895 for (var i = 0; i < clauses.length; i++) {
3896 var map;
3897 switch (clauses[i].attr) {
3898 case 'type':
3899 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3900 break;
3901 case 'lang':
3902 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3903 break;
3904 case 'tag':
3905 map = IS_RESOURCE_TAGGED[clauses[i].value];
3906 break;
3907 }
3908
3909 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3910 return clauses[i].negative;
3911 }
3912 }
3913 return true;
3914 }
smain@google.com95948b82014-06-16 19:24:25 -07003915
Robert Lye7eeb402014-06-03 19:35:24 -07003916 function cleanUrl(url)
3917 {
3918 if (url && url.indexOf('//') === -1) {
3919 url = toRoot + url;
3920 }
smain@google.com95948b82014-06-16 19:24:25 -07003921
Robert Lye7eeb402014-06-03 19:35:24 -07003922 return url;
3923 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003924
3925
3926 function parseResourceQuery(query) {
3927 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3928 var expressions = [];
3929 var expressionStrs = query.split(',') || [];
3930 for (var i = 0; i < expressionStrs.length; i++) {
3931 var expr = expressionStrs[i] || '';
3932
3933 // Break expression into clauses (clause e.g. 'tag:foo')
3934 var clauses = [];
3935 var clauseStrs = expr.split(/(?=[\+\-])/);
3936 for (var j = 0; j < clauseStrs.length; j++) {
3937 var clauseStr = clauseStrs[j] || '';
3938
3939 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3940 var parts = clauseStr.split(':');
3941 var clause = {};
3942
3943 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3944 if (clause.attr) {
3945 if (clause.attr.charAt(0) == '+') {
3946 clause.attr = clause.attr.substring(1);
3947 } else if (clause.attr.charAt(0) == '-') {
3948 clause.negative = true;
3949 clause.attr = clause.attr.substring(1);
3950 }
3951 }
3952
3953 if (parts.length > 1) {
3954 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3955 }
3956
3957 clauses.push(clause);
3958 }
3959
3960 if (!clauses.length) {
3961 continue;
3962 }
3963
3964 expressions.push(clauses);
3965 }
3966
3967 return expressions;
3968 }
3969})();
3970
3971(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003972
smain@google.com95948b82014-06-16 19:24:25 -07003973 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003974 Utility method for creating dom for the description area of a card.
3975 Used in decorateResourceCard and decorateResource.
3976 */
3977 function buildResourceCardDescription(resource, plusone) {
3978 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07003979
Robert Lye7eeb402014-06-03 19:35:24 -07003980 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07003981
Robert Lye7eeb402014-06-03 19:35:24 -07003982 if (resource.cta) {
3983 $description.append($('<a>').addClass('cta').html(resource.cta));
3984 }
smain@google.com95948b82014-06-16 19:24:25 -07003985
Robert Lye7eeb402014-06-03 19:35:24 -07003986 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07003987 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07003988 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003989
Robert Lye7eeb402014-06-03 19:35:24 -07003990 $description.append($('<div>').addClass('util')
3991 .append($('<div>').addClass('g-plusone')
3992 .attr('data-size', 'small')
3993 .attr('data-align', 'right')
3994 .attr('data-href', plusurl)));
3995 }
smain@google.com95948b82014-06-16 19:24:25 -07003996
Robert Lye7eeb402014-06-03 19:35:24 -07003997 return $description;
3998 }
smain@google.com95948b82014-06-16 19:24:25 -07003999
4000
Dirk Doughertyc3921652014-05-13 16:55:26 -07004001 /* Simple jquery function to create dom for a standard resource card */
4002 $.fn.decorateResourceCard = function(resource,plusone) {
4003 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07004004 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004005 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07004006
Robert Lye7eeb402014-06-03 19:35:24 -07004007 if (imgUrl.indexOf('//') === -1) {
4008 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07004009 }
Robert Lye7eeb402014-06-03 19:35:24 -07004010
4011 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07004012 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07004013 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07004014 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07004015
Robert Lye7eeb402014-06-03 19:35:24 -07004016 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
4017 .append($('<div>').addClass('section').text(section))
4018 .append($('<div>').addClass('title').html(resource.title))
4019 .append(buildResourceCardDescription(resource, plusone))
4020 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07004021
4022 return this;
4023 };
4024
4025 /* Simple jquery function to create dom for a resource section card (menu) */
4026 $.fn.decorateResourceSection = function(section,plusone) {
4027 var resource = section.resource;
4028 //keep url clean for matching and offline mode handling
4029 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
4030 var $base = $('<a>')
4031 .addClass('card-bg')
4032 .attr('href', resource.url)
4033 .append($('<div>').addClass('card-section-icon')
4034 .append($('<div>').addClass('icon'))
4035 .append($('<div>').addClass('section').html(resource.title)))
4036 .appendTo(this);
4037
4038 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
4039
4040 if (section.sections && section.sections.length) {
4041 // Recurse the section sub-tree to find a resource image.
4042 var stack = [section];
4043
4044 while (stack.length) {
4045 if (stack[0].resource.image) {
4046 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
4047 break;
4048 }
4049
4050 if (stack[0].sections) {
4051 stack = stack.concat(stack[0].sections);
4052 }
4053
4054 stack.shift();
4055 }
4056
4057 var $ul = $('<ul>')
4058 .appendTo($cardInfo);
4059
4060 var max = section.sections.length > 3 ? 3 : section.sections.length;
4061
4062 for (var i = 0; i < max; ++i) {
4063
4064 var subResource = section.sections[i];
4065 if (!plusone) {
4066 $('<li>')
4067 .append($('<a>').attr('href', subResource.url)
4068 .append($('<div>').addClass('title').html(subResource.title))
4069 .append($('<div>').addClass('description ellipsis')
4070 .append($('<div>').addClass('text').html(subResource.summary))
4071 .append($('<div>').addClass('util'))))
4072 .appendTo($ul);
4073 } else {
4074 $('<li>')
4075 .append($('<a>').attr('href', subResource.url)
4076 .append($('<div>').addClass('title').html(subResource.title))
4077 .append($('<div>').addClass('description ellipsis')
4078 .append($('<div>').addClass('text').html(subResource.summary))
4079 .append($('<div>').addClass('util')
4080 .append($('<div>').addClass('g-plusone')
4081 .attr('data-size', 'small')
4082 .attr('data-align', 'right')
4083 .attr('data-href', resource.url)))))
4084 .appendTo($ul);
4085 }
4086 }
4087
4088 // Add a more row
4089 if (max < section.sections.length) {
4090 $('<li>')
4091 .append($('<a>').attr('href', resource.url)
4092 .append($('<div>')
4093 .addClass('title')
4094 .text('More')))
4095 .appendTo($ul);
4096 }
4097 } else {
4098 // No sub-resources, just render description?
4099 }
4100
4101 return this;
4102 };
smain@google.com95948b82014-06-16 19:24:25 -07004103
4104
4105
4106
Robert Lye7eeb402014-06-03 19:35:24 -07004107 /* Render other types of resource styles that are not cards. */
4108 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07004109 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004110 'assets/images/resource-card-default-android.jpg';
4111 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07004112
Robert Lye7eeb402014-06-03 19:35:24 -07004113 if (imgUrl.indexOf('//') === -1) {
4114 imgUrl = toRoot + imgUrl;
4115 }
smain@google.com95948b82014-06-16 19:24:25 -07004116
Robert Lye7eeb402014-06-03 19:35:24 -07004117 if (linkUrl && linkUrl.indexOf('//') === -1) {
4118 linkUrl = toRoot + linkUrl;
4119 }
4120
4121 $(this).append(
4122 $('<div>').addClass('image')
4123 .css('background-image', 'url(' + imgUrl + ')'),
4124 $('<div>').addClass('info').append(
4125 $('<h4>').addClass('title').html(resource.title),
4126 $('<p>').addClass('summary').html(resource.summary),
4127 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4128 )
4129 );
4130
4131 return this;
4132 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004133})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004134
4135
Dirk Doughertyc3921652014-05-13 16:55:26 -07004136/* Calculate the vertical area remaining */
4137(function($) {
4138 $.fn.ellipsisfade= function(lineHeight) {
4139 this.each(function() {
4140 // get element text
4141 var $this = $(this);
4142 var remainingHeight = $this.parent().parent().height();
4143 $this.parent().siblings().each(function ()
smain@google.comc91ecb72014-06-23 10:22:23 -07004144 {
smain@google.comcda1a9a2014-06-19 17:07:46 -07004145 if ($(this).is(":visible")) {
4146 var h = $(this).height();
4147 remainingHeight = remainingHeight - h;
4148 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07004149 });
4150
4151 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4152 $this.parent().css({'height': adjustedRemainingHeight});
4153 $this.css({'height': "auto"});
4154 });
4155
4156 return this;
4157 };
4158}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004159
4160/*
4161 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004162
Robert Lye7eeb402014-06-03 19:35:24 -07004163 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004164 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004165 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004166
Robert Lye7eeb402014-06-03 19:35:24 -07004167 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004168
Robert Lye7eeb402014-06-03 19:35:24 -07004169 <div class="fullscreen-carousel">
4170 <div class="fullscreen-carousel-content">
4171 <!-- content here -->
4172 </div>
4173 <div class="fullscreen-carousel-content">
4174 <!-- content here -->
4175 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004176
Robert Lye7eeb402014-06-03 19:35:24 -07004177 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004178
Robert Lye7eeb402014-06-03 19:35:24 -07004179 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004180
Robert Lye7eeb402014-06-03 19:35:24 -07004181 Control over how the carousel takes over the screen can mostly be defined in
4182 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004183 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004184 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004185 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004186 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004187
Robert Lye7eeb402014-06-03 19:35:24 -07004188 There is limited functionality for having multiple sections since that request
4189 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4190 scroll between multiple content areas.
4191*/
4192
4193(function() {
4194 $(document).ready(function() {
4195 $('.fullscreen-carousel').each(function() {
4196 initWidget(this);
4197 });
4198 });
4199
4200 function initWidget(widget) {
4201 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004202
Robert Lye7eeb402014-06-03 19:35:24 -07004203 var topOffset = $widget.offset().top;
4204 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4205 var maxHeight = 0;
4206 var minHeight = 0;
4207 var $content = $widget.find('.fullscreen-carousel-content');
4208 var $nextArrow = $widget.find('.next-arrow');
4209 var $prevArrow = $widget.find('.prev-arrow');
4210 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004211
Robert Lye7eeb402014-06-03 19:35:24 -07004212 if ($content.length <= 1) {
4213 $nextArrow.hide();
4214 $prevArrow.hide();
4215 } else {
4216 $nextArrow.click(function() {
4217 var index = ($content.index($curSection) + 1);
4218 $curSection.hide();
4219 $curSection = $($content[index >= $content.length ? 0 : index]);
4220 $curSection.show();
4221 });
smain@google.com95948b82014-06-16 19:24:25 -07004222
Robert Lye7eeb402014-06-03 19:35:24 -07004223 $prevArrow.click(function() {
4224 var index = ($content.index($curSection) - 1);
4225 $curSection.hide();
4226 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4227 $curSection.show();
4228 });
4229 }
4230
4231 // Just hide all content sections except first.
4232 $content.each(function(index) {
4233 if ($(this).height() > minHeight) minHeight = $(this).height();
4234 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4235 });
4236
4237 // Register for changes to window size, and trigger.
4238 $(window).resize(resizeWidget);
4239 resizeWidget();
4240
4241 function resizeWidget() {
4242 var height = $(window).height() - topOffset - padBottom;
4243 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004244 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004245 (maxHeight && height > maxHeight ? maxHeight : height));
4246 }
smain@google.com95948b82014-06-16 19:24:25 -07004247 }
Robert Lye7eeb402014-06-03 19:35:24 -07004248})();
4249
4250
4251
4252
4253
4254/*
4255 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004256
Robert Lye7eeb402014-06-03 19:35:24 -07004257 The following allows tab widgets to be installed via the html below. Each
4258 tab content section should have a data-tab attribute matching one of the
4259 nav items'. Also each tab content section should have a width matching the
4260 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004261
Robert Lye7eeb402014-06-03 19:35:24 -07004262 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004263
Robert Lye7eeb402014-06-03 19:35:24 -07004264 <div class="tab-carousel">
4265 <ul class="tab-nav">
4266 <li><a href="#" data-tab="handsets">Handsets</a>
4267 <li><a href="#" data-tab="wearable">Wearable</a>
4268 <li><a href="#" data-tab="tv">TV</a>
4269 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004270
Robert Lye7eeb402014-06-03 19:35:24 -07004271 <div class="tab-carousel-content">
4272 <div data-tab="handsets">
4273 <!--Full width content here-->
4274 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004275
Robert Lye7eeb402014-06-03 19:35:24 -07004276 <div data-tab="wearable">
4277 <!--Full width content here-->
4278 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004279
Robert Lye7eeb402014-06-03 19:35:24 -07004280 <div data-tab="tv">
4281 <!--Full width content here-->
4282 </div>
4283 </div>
4284 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004285
Robert Lye7eeb402014-06-03 19:35:24 -07004286*/
4287(function() {
4288 $(document).ready(function() {
4289 $('.tab-carousel').each(function() {
4290 initWidget(this);
4291 });
4292 });
4293
4294 function initWidget(widget) {
4295 var $widget = $(widget);
4296 var $nav = $widget.find('.tab-nav');
4297 var $anchors = $nav.find('[data-tab]');
4298 var $li = $nav.find('li');
4299 var $contentContainer = $widget.find('.tab-carousel-content');
4300 var $tabs = $contentContainer.find('[data-tab]');
4301 var $curTab = $($tabs[0]); // Current tab is first tab.
4302 var width = $widget.width();
4303
4304 // Setup nav interactivity.
4305 $anchors.click(function(evt) {
4306 evt.preventDefault();
4307 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004308 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004309 });
smain@google.com95948b82014-06-16 19:24:25 -07004310
Robert Lye7eeb402014-06-03 19:35:24 -07004311 // Add highlight for navigation on first item.
4312 var $highlight = $('<div>').addClass('highlight')
4313 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4314 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004315
Robert Lye7eeb402014-06-03 19:35:24 -07004316 // Store height since we will change contents to absolute.
4317 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004318
Robert Lye7eeb402014-06-03 19:35:24 -07004319 // Absolutely position tabs so they're ready for transition.
4320 $tabs.each(function(index) {
4321 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4322 });
smain@google.com95948b82014-06-16 19:24:25 -07004323
Robert Lye7eeb402014-06-03 19:35:24 -07004324 function transitionWidget($toTab) {
4325 if (!$curTab.is($toTab)) {
4326 var curIndex = $tabs.index($curTab[0]);
4327 var toIndex = $tabs.index($toTab[0]);
4328 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004329
Robert Lye7eeb402014-06-03 19:35:24 -07004330 // Animate content sections.
4331 $toTab.css({left:(width * dir) + 'px'});
4332 $curTab.animate({left:(width * -dir) + 'px'});
4333 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004334
Robert Lye7eeb402014-06-03 19:35:24 -07004335 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004336 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004337 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004338
Robert Lye7eeb402014-06-03 19:35:24 -07004339 // Store new current section.
4340 $curTab = $toTab;
4341 }
4342 }
smain@google.com95948b82014-06-16 19:24:25 -07004343 }
Dirk Doughertyb87e3002014-11-18 19:34:34 -08004344})();