blob: 945cfa7b2999cc1a5b2cd8f9b219b759441e7fe4 [file] [log] [blame]
Scott Maine4d8f1b2012-06-21 18:03:05 -07001var classesNav;
2var devdocNav;
3var sidenav;
4var cookie_namespace = 'android_developer';
5var NAV_PREF_TREE = "tree";
6var NAV_PREF_PANELS = "panels";
7var nav_pref;
Scott Maine4d8f1b2012-06-21 18:03:05 -07008var isMobile = false; // true if mobile, so we can adjust some layout
Scott Mainf6145542013-04-01 16:38:11 -07009var mPagePath; // initialized in ready() function
Scott Maine4d8f1b2012-06-21 18:03:05 -070010
Scott Main1b3db112012-07-03 14:06:22 -070011var basePath = getBaseUri(location.pathname);
12var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
Scott Main7e447ed2013-02-19 17:22:37 -080013var GOOGLE_DATA; // combined data for google service apis, used for search suggest
Scott Main3b90aff2013-08-01 18:09:35 -070014
Scott Main25e73002013-03-27 15:24:06 -070015// Ensure that all ajax getScript() requests allow caching
16$.ajaxSetup({
17 cache: true
18});
Scott Maine4d8f1b2012-06-21 18:03:05 -070019
20/****** ON LOAD SET UP STUFF *********/
21
Scott Maine4d8f1b2012-06-21 18:03:05 -070022$(document).ready(function() {
smain@google.com698fff02014-11-20 20:39:33 -080023
Dirk Doughertyb87e3002014-11-18 19:34:34 -080024 // show lang dialog if the URL includes /intl/
25 //if (location.pathname.substring(0,6) == "/intl/") {
26 // var lang = location.pathname.split('/')[2];
27 // if (lang != getLangPref()) {
28 // $("#langMessage a.yes").attr("onclick","changeLangPref('" + lang
29 // + "', true); $('#langMessage').hide(); return false;");
30 // $("#langMessage .lang." + lang).show();
31 // $("#langMessage").show();
32 // }
33 //}
Scott Main7e447ed2013-02-19 17:22:37 -080034
Scott Main0e76e7e2013-03-12 10:24:07 -070035 // load json file for JD doc search suggestions
Scott Main719acb42013-12-05 16:05:09 -080036 $.getScript(toRoot + 'jd_lists_unified.js');
Scott Main7e447ed2013-02-19 17:22:37 -080037 // load json file for Android API search suggestions
38 $.getScript(toRoot + 'reference/lists.js');
39 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080040 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080041 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
42 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080043 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080044 if(jqxhr.status === 200) {
45 // combine GCM and GMS data
46 GOOGLE_DATA = GMS_DATA;
47 var start = GOOGLE_DATA.length;
48 for (var i=0; i<GCM_DATA.length; i++) {
49 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
50 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
51 }
52 }
53 });
54 }
55 });
56
Scott Main0e76e7e2013-03-12 10:24:07 -070057 // setup keyboard listener for search shortcut
58 $('body').keyup(function(event) {
59 if (event.which == 191) {
60 $('#search_autocomplete').focus();
61 }
62 });
Scott Main015d6162013-01-29 09:01:52 -080063
Scott Maine4d8f1b2012-06-21 18:03:05 -070064 // init the fullscreen toggle click event
65 $('#nav-swap .fullscreen').click(function(){
66 if ($(this).hasClass('disabled')) {
67 toggleFullscreen(true);
68 } else {
69 toggleFullscreen(false);
70 }
71 });
Scott Main3b90aff2013-08-01 18:09:35 -070072
Scott Maine4d8f1b2012-06-21 18:03:05 -070073 // initialize the divs with custom scrollbars
74 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070075
Scott Maine4d8f1b2012-06-21 18:03:05 -070076 // add HRs below all H2s (except for a few other h2 variants)
Scott Mainc29b3f52014-05-30 21:18:30 -070077 $('h2').not('#qv h2')
78 .not('#tb h2')
79 .not('.sidebox h2')
80 .not('#devdoc-nav h2')
81 .not('h2.norule').css({marginBottom:0})
82 .after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070083
84 // set up the search close button
85 $('.search .close').click(function() {
86 $searchInput = $('#search_autocomplete');
87 $searchInput.attr('value', '');
88 $(this).addClass("hide");
89 $("#search-container").removeClass('active');
90 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070091 search_focus_changed($searchInput.get(), false);
92 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070093 });
94
95 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070096 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070097 $("#btn-quicknav").click(function() {
98 if (quicknav_open) {
99 $(this).removeClass('active');
100 quicknav_open = false;
101 collapse();
102 } else {
103 $(this).addClass('active');
104 quicknav_open = true;
105 expand();
106 }
107 })
Scott Main3b90aff2013-08-01 18:09:35 -0700108
Scott Maine4d8f1b2012-06-21 18:03:05 -0700109 var expand = function() {
110 $('#header-wrap').addClass('quicknav');
111 $('#quicknav').stop().show().animate({opacity:'1'});
112 }
Scott Main3b90aff2013-08-01 18:09:35 -0700113
Scott Maine4d8f1b2012-06-21 18:03:05 -0700114 var collapse = function() {
115 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
116 $(this).hide();
117 $('#header-wrap').removeClass('quicknav');
118 });
119 }
Scott Main3b90aff2013-08-01 18:09:35 -0700120
121
Scott Maine4d8f1b2012-06-21 18:03:05 -0700122 //Set up search
123 $("#search_autocomplete").focus(function() {
124 $("#search-container").addClass('active');
125 })
126 $("#search-container").mouseover(function() {
127 $("#search-container").addClass('active');
128 $("#search_autocomplete").focus();
129 })
130 $("#search-container").mouseout(function() {
131 if ($("#search_autocomplete").is(":focus")) return;
132 if ($("#search_autocomplete").val() == '') {
133 setTimeout(function(){
134 $("#search-container").removeClass('active');
135 $("#search_autocomplete").blur();
136 },250);
137 }
138 })
139 $("#search_autocomplete").blur(function() {
140 if ($("#search_autocomplete").val() == '') {
141 $("#search-container").removeClass('active');
142 }
143 })
144
Scott Main3b90aff2013-08-01 18:09:35 -0700145
Scott Maine4d8f1b2012-06-21 18:03:05 -0700146 // prep nav expandos
147 var pagePath = document.location.pathname;
148 // account for intl docs by removing the intl/*/ path
149 if (pagePath.indexOf("/intl/") == 0) {
150 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
151 }
Scott Mainac2aef52013-02-12 14:15:23 -0800152
Scott Maine4d8f1b2012-06-21 18:03:05 -0700153 if (pagePath.indexOf(SITE_ROOT) == 0) {
154 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
155 pagePath += 'index.html';
156 }
157 }
158
Scott Main01a25452013-02-12 17:32:27 -0800159 // Need a copy of the pagePath before it gets changed in the next block;
160 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
161 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700162 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
163 // If running locally, SITE_ROOT will be a relative path, so account for that by
164 // finding the relative URL to this page. This will allow us to find links on the page
165 // leading back to this page.
166 var pathParts = pagePath.split('/');
167 var relativePagePathParts = [];
168 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
169 for (var i = 0; i < upDirs; i++) {
170 relativePagePathParts.push('..');
171 }
172 for (var i = 0; i < upDirs; i++) {
173 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
174 }
175 relativePagePathParts.push(pathParts[pathParts.length - 1]);
176 pagePath = relativePagePathParts.join('/');
177 } else {
178 // Otherwise the page path is already an absolute URL
179 }
180
Scott Mainac2aef52013-02-12 14:15:23 -0800181 // Highlight the header tabs...
182 // highlight Design tab
183 if ($("body").hasClass("design")) {
184 $("#header li.design a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700185 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800186
smain@google.com6040ffa2014-06-13 15:06:23 -0700187 // highlight About tabs
188 } else if ($("body").hasClass("about")) {
189 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
190 if (rootDir == "about") {
191 $("#nav-x li.about a").addClass("selected");
192 } else if (rootDir == "wear") {
193 $("#nav-x li.wear a").addClass("selected");
194 } else if (rootDir == "tv") {
195 $("#nav-x li.tv a").addClass("selected");
196 } else if (rootDir == "auto") {
197 $("#nav-x li.auto a").addClass("selected");
198 }
Scott Mainac2aef52013-02-12 14:15:23 -0800199 // highlight Develop tab
200 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
201 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700202 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800203 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800204 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800205 if (rootDir == "training") {
206 $("#nav-x li.training a").addClass("selected");
207 } else if (rootDir == "guide") {
208 $("#nav-x li.guide a").addClass("selected");
209 } else if (rootDir == "reference") {
210 // If the root is reference, but page is also part of Google Services, select Google
211 if ($("body").hasClass("google")) {
212 $("#nav-x li.google a").addClass("selected");
213 } else {
214 $("#nav-x li.reference a").addClass("selected");
215 }
216 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
217 $("#nav-x li.tools a").addClass("selected");
218 } else if ($("body").hasClass("google")) {
219 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700220 } else if ($("body").hasClass("samples")) {
221 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800222 }
223
224 // highlight Distribute tab
225 } else if ($("body").hasClass("distribute")) {
226 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700227 $("#sticky-header").addClass("distribute");
228
229 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
230 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
231 if (secondFrag == "users") {
232 $("#nav-x li.users a").addClass("selected");
233 } else if (secondFrag == "engage") {
234 $("#nav-x li.engage a").addClass("selected");
235 } else if (secondFrag == "monetize") {
236 $("#nav-x li.monetize a").addClass("selected");
237 } else if (secondFrag == "tools") {
238 $("#nav-x li.disttools a").addClass("selected");
239 } else if (secondFrag == "stories") {
240 $("#nav-x li.stories a").addClass("selected");
241 } else if (secondFrag == "essentials") {
242 $("#nav-x li.essentials a").addClass("selected");
243 } else if (secondFrag == "googleplay") {
244 $("#nav-x li.googleplay a").addClass("selected");
245 }
246 } else if ($("body").hasClass("about")) {
247 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700248 }
Scott Mainac2aef52013-02-12 14:15:23 -0800249
Scott Mainf6145542013-04-01 16:38:11 -0700250 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
251 // and highlight the sidenav
252 mPagePath = pagePath;
253 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700254 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800255
Scott Mainf6145542013-04-01 16:38:11 -0700256 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700257 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700258 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700259 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800260 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700261
262 // set up prev links
263 var $prevLink = [];
264 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700265
Scott Maine4d8f1b2012-06-21 18:03:05 -0700266 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
267false; // navigate across topic boundaries only in design docs
268 if ($prevListItem.length) {
smain@google.comc91ecb72014-06-23 10:22:23 -0700269 if ($prevListItem.hasClass('nav-section') || crossBoundaries) {
Scott Main5a1123e2012-09-26 12:51:28 -0700270 // jump to last topic of previous section
271 $prevLink = $prevListItem.find('a:last');
272 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700273 // jump to previous topic in this section
274 $prevLink = $prevListItem.find('a:eq(0)');
275 }
276 } else {
277 // jump to this section's index page (if it exists)
278 var $parentListItem = $selListItem.parents('li');
279 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700280
Scott Maine4d8f1b2012-06-21 18:03:05 -0700281 // except if cross boundaries aren't allowed, and we're at the top of a section already
282 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700283 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700284 && $selListItem.hasClass('nav-section')) {
285 $prevLink = [];
286 }
287 }
288
Scott Maine4d8f1b2012-06-21 18:03:05 -0700289 // set up next links
290 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700291 var startClass = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700292 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700293
Scott Main1a00f7f2013-10-29 11:11:19 -0700294 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700295 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700296 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700297
298 // if there aren't any children, go to the next section (required for About pages)
299 if($nextLink.length == 0) {
300 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700301 } else if ($('.topic-start-link').length) {
302 // as long as there's a child link and there is a "topic start link" (we're on a landing)
303 // then set the landing page "start link" text to be the first doc title
304 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700305 }
Scott Main3b90aff2013-08-01 18:09:35 -0700306
Scott Main5a1123e2012-09-26 12:51:28 -0700307 // If the selected page has a description, then it's a class or article homepage
308 if ($selListItem.find('a[description]').length) {
309 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700310 startClass = true;
311 }
312 } else {
313 // jump to the next topic in this section (if it exists)
314 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700315 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700316 isCrossingBoundary = true;
317 // no more topics in this section, jump to the first topic in the next section
smain@google.comabf34112014-06-23 11:39:02 -0700318 $nextLink = $selListItem.parents('li:eq(0)').next('li').find('a:eq(0)');
Scott Main5a1123e2012-09-26 12:51:28 -0700319 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
320 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700321 if ($nextLink.length == 0) {
322 // if that doesn't work, we're at the end of the list, so disable NEXT link
323 $('.next-page-link').attr('href','').addClass("disabled")
324 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700325 // and completely hide the one in the footer
326 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700327 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700328 }
329 }
330 }
Scott Main5a1123e2012-09-26 12:51:28 -0700331
332 if (startClass) {
333 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
334
Scott Main3b90aff2013-08-01 18:09:35 -0700335 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700336 // then we need to add a bottom border to button
337 if (!$("#tb").length) {
338 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700339 }
Scott Main5a1123e2012-09-26 12:51:28 -0700340 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
341 $('.content-footer.next-class').show();
342 $('.next-page-link').attr('href','')
343 .removeClass("hide").addClass("disabled")
344 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700345 // and completely hide the one in the footer
346 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700347 if ($nextLink.length) {
348 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700349 .removeClass("hide")
350 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700351 $('.next-class-link').find('.new').empty();
352 }
Scott Main5a1123e2012-09-26 12:51:28 -0700353 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700354 $('.next-page-link').attr('href', $nextLink.attr('href'))
355 .removeClass("hide");
356 // for the footer link, also add the next page title
357 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700358 }
359
360 if (!startClass && $prevLink.length) {
361 var prevHref = $prevLink.attr('href');
362 if (prevHref == SITE_ROOT + 'index.html') {
363 // Don't show Previous when it leads to the homepage
364 } else {
365 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
366 }
Scott Main3b90aff2013-08-01 18:09:35 -0700367 }
Scott Main5a1123e2012-09-26 12:51:28 -0700368
Scott Maine4d8f1b2012-06-21 18:03:05 -0700369 }
Scott Main3b90aff2013-08-01 18:09:35 -0700370
371
372
Scott Main5a1123e2012-09-26 12:51:28 -0700373 // Set up the course landing pages for Training with class names and descriptions
374 if ($('body.trainingcourse').length) {
375 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700376
377 // create an array for all the class descriptions
378 var $classDescriptions = new Array($classLinks.length);
379 var lang = getLangPref();
380 $classLinks.each(function(index) {
381 var langDescr = $(this).attr(lang + "-description");
382 if (typeof langDescr !== 'undefined' && langDescr !== false) {
383 // if there's a class description in the selected language, use that
384 $classDescriptions[index] = langDescr;
385 } else {
386 // otherwise, use the default english description
387 $classDescriptions[index] = $(this).attr("description");
388 }
389 });
Scott Main3b90aff2013-08-01 18:09:35 -0700390
Scott Main5a1123e2012-09-26 12:51:28 -0700391 var $olClasses = $('<ol class="class-list"></ol>');
392 var $liClass;
393 var $imgIcon;
394 var $h2Title;
395 var $pSummary;
396 var $olLessons;
397 var $liLesson;
398 $classLinks.each(function(index) {
399 $liClass = $('<li></li>');
400 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700401 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700402
Scott Main5a1123e2012-09-26 12:51:28 -0700403 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700404
Scott Main5a1123e2012-09-26 12:51:28 -0700405 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700406
Scott Main5a1123e2012-09-26 12:51:28 -0700407 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700408 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
409 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700410 $lessons.each(function(index) {
411 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
412 });
413 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700414 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
415 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700416 $pSummary.addClass('article');
417 }
418
419 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
420 $olClasses.append($liClass);
421 });
422 $('.jd-descr').append($olClasses);
423 }
424
Scott Maine4d8f1b2012-06-21 18:03:05 -0700425 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700426 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700427
Scott Main3b90aff2013-08-01 18:09:35 -0700428
Scott Maine4d8f1b2012-06-21 18:03:05 -0700429 $(".scroll-pane").scroll(function(event) {
430 event.preventDefault();
431 return false;
432 });
433
434 /* Resize nav height when window height changes */
435 $(window).resize(function() {
436 if ($('#side-nav').length == 0) return;
437 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
438 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
439 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700440 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700441 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
442 updateSideNavPosition();
443 } else {
444 updateSidenavFullscreenWidth();
445 }
446 }
447 resizeNav();
448 });
449
450
Scott Maine4d8f1b2012-06-21 18:03:05 -0700451 var navBarLeftPos;
452 if ($('#devdoc-nav').length) {
453 setNavBarLeftPos();
454 }
455
456
Scott Maine4d8f1b2012-06-21 18:03:05 -0700457 // Set up play-on-hover <video> tags.
458 $('video.play-on-hover').bind('click', function(){
459 $(this).get(0).load(); // in case the video isn't seekable
460 $(this).get(0).play();
461 });
462
463 // Set up tooltips
464 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700465 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700466 var $target = $(this);
467 var $tooltip = $('<div>')
468 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700469 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700470 .hide()
471 .appendTo('body');
472 $target.removeAttr('title');
473
474 $target.hover(function() {
475 // in
476 var targetRect = $target.offset();
477 targetRect.width = $target.width();
478 targetRect.height = $target.height();
479
480 $tooltip.css({
481 left: targetRect.left,
482 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
483 });
484 $tooltip.addClass('below');
485 $tooltip.show();
486 }, function() {
487 // out
488 $tooltip.hide();
489 });
490 });
491
492 // Set up <h2> deeplinks
493 $('h2').click(function() {
494 var id = $(this).attr('id');
495 if (id) {
496 document.location.hash = id;
497 }
498 });
499
500 //Loads the +1 button
501 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
502 po.src = 'https://apis.google.com/js/plusone.js';
503 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
504
505
Scott Main3b90aff2013-08-01 18:09:35 -0700506 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700507 // which avoids the visible width from changing each time the bar appears
508 var $sidenav = $("#side-nav");
509 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700510
Scott Maine4d8f1b2012-06-21 18:03:05 -0700511 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
512
513
514 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700515
Scott Maine4d8f1b2012-06-21 18:03:05 -0700516 if ($(".scroll-pane").length > 1) {
517 // Check if there's a user preference for the panel heights
518 var cookieHeight = readCookie("reference_height");
519 if (cookieHeight) {
520 restoreHeight(cookieHeight);
521 }
522 }
Scott Main3b90aff2013-08-01 18:09:35 -0700523
Scott Main06f3f2c2014-05-30 11:23:00 -0700524 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700525 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700526 // Check if there's an anchor that we need to scroll into view.
527 // A delay is needed, because some browsers do not immediately scroll down to the anchor
528 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700529
Scott Main015d6162013-01-29 09:01:52 -0800530 /* init the language selector based on user cookie for lang */
531 loadLangPref();
532 changeNavLang(getLangPref());
533
534 /* setup event handlers to ensure the overflow menu is visible while picking lang */
535 $("#language select")
536 .mousedown(function() {
537 $("div.morehover").addClass("hover"); })
538 .blur(function() {
539 $("div.morehover").removeClass("hover"); });
540
541 /* some global variable setup */
542 resizePackagesNav = $("#resize-packages-nav");
543 classesNav = $("#classes-nav");
544 devdocNav = $("#devdoc-nav");
545
546 var cookiePath = "";
547 if (location.href.indexOf("/reference/") != -1) {
548 cookiePath = "reference_";
549 } else if (location.href.indexOf("/guide/") != -1) {
550 cookiePath = "guide_";
551 } else if (location.href.indexOf("/tools/") != -1) {
552 cookiePath = "tools_";
553 } else if (location.href.indexOf("/training/") != -1) {
554 cookiePath = "training_";
555 } else if (location.href.indexOf("/design/") != -1) {
556 cookiePath = "design_";
557 } else if (location.href.indexOf("/distribute/") != -1) {
558 cookiePath = "distribute_";
559 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700560
smain@google.com698fff02014-11-20 20:39:33 -0800561
562 /* setup shadowbox for any videos that want it */
smain@google.comf75ee212014-11-24 09:42:59 -0800563 var $videoLinks = $("a.video-shadowbox-button, a.notice-developers-video");
smain@google.com698fff02014-11-20 20:39:33 -0800564 if ($videoLinks.length) {
565 // if there's at least one, add the shadowbox HTML to the body
566 $('body').prepend(
567'<div id="video-container">'+
568 '<div id="video-frame">'+
569 '<div class="video-close">'+
570 '<span id="icon-video-close" onclick="closeVideo()">&nbsp;</span>'+
571 '</div>'+
572 '<div id="youTubePlayer"></div>'+
573 '</div>'+
574'</div>');
575
576 // loads the IFrame Player API code asynchronously.
577 $.getScript("https://www.youtube.com/iframe_api");
578
579 $videoLinks.each(function() {
580 var videoId = $(this).attr('href').split('?v=')[1];
581 $(this).click(function(event) {
582 event.preventDefault();
583 startYouTubePlayer(videoId);
584 });
585 });
smain@google.com698fff02014-11-20 20:39:33 -0800586 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700587});
Scott Main7e447ed2013-02-19 17:22:37 -0800588// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700589
590
smain@google.com698fff02014-11-20 20:39:33 -0800591var youTubePlayer;
592function onYouTubeIframeAPIReady() {
593}
594
595function startYouTubePlayer(videoId) {
596 if (youTubePlayer == null) {
597 youTubePlayer = new YT.Player('youTubePlayer', {
598 height: '529',
599 width: '940',
600 videoId: videoId,
601 events: {
smain@google.comf75ee212014-11-24 09:42:59 -0800602 'onReady': onPlayerReady,
603 'onStateChange': onPlayerStateChange
smain@google.com698fff02014-11-20 20:39:33 -0800604 }
605 });
606 } else {
607 youTubePlayer.playVideo();
608 }
609 $("#video-container").fadeIn(200, function(){$("#video-frame").show()});
610}
611
612function onPlayerReady(event) {
613 event.target.playVideo();
614}
615
616function closeVideo() {
617 try {
smain@google.comf75ee212014-11-24 09:42:59 -0800618 youTubePlayer.pauseVideo();
smain@google.com698fff02014-11-20 20:39:33 -0800619 $("#video-container").fadeOut(200);
620 } catch(e) {
621 console.log('Video not available');
622 $("#video-container").fadeOut(200);
623 }
624}
625
smain@google.comf75ee212014-11-24 09:42:59 -0800626/* Track youtube playback for analytics */
627function onPlayerStateChange(event) {
628 // Video starts, send the video ID
629 if (event.data == YT.PlayerState.PLAYING) {
630 ga('send', 'event', 'Videos', 'Play', youTubePlayer.getVideoUrl().split('?v=')[1]);
631 }
632 // Video paused, send video ID and video elapsed time
633 if (event.data == YT.PlayerState.PAUSED) {
634 ga('send', 'event', 'Videos', 'Paused', youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
635 }
636 // Video finished, send video ID and video elapsed time
637 if (event.data == YT.PlayerState.ENDED) {
638 ga('send', 'event', 'Videos', 'Finished', youTubePlayer.getVideoUrl().split('?v=')[1], youTubePlayer.getCurrentTime());
639 }
640}
641
smain@google.com698fff02014-11-20 20:39:33 -0800642
643
Scott Mainad08f072013-08-20 16:49:57 -0700644function initExpandableNavItems(rootTag) {
645 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
646 var section = $(this).closest('li.nav-section');
647 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700648 /* hide me and descendants */
649 section.find('ul').slideUp(250, function() {
650 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700651 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700652 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700653 resizeNav();
654 });
655 } else {
656 /* show me */
657 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700658 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700659 $others.removeClass('expanded').children('ul').slideUp(250);
660
661 // now expand me
662 section.closest('li').addClass('expanded');
663 section.children('ul').slideDown(250, function() {
664 resizeNav();
665 });
666 }
667 });
Scott Mainf0093852013-08-22 11:37:11 -0700668
669 // Stop expand/collapse behavior when clicking on nav section links
670 // (since we're navigating away from the page)
671 // This selector captures the first instance of <a>, but not those with "#" as the href.
672 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
673 window.location.href = $(this).attr('href');
674 return false;
675 });
Scott Mainad08f072013-08-20 16:49:57 -0700676}
677
Dirk Doughertyc3921652014-05-13 16:55:26 -0700678
679/** Create the list of breadcrumb links in the sticky header */
680function buildBreadcrumbs() {
681 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
682 // Add the secondary horizontal nav item, if provided
683 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
684 if ($selectedSecondNav.length) {
685 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
686 }
687 // Add the primary horizontal nav
688 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
689 // If there's no header nav item, use the logo link and title from alt text
690 if ($selectedFirstNav.length < 1) {
691 $selectedFirstNav = $("<a>")
692 .attr('href', $("div#header .logo a").attr('href'))
693 .text($("div#header .logo img").attr('alt'));
694 }
695 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
696}
697
698
699
Scott Maine624b3f2013-09-12 12:56:41 -0700700/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700701function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700702 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
703 if ($("ul#nav li.selected").length) {
704 unHighlightSidenav();
705 }
706 // look for URL in sidenav, including the hash
707 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
708
709 // If the selNavLink is still empty, look for it without the hash
710 if ($selNavLink.length == 0) {
711 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
712 }
713
Scott Mainf6145542013-04-01 16:38:11 -0700714 var $selListItem;
715 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700716 // Find this page's <li> in sidenav and set selected
717 $selListItem = $selNavLink.closest('li');
718 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700719
Scott Mainf6145542013-04-01 16:38:11 -0700720 // Traverse up the tree and expand all parent nav-sections
721 $selNavLink.parents('li.nav-section').each(function() {
722 $(this).addClass('expanded');
723 $(this).children('ul').show();
724 });
725 }
726}
727
Scott Maine624b3f2013-09-12 12:56:41 -0700728function unHighlightSidenav() {
729 $("ul#nav li.selected").removeClass("selected");
730 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
731}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700732
733function toggleFullscreen(enable) {
734 var delay = 20;
735 var enabled = true;
736 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
737 if (enable) {
738 // Currently NOT USING fullscreen; enable fullscreen
739 stylesheet.removeAttr('disabled');
740 $('#nav-swap .fullscreen').removeClass('disabled');
741 $('#devdoc-nav').css({left:''});
742 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
743 enabled = true;
744 } else {
745 // Currently USING fullscreen; disable fullscreen
746 stylesheet.attr('disabled', 'disabled');
747 $('#nav-swap .fullscreen').addClass('disabled');
748 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
749 enabled = false;
750 }
smain@google.com6bdcb982014-11-14 11:53:07 -0800751 writeCookie("fullscreen", enabled, null);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700752 setNavBarLeftPos();
753 resizeNav(delay);
754 updateSideNavPosition();
755 setTimeout(initSidenavHeightResize,delay);
756}
757
758
759function setNavBarLeftPos() {
760 navBarLeftPos = $('#body-content').offset().left;
761}
762
763
764function updateSideNavPosition() {
765 var newLeft = $(window).scrollLeft() - navBarLeftPos;
766 $('#devdoc-nav').css({left: -newLeft});
767 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
768}
Scott Main3b90aff2013-08-01 18:09:35 -0700769
Scott Maine4d8f1b2012-06-21 18:03:05 -0700770// TODO: use $(document).ready instead
771function addLoadEvent(newfun) {
772 var current = window.onload;
773 if (typeof window.onload != 'function') {
774 window.onload = newfun;
775 } else {
776 window.onload = function() {
777 current();
778 newfun();
779 }
780 }
781}
782
783var agent = navigator['userAgent'].toLowerCase();
784// If a mobile phone, set flag and do mobile setup
785if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
786 (agent.indexOf("blackberry") != -1) ||
787 (agent.indexOf("webos") != -1) ||
788 (agent.indexOf("mini") != -1)) { // opera mini browsers
789 isMobile = true;
790}
791
792
Scott Main498d7102013-08-21 15:47:38 -0700793$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700794 $("pre:not(.no-pretty-print)").addClass("prettyprint");
795 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700796});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700797
Scott Maine4d8f1b2012-06-21 18:03:05 -0700798
799
800
801/* ######### RESIZE THE SIDENAV HEIGHT ########## */
802
803function resizeNav(delay) {
804 var $nav = $("#devdoc-nav");
805 var $window = $(window);
806 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700807
Scott Maine4d8f1b2012-06-21 18:03:05 -0700808 // Get the height of entire window and the total header height.
809 // Then figure out based on scroll position whether the header is visible
810 var windowHeight = $window.height();
811 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700812 var headerHeight = $('#header-wrapper').outerHeight();
813 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700814
815 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700816 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700817 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700818 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700819
Scott Maine4d8f1b2012-06-21 18:03:05 -0700820 // Depending on whether the header is visible, set the side nav's height.
821 if (headerVisible) {
822 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700823 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700824 } else {
825 // Once header is off screen, the nav height is almost full window height
826 navHeight = windowHeight - topMargin;
827 }
Scott Main3b90aff2013-08-01 18:09:35 -0700828
829
830
Scott Maine4d8f1b2012-06-21 18:03:05 -0700831 $scrollPanes = $(".scroll-pane");
832 if ($scrollPanes.length > 1) {
833 // subtract the height of the api level widget and nav swapper from the available nav height
834 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700835
Scott Maine4d8f1b2012-06-21 18:03:05 -0700836 $("#swapper").css({height:navHeight + "px"});
837 if ($("#nav-tree").is(":visible")) {
838 $("#nav-tree").css({height:navHeight});
839 }
Scott Main3b90aff2013-08-01 18:09:35 -0700840
841 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700842 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700843
844 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700845 // then the package panel should begin to shrink
846 if (parseInt(classesHeight) <= 0) {
847 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
848 $("#packages-nav").css({height:navHeight - 10});
849 }
Scott Main3b90aff2013-08-01 18:09:35 -0700850
Scott Maine4d8f1b2012-06-21 18:03:05 -0700851 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
852 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700853
854
Scott Maine4d8f1b2012-06-21 18:03:05 -0700855 } else {
856 $nav.height(navHeight);
857 }
Scott Main3b90aff2013-08-01 18:09:35 -0700858
Scott Maine4d8f1b2012-06-21 18:03:05 -0700859 if (delay) {
860 updateFromResize = true;
861 delayedReInitScrollbars(delay);
862 } else {
863 reInitScrollbars();
864 }
Scott Main3b90aff2013-08-01 18:09:35 -0700865
Scott Maine4d8f1b2012-06-21 18:03:05 -0700866}
867
868var updateScrollbars = false;
869var updateFromResize = false;
870
871/* Re-initialize the scrollbars to account for changed nav size.
872 * This method postpones the actual update by a 1/4 second in order to optimize the
873 * scroll performance while the header is still visible, because re-initializing the
874 * scroll panes is an intensive process.
875 */
876function delayedReInitScrollbars(delay) {
877 // If we're scheduled for an update, but have received another resize request
878 // before the scheduled resize has occured, just ignore the new request
879 // (and wait for the scheduled one).
880 if (updateScrollbars && updateFromResize) {
881 updateFromResize = false;
882 return;
883 }
Scott Main3b90aff2013-08-01 18:09:35 -0700884
Scott Maine4d8f1b2012-06-21 18:03:05 -0700885 // We're scheduled for an update and the update request came from this method's setTimeout
886 if (updateScrollbars && !updateFromResize) {
887 reInitScrollbars();
888 updateScrollbars = false;
889 } else {
890 updateScrollbars = true;
891 updateFromResize = false;
892 setTimeout('delayedReInitScrollbars()',delay);
893 }
894}
895
896/* Re-initialize the scrollbars to account for changed nav size. */
897function reInitScrollbars() {
898 var pane = $(".scroll-pane").each(function(){
899 var api = $(this).data('jsp');
900 if (!api) { setTimeout(reInitScrollbars,300); return;}
901 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700902 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700903 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
904}
905
906
907/* Resize the height of the nav panels in the reference,
908 * and save the new size to a cookie */
909function saveNavPanels() {
910 var basePath = getBaseUri(location.pathname);
911 var section = basePath.substring(1,basePath.indexOf("/",1));
smain@google.com6bdcb982014-11-14 11:53:07 -0800912 writeCookie("height", resizePackagesNav.css("height"), section);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700913}
914
915
916
917function restoreHeight(packageHeight) {
918 $("#resize-packages-nav").height(packageHeight);
919 $("#packages-nav").height(packageHeight);
920 // var classesHeight = navHeight - packageHeight;
921 // $("#classes-nav").css({height:classesHeight});
922 // $("#classes-nav .jspContainer").css({height:classesHeight});
923}
924
925
926
927/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
928
929
930
931
932
Scott Main3b90aff2013-08-01 18:09:35 -0700933/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700934 This is called when the page finished loading. */
935function scrollIntoView(nav) {
936 var $nav = $("#"+nav);
937 var element = $nav.jScrollPane({/* ...settings... */});
938 var api = element.data('jsp');
939
940 if ($nav.is(':visible')) {
941 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700942 if ($selected.length == 0) {
943 // If no selected item found, exit
944 return;
945 }
Scott Main52dd2062013-08-15 12:22:28 -0700946 // get the selected item's offset from its container nav by measuring the item's offset
947 // relative to the document then subtract the container nav's offset relative to the document
948 var selectedOffset = $selected.offset().top - $nav.offset().top;
949 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
950 // if it's more than 80% down the nav
951 // scroll the item up by an amount equal to 80% the container nav's height
952 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700953 }
954 }
955}
956
957
958
959
960
961
962/* Show popup dialogs */
963function showDialog(id) {
964 $dialog = $("#"+id);
965 $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>');
966 $dialog.wrapInner('<div/>');
967 $dialog.removeClass("hide");
968}
969
970
971
972
973
974/* ######### COOKIES! ########## */
975
976function readCookie(cookie) {
977 var myCookie = cookie_namespace+"_"+cookie+"=";
978 if (document.cookie) {
979 var index = document.cookie.indexOf(myCookie);
980 if (index != -1) {
981 var valStart = index + myCookie.length;
982 var valEnd = document.cookie.indexOf(";", valStart);
983 if (valEnd == -1) {
984 valEnd = document.cookie.length;
985 }
986 var val = document.cookie.substring(valStart, valEnd);
987 return val;
988 }
989 }
990 return 0;
991}
992
smain@google.com6bdcb982014-11-14 11:53:07 -0800993function writeCookie(cookie, val, section) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700994 if (val==undefined) return;
995 section = section == null ? "_" : "_"+section+"_";
smain@google.com6bdcb982014-11-14 11:53:07 -0800996 var age = 2*365*24*60*60; // set max-age to 2 years
Scott Main3b90aff2013-08-01 18:09:35 -0700997 var cookieValue = cookie_namespace + section + cookie + "=" + val
smain@google.com80e38f42014-11-03 10:47:12 -0800998 + "; max-age=" + age +"; path=/";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700999 document.cookie = cookieValue;
1000}
1001
1002/* ######### END COOKIES! ########## */
1003
1004
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001005var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -07001006var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001007var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -07001008/* Sets the vertical scoll position at which the sticky bar should appear.
1009 This method is called to reset the position when search results appear or hide */
1010function setStickyTop() {
1011 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
1012}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001013
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001014/*
Scott Mainb16376f2014-05-21 20:35:47 -07001015 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -07001016 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001017$(window).scroll(function(event) {
1018
1019 setStickyTop();
1020 var hiding = false;
1021 var $stickyEl = $('#sticky-header');
1022 var $menuEl = $('.menu-container');
1023 // Exit if there's no sidenav
1024 if ($('#side-nav').length == 0) return;
1025 // Exit if the mouse target is a DIV, because that means the event is coming
1026 // from a scrollable div and so there's no need to make adjustments to our layout
1027 if ($(event.target).nodeName == "DIV") {
1028 return;
1029 }
1030
1031 var top = $(window).scrollTop();
1032 // we set the navbar fixed when the scroll position is beyond the height of the site header...
1033 var shouldBeSticky = top >= stickyTop;
1034 // ... except if the document content is shorter than the sidenav height.
1035 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
1036 if ($("#doc-col").height() < $("#side-nav").height()) {
1037 shouldBeSticky = false;
1038 }
Scott Mainf5257812014-05-22 17:26:38 -07001039 // Account for horizontal scroll
1040 var scrollLeft = $(window).scrollLeft();
1041 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
1042 if (sticky && (scrollLeft != prevScrollLeft)) {
1043 updateSideNavPosition();
1044 prevScrollLeft = scrollLeft;
1045 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001046
1047 // Don't continue if the header is sufficently far away
1048 // (to avoid intensive resizing that slows scrolling)
1049 if (sticky == shouldBeSticky) {
1050 return;
1051 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001052
1053 // If sticky header visible and position is now near top, hide sticky
1054 if (sticky && !shouldBeSticky) {
1055 sticky = false;
1056 hiding = true;
1057 // make the sidenav static again
1058 $('#devdoc-nav')
1059 .removeClass('fixed')
1060 .css({'width':'auto','margin':''})
1061 .prependTo('#side-nav');
1062 // delay hide the sticky
1063 $menuEl.removeClass('sticky-menu');
1064 $stickyEl.fadeOut(250);
1065 hiding = false;
1066
1067 // update the sidenaav position for side scrolling
1068 updateSideNavPosition();
1069 } else if (!sticky && shouldBeSticky) {
1070 sticky = true;
1071 $stickyEl.fadeIn(10);
1072 $menuEl.addClass('sticky-menu');
1073
1074 // make the sidenav fixed
1075 var width = $('#devdoc-nav').width();
1076 $('#devdoc-nav')
1077 .addClass('fixed')
1078 .css({'width':width+'px'})
1079 .prependTo('#body-content');
1080
1081 // update the sidenaav position for side scrolling
1082 updateSideNavPosition();
1083
1084 } else if (hiding && top < 15) {
1085 $menuEl.removeClass('sticky-menu');
1086 $stickyEl.hide();
1087 hiding = false;
1088 }
1089 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
1090});
1091
1092/*
1093 * Manages secion card states and nav resize to conclude loading
1094 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001095(function() {
1096 $(document).ready(function() {
1097
Dirk Doughertyc3921652014-05-13 16:55:26 -07001098 // Stack hover states
1099 $('.section-card-menu').each(function(index, el) {
1100 var height = $(el).height();
1101 $(el).css({height:height+'px', position:'relative'});
1102 var $cardInfo = $(el).find('.card-info');
1103
1104 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1105 });
1106
Dirk Doughertyc3921652014-05-13 16:55:26 -07001107 });
1108
1109})();
1110
Scott Maine4d8f1b2012-06-21 18:03:05 -07001111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
Scott Maind7026f72013-06-17 15:08:49 -07001124/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001125
1126
1127
1128
1129
1130function toggle(obj, slide) {
1131 var ul = $("ul:first", obj);
1132 var li = ul.parent();
1133 if (li.hasClass("closed")) {
1134 if (slide) {
1135 ul.slideDown("fast");
1136 } else {
1137 ul.show();
1138 }
1139 li.removeClass("closed");
1140 li.addClass("open");
1141 $(".toggle-img", li).attr("title", "hide pages");
1142 } else {
1143 ul.slideUp("fast");
1144 li.removeClass("open");
1145 li.addClass("closed");
1146 $(".toggle-img", li).attr("title", "show pages");
1147 }
1148}
1149
1150
Scott Maine4d8f1b2012-06-21 18:03:05 -07001151function buildToggleLists() {
1152 $(".toggle-list").each(
1153 function(i) {
1154 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1155 $(this).addClass("closed");
1156 });
1157}
1158
1159
1160
Scott Maind7026f72013-06-17 15:08:49 -07001161function hideNestedItems(list, toggle) {
1162 $list = $(list);
1163 // hide nested lists
1164 if($list.hasClass('showing')) {
1165 $("li ol", $list).hide('fast');
1166 $list.removeClass('showing');
1167 // show nested lists
1168 } else {
1169 $("li ol", $list).show('fast');
1170 $list.addClass('showing');
1171 }
1172 $(".more,.less",$(toggle)).toggle();
1173}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001174
1175
smain@google.com95948b82014-06-16 19:24:25 -07001176/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1177function setupIdeDocToggle() {
1178 $( "select.ide" ).change(function() {
1179 var selected = $(this).find("option:selected").attr("value");
1180 $(".select-ide").hide();
1181 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001182
smain@google.com95948b82014-06-16 19:24:25 -07001183 $("select.ide").val(selected);
1184 });
1185}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210/* REFERENCE NAV SWAP */
1211
1212
1213function getNavPref() {
1214 var v = readCookie('reference_nav');
1215 if (v != NAV_PREF_TREE) {
1216 v = NAV_PREF_PANELS;
1217 }
1218 return v;
1219}
1220
1221function chooseDefaultNav() {
1222 nav_pref = getNavPref();
1223 if (nav_pref == NAV_PREF_TREE) {
1224 $("#nav-panels").toggle();
1225 $("#panel-link").toggle();
1226 $("#nav-tree").toggle();
1227 $("#tree-link").toggle();
1228 }
1229}
1230
1231function swapNav() {
1232 if (nav_pref == NAV_PREF_TREE) {
1233 nav_pref = NAV_PREF_PANELS;
1234 } else {
1235 nav_pref = NAV_PREF_TREE;
1236 init_default_navtree(toRoot);
1237 }
smain@google.com6bdcb982014-11-14 11:53:07 -08001238 writeCookie("nav", nav_pref, "reference");
Scott Maine4d8f1b2012-06-21 18:03:05 -07001239
1240 $("#nav-panels").toggle();
1241 $("#panel-link").toggle();
1242 $("#nav-tree").toggle();
1243 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001244
Scott Maine4d8f1b2012-06-21 18:03:05 -07001245 resizeNav();
1246
1247 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1248 $("#nav-tree .jspContainer:visible")
1249 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1250 // Another nasty hack to make the scrollbar appear now that we have height
1251 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001252
Scott Maine4d8f1b2012-06-21 18:03:05 -07001253 if ($("#nav-tree").is(':visible')) {
1254 scrollIntoView("nav-tree");
1255 } else {
1256 scrollIntoView("packages-nav");
1257 scrollIntoView("classes-nav");
1258 }
1259}
1260
1261
1262
Scott Mainf5089842012-08-14 16:31:07 -07001263/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001264/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001265/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001266
1267function getBaseUri(uri) {
1268 var intlUrl = (uri.substring(0,6) == "/intl/");
1269 if (intlUrl) {
1270 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1271 base = base.substring(base.indexOf('/')+1, base.length);
1272 //alert("intl, returning base url: /" + base);
1273 return ("/" + base);
1274 } else {
1275 //alert("not intl, returning uri as found.");
1276 return uri;
1277 }
1278}
1279
1280function requestAppendHL(uri) {
1281//append "?hl=<lang> to an outgoing request (such as to blog)
1282 var lang = getLangPref();
1283 if (lang) {
1284 var q = 'hl=' + lang;
1285 uri += '?' + q;
1286 window.location = uri;
1287 return false;
1288 } else {
1289 return true;
1290 }
1291}
1292
1293
Scott Maine4d8f1b2012-06-21 18:03:05 -07001294function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001295 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1296 $links.each(function(i){ // for each link with a translation
1297 var $link = $(this);
1298 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1299 // put the desired language from the attribute as the text
1300 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001301 }
Scott Main6eb95f12012-10-02 17:12:23 -07001302 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001303}
1304
Scott Main015d6162013-01-29 09:01:52 -08001305function changeLangPref(lang, submit) {
smain@google.com6bdcb982014-11-14 11:53:07 -08001306 writeCookie("pref_lang", lang, null);
Scott Main015d6162013-01-29 09:01:52 -08001307
1308 // ####### TODO: Remove this condition once we're stable on devsite #######
1309 // This condition is only needed if we still need to support legacy GAE server
1310 if (devsite) {
1311 // Switch language when on Devsite server
1312 if (submit) {
1313 $("#setlang").submit();
1314 }
1315 } else {
1316 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001317 if (submit) {
1318 window.location = getBaseUri(location.pathname);
1319 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001320 }
1321}
1322
1323function loadLangPref() {
1324 var lang = readCookie("pref_lang");
1325 if (lang != 0) {
1326 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1327 }
1328}
1329
1330function getLangPref() {
1331 var lang = $("#language").find(":selected").attr("value");
1332 if (!lang) {
1333 lang = readCookie("pref_lang");
1334 }
1335 return (lang != 0) ? lang : 'en';
1336}
1337
1338/* ########## END LOCALIZATION ############ */
1339
1340
1341
1342
1343
1344
1345/* Used to hide and reveal supplemental content, such as long code samples.
1346 See the companion CSS in android-developer-docs.css */
1347function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001348 var div = $(obj).closest(".toggle-content");
1349 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001350 if (div.hasClass("closed")) { // if it's closed, open it
1351 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001352 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001353 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001354 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001355 + "assets/images/triangle-opened.png");
1356 } else { // if it's open, close it
1357 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001358 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001359 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001360 div.find(".toggle-content").removeClass("open").addClass("closed")
1361 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001362 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001363 + "assets/images/triangle-closed.png");
1364 });
1365 }
1366 return false;
1367}
Scott Mainf5089842012-08-14 16:31:07 -07001368
1369
Scott Maindb3678b2012-10-23 14:13:41 -07001370/* New version of expandable content */
1371function toggleExpandable(link,id) {
1372 if($(id).is(':visible')) {
1373 $(id).slideUp();
1374 $(link).removeClass('expanded');
1375 } else {
1376 $(id).slideDown();
1377 $(link).addClass('expanded');
1378 }
1379}
1380
1381function hideExpandable(ids) {
1382 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001383 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001384}
1385
Scott Mainf5089842012-08-14 16:31:07 -07001386
1387
1388
1389
Scott Main3b90aff2013-08-01 18:09:35 -07001390/*
Scott Mainf5089842012-08-14 16:31:07 -07001391 * Slideshow 1.0
1392 * Used on /index.html and /develop/index.html for carousel
1393 *
1394 * Sample usage:
1395 * HTML -
1396 * <div class="slideshow-container">
1397 * <a href="" class="slideshow-prev">Prev</a>
1398 * <a href="" class="slideshow-next">Next</a>
1399 * <ul>
1400 * <li class="item"><img src="images/marquee1.jpg"></li>
1401 * <li class="item"><img src="images/marquee2.jpg"></li>
1402 * <li class="item"><img src="images/marquee3.jpg"></li>
1403 * <li class="item"><img src="images/marquee4.jpg"></li>
1404 * </ul>
1405 * </div>
1406 *
1407 * <script type="text/javascript">
1408 * $('.slideshow-container').dacSlideshow({
1409 * auto: true,
1410 * btnPrev: '.slideshow-prev',
1411 * btnNext: '.slideshow-next'
1412 * });
1413 * </script>
1414 *
1415 * Options:
1416 * btnPrev: optional identifier for previous button
1417 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001418 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001419 * auto: whether or not to auto-proceed
1420 * speed: animation speed
1421 * autoTime: time between auto-rotation
1422 * easing: easing function for transition
1423 * start: item to select by default
1424 * scroll: direction to scroll in
1425 * pagination: whether or not to include dotted pagination
1426 *
1427 */
1428
1429 (function($) {
1430 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001431
Scott Mainf5089842012-08-14 16:31:07 -07001432 //Options - see above
1433 o = $.extend({
1434 btnPrev: null,
1435 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001436 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001437 auto: true,
1438 speed: 500,
1439 autoTime: 12000,
1440 easing: null,
1441 start: 0,
1442 scroll: 1,
1443 pagination: true
1444
1445 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001446
1447 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001448 return this.each(function() {
1449
1450 var running = false;
1451 var animCss = o.vertical ? "top" : "left";
1452 var sizeCss = o.vertical ? "height" : "width";
1453 var div = $(this);
1454 var ul = $("ul", div);
1455 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001456 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001457 var timer = null;
1458
1459 var li = $("li", ul);
1460 var itemLength = li.size();
1461 var curr = o.start;
1462
1463 li.css({float: o.vertical ? "none" : "left"});
1464 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1465 div.css({position: "relative", "z-index": "2", left: "0px"});
1466
1467 var liSize = o.vertical ? height(li) : width(li);
1468 var ulSize = liSize * itemLength;
1469 var divSize = liSize;
1470
1471 li.css({width: li.width(), height: li.height()});
1472 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1473
1474 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001475
Scott Mainf5089842012-08-14 16:31:07 -07001476 //Pagination
1477 if (o.pagination) {
1478 var pagination = $("<div class='pagination'></div>");
1479 var pag_ul = $("<ul></ul>");
1480 if (tl > 1) {
1481 for (var i=0;i<tl;i++) {
1482 var li = $("<li>"+i+"</li>");
1483 pag_ul.append(li);
1484 if (i==o.start) li.addClass('active');
1485 li.click(function() {
1486 go(parseInt($(this).text()));
1487 })
1488 }
1489 pagination.append(pag_ul);
1490 div.append(pagination);
1491 }
1492 }
Scott Main3b90aff2013-08-01 18:09:35 -07001493
Scott Mainf5089842012-08-14 16:31:07 -07001494 //Previous button
1495 if(o.btnPrev)
1496 $(o.btnPrev).click(function(e) {
1497 e.preventDefault();
1498 return go(curr-o.scroll);
1499 });
1500
1501 //Next button
1502 if(o.btnNext)
1503 $(o.btnNext).click(function(e) {
1504 e.preventDefault();
1505 return go(curr+o.scroll);
1506 });
Scott Maineb410352013-01-14 19:03:40 -08001507
1508 //Pause button
1509 if(o.btnPause)
1510 $(o.btnPause).click(function(e) {
1511 e.preventDefault();
1512 if ($(this).hasClass('paused')) {
1513 startRotateTimer();
1514 } else {
1515 pauseRotateTimer();
1516 }
1517 });
Scott Main3b90aff2013-08-01 18:09:35 -07001518
Scott Mainf5089842012-08-14 16:31:07 -07001519 //Auto rotation
1520 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001521
Scott Mainf5089842012-08-14 16:31:07 -07001522 function startRotateTimer() {
1523 clearInterval(timer);
1524 timer = setInterval(function() {
1525 if (curr == tl-1) {
1526 go(0);
1527 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001528 go(curr+o.scroll);
1529 }
Scott Mainf5089842012-08-14 16:31:07 -07001530 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001531 $(o.btnPause).removeClass('paused');
1532 }
1533
1534 function pauseRotateTimer() {
1535 clearInterval(timer);
1536 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001537 }
1538
1539 //Go to an item
1540 function go(to) {
1541 if(!running) {
1542
1543 if(to<0) {
1544 to = itemLength-1;
1545 } else if (to>itemLength-1) {
1546 to = 0;
1547 }
1548 curr = to;
1549
1550 running = true;
1551
1552 ul.animate(
1553 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1554 function() {
1555 running = false;
1556 }
1557 );
1558
1559 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1560 $( (curr-o.scroll<0 && o.btnPrev)
1561 ||
1562 (curr+o.scroll > itemLength && o.btnNext)
1563 ||
1564 []
1565 ).addClass("disabled");
1566
Scott Main3b90aff2013-08-01 18:09:35 -07001567
Scott Mainf5089842012-08-14 16:31:07 -07001568 var nav_items = $('li', pagination);
1569 nav_items.removeClass('active');
1570 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001571
Scott Mainf5089842012-08-14 16:31:07 -07001572
1573 }
1574 if(o.auto) startRotateTimer();
1575 return false;
1576 };
1577 });
1578 };
1579
1580 function css(el, prop) {
1581 return parseInt($.css(el[0], prop)) || 0;
1582 };
1583 function width(el) {
1584 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1585 };
1586 function height(el) {
1587 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1588 };
1589
1590 })(jQuery);
1591
1592
Scott Main3b90aff2013-08-01 18:09:35 -07001593/*
Scott Mainf5089842012-08-14 16:31:07 -07001594 * dacSlideshow 1.0
1595 * Used on develop/index.html for side-sliding tabs
1596 *
1597 * Sample usage:
1598 * HTML -
1599 * <div class="slideshow-container">
1600 * <a href="" class="slideshow-prev">Prev</a>
1601 * <a href="" class="slideshow-next">Next</a>
1602 * <ul>
1603 * <li class="item"><img src="images/marquee1.jpg"></li>
1604 * <li class="item"><img src="images/marquee2.jpg"></li>
1605 * <li class="item"><img src="images/marquee3.jpg"></li>
1606 * <li class="item"><img src="images/marquee4.jpg"></li>
1607 * </ul>
1608 * </div>
1609 *
1610 * <script type="text/javascript">
1611 * $('.slideshow-container').dacSlideshow({
1612 * auto: true,
1613 * btnPrev: '.slideshow-prev',
1614 * btnNext: '.slideshow-next'
1615 * });
1616 * </script>
1617 *
1618 * Options:
1619 * btnPrev: optional identifier for previous button
1620 * btnNext: optional identifier for next button
1621 * auto: whether or not to auto-proceed
1622 * speed: animation speed
1623 * autoTime: time between auto-rotation
1624 * easing: easing function for transition
1625 * start: item to select by default
1626 * scroll: direction to scroll in
1627 * pagination: whether or not to include dotted pagination
1628 *
1629 */
1630 (function($) {
1631 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001632
Scott Mainf5089842012-08-14 16:31:07 -07001633 //Options - see above
1634 o = $.extend({
1635 speed : 250,
1636 easing: null,
1637 nav_id: null,
1638 frame_id: null
1639 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001640
1641 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001642 return this.each(function() {
1643
1644 var curr = 0;
1645 var running = false;
1646 var animCss = "margin-left";
1647 var sizeCss = "width";
1648 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001649
Scott Mainf5089842012-08-14 16:31:07 -07001650 var nav = $(o.nav_id, div);
1651 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001652 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001653 var frame = div.find(o.frame_id);
1654 var content_width = $(frame).find('ul').width();
1655 //Buttons
1656 $(nav_li).click(function(e) {
1657 go($(nav_li).index($(this)));
1658 })
Scott Main3b90aff2013-08-01 18:09:35 -07001659
Scott Mainf5089842012-08-14 16:31:07 -07001660 //Go to an item
1661 function go(to) {
1662 if(!running) {
1663 curr = to;
1664 running = true;
1665
1666 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1667 function() {
1668 running = false;
1669 }
1670 );
1671
Scott Main3b90aff2013-08-01 18:09:35 -07001672
Scott Mainf5089842012-08-14 16:31:07 -07001673 nav_li.removeClass('active');
1674 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001675
Scott Mainf5089842012-08-14 16:31:07 -07001676
1677 }
1678 return false;
1679 };
1680 });
1681 };
1682
1683 function css(el, prop) {
1684 return parseInt($.css(el[0], prop)) || 0;
1685 };
1686 function width(el) {
1687 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1688 };
1689 function height(el) {
1690 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1691 };
1692
1693 })(jQuery);
1694
1695
1696
1697
1698
1699/* ######################################################## */
1700/* ################ SEARCH SUGGESTIONS ################## */
1701/* ######################################################## */
1702
1703
Scott Main7e447ed2013-02-19 17:22:37 -08001704
Scott Main0e76e7e2013-03-12 10:24:07 -07001705var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1706var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1707
Scott Mainf5089842012-08-14 16:31:07 -07001708var gMatches = new Array();
1709var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001710var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001711var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1712var gListLength = 0;
1713
1714
1715var gGoogleMatches = new Array();
1716var ROW_COUNT_GOOGLE = 15; // max number of results in list
1717var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001718
Scott Main0e76e7e2013-03-12 10:24:07 -07001719var gDocsMatches = new Array();
1720var ROW_COUNT_DOCS = 100; // max number of results in list
1721var gDocsListLength = 0;
1722
Scott Mainde295272013-03-25 15:48:35 -07001723function onSuggestionClick(link) {
1724 // When user clicks a suggested document, track it
smain@google.com633f3222014-10-03 15:49:45 -07001725 ga('send', 'event', 'Suggestion Click', 'clicked: ' + $(link).text(),
1726 'from: ' + $("#search_autocomplete").val());
Scott Mainde295272013-03-25 15:48:35 -07001727}
1728
Scott Mainf5089842012-08-14 16:31:07 -07001729function set_item_selected($li, selected)
1730{
1731 if (selected) {
1732 $li.attr('class','jd-autocomplete jd-selected');
1733 } else {
1734 $li.attr('class','jd-autocomplete');
1735 }
1736}
1737
1738function set_item_values(toroot, $li, match)
1739{
1740 var $link = $('a',$li);
1741 $link.html(match.__hilabel || match.label);
1742 $link.attr('href',toroot + match.link);
1743}
1744
Scott Main719acb42013-12-05 16:05:09 -08001745function set_item_values_jd(toroot, $li, match)
1746{
1747 var $link = $('a',$li);
1748 $link.html(match.title);
1749 $link.attr('href',toroot + match.url);
1750}
1751
Scott Main0e76e7e2013-03-12 10:24:07 -07001752function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001753 var $li = $("<li class='jd-autocomplete'></li>");
1754 $list.append($li);
1755
1756 $li.mousedown(function() {
1757 window.location = this.firstChild.getAttribute("href");
1758 });
1759 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001760 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001761 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001762 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1763 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001764 });
Scott Mainde295272013-03-25 15:48:35 -07001765 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001766 $li.attr('class','show-item');
1767 return $li;
1768}
1769
Scott Mainf5089842012-08-14 16:31:07 -07001770function sync_selection_table(toroot)
1771{
Scott Mainf5089842012-08-14 16:31:07 -07001772 var $li; //list item jquery object
1773 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001774
Scott Main0e76e7e2013-03-12 10:24:07 -07001775 // if there are NO results at all, hide all columns
1776 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1777 $('.suggest-card').hide(300);
1778 return;
1779 }
1780
1781 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001782 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001783 // reveal suggestion list
1784 $('.suggest-card.dummy').show();
1785 $('.suggest-card.reference').show();
1786 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001787
Scott Main0e76e7e2013-03-12 10:24:07 -07001788 // reset the lists
1789 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001790
Scott Main0e76e7e2013-03-12 10:24:07 -07001791 // ########### ANDROID RESULTS #############
1792 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001793
Scott Main0e76e7e2013-03-12 10:24:07 -07001794 // determine android results to show
1795 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1796 gMatches.length : ROW_COUNT_FRAMEWORK;
1797 for (i=0; i<gListLength; i++) {
1798 var $li = new_suggestion($(".suggest-card.reference ul"));
1799 set_item_values(toroot, $li, gMatches[i]);
1800 set_item_selected($li, i == gSelectedIndex);
1801 }
1802 }
Scott Main7e447ed2013-02-19 17:22:37 -08001803
Scott Main0e76e7e2013-03-12 10:24:07 -07001804 // ########### GOOGLE RESULTS #############
1805 if (gGoogleMatches.length > 0) {
1806 // show header for list
1807 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001808
Scott Main0e76e7e2013-03-12 10:24:07 -07001809 // determine google results to show
1810 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1811 for (i=0; i<gGoogleListLength; i++) {
1812 var $li = new_suggestion($(".suggest-card.reference ul"));
1813 set_item_values(toroot, $li, gGoogleMatches[i]);
1814 set_item_selected($li, i == gSelectedIndex);
1815 }
1816 }
Scott Mainf5089842012-08-14 16:31:07 -07001817 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001818 $('.suggest-card.reference').hide();
1819 $('.suggest-card.dummy').hide();
1820 }
1821
1822 // ########### JD DOC RESULTS #############
1823 if (gDocsMatches.length > 0) {
1824 // reset the lists
1825 $(".search_filtered_wrapper.docs li").remove();
1826
1827 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001828 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1829 // The order must match the reverse order that each section appears as a card in
1830 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001831 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1832 for (i=0; i<gDocsListLength; i++) {
1833 var sugg = gDocsMatches[i];
1834 var $li;
1835 if (sugg.type == "design") {
1836 $li = new_suggestion($(".suggest-card.design ul"));
1837 } else
1838 if (sugg.type == "distribute") {
1839 $li = new_suggestion($(".suggest-card.distribute ul"));
1840 } else
Scott Main719acb42013-12-05 16:05:09 -08001841 if (sugg.type == "samples") {
1842 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1843 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001844 if (sugg.type == "training") {
1845 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1846 } else
Scott Main719acb42013-12-05 16:05:09 -08001847 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001848 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1849 } else {
1850 continue;
1851 }
1852
Scott Main719acb42013-12-05 16:05:09 -08001853 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001854 set_item_selected($li, i == gSelectedIndex);
1855 }
1856
1857 // add heading and show or hide card
1858 if ($(".suggest-card.design li").length > 0) {
1859 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1860 $(".suggest-card.design").show(300);
1861 } else {
1862 $('.suggest-card.design').hide(300);
1863 }
1864 if ($(".suggest-card.distribute li").length > 0) {
1865 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1866 $(".suggest-card.distribute").show(300);
1867 } else {
1868 $('.suggest-card.distribute').hide(300);
1869 }
1870 if ($(".child-card.guides li").length > 0) {
1871 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1872 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1873 }
1874 if ($(".child-card.training li").length > 0) {
1875 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1876 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1877 }
Scott Main719acb42013-12-05 16:05:09 -08001878 if ($(".child-card.samples li").length > 0) {
1879 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1880 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1881 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001882
1883 if ($(".suggest-card.develop li").length > 0) {
1884 $(".suggest-card.develop").show(300);
1885 } else {
1886 $('.suggest-card.develop').hide(300);
1887 }
1888
1889 } else {
1890 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001891 }
1892}
1893
Scott Main0e76e7e2013-03-12 10:24:07 -07001894/** Called by the search input's onkeydown and onkeyup events.
1895 * Handles navigation with keyboard arrows, Enter key to invoke search,
1896 * otherwise invokes search suggestions on key-up event.
1897 * @param e The JS event
1898 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001899 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001900 * @returns True if the event should bubble up
1901 */
Scott Mainf5089842012-08-14 16:31:07 -07001902function search_changed(e, kd, toroot)
1903{
Scott Main719acb42013-12-05 16:05:09 -08001904 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001905 var search = document.getElementById("search_autocomplete");
1906 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001907 // get the ul hosting the currently selected item
1908 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1909 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1910 var $selectedUl = $columns[gSelectedColumn];
1911
Scott Mainf5089842012-08-14 16:31:07 -07001912 // show/hide the close button
1913 if (text != '') {
1914 $(".search .close").removeClass("hide");
1915 } else {
1916 $(".search .close").addClass("hide");
1917 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001918 // 27 = esc
1919 if (e.keyCode == 27) {
1920 // close all search results
1921 if (kd) $('.search .close').trigger('click');
1922 return true;
1923 }
Scott Mainf5089842012-08-14 16:31:07 -07001924 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001925 else if (e.keyCode == 13) {
1926 if (gSelectedIndex < 0) {
1927 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001928 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1929 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001930 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001931 return true;
1932 } else {
1933 // otherwise, results are already showing, so allow ajax to auto refresh the results
1934 // and ignore this Enter press to avoid the reload.
1935 return false;
1936 }
1937 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001938 // click the link corresponding to selected item
1939 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001940 return false;
1941 }
1942 }
Scott Mainb16376f2014-05-21 20:35:47 -07001943 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001944 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001945 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001946 if ((sticky ) && (search.value != "")) {
1947 $('body,html').animate({scrollTop:0}, '500', 'swing');
1948 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001949 return true;
1950 }
1951 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001952 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001953 // if the next item is a header, skip it
1954 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001955 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001956 }
1957 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001958 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001959 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001960 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1961 // If user reaches top, reset selected column
1962 if (gSelectedIndex < 0) {
1963 gSelectedColumn = -1;
1964 }
Scott Mainf5089842012-08-14 16:31:07 -07001965 }
1966 return false;
1967 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001968 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001969 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001970 // if the next item is a header, skip it
1971 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001972 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001973 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001974 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1975 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1976 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001977 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001978 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001979 }
1980 return false;
1981 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001982 // Consider left/right arrow navigation
1983 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1984 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1985 // 37 LEFT ARROW
1986 // go left only if current column is not left-most column (last column)
1987 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1988 $('li', $selectedUl).removeClass('jd-selected');
1989 gSelectedColumn++;
1990 $selectedUl = $columns[gSelectedColumn];
1991 // keep or reset the selected item to last item as appropriate
1992 gSelectedIndex = gSelectedIndex >
1993 $("li", $selectedUl).length-1 ?
1994 $("li", $selectedUl).length-1 : gSelectedIndex;
1995 // if the corresponding item is a header, move down
1996 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1997 gSelectedIndex++;
1998 }
Scott Main3b90aff2013-08-01 18:09:35 -07001999 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002000 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2001 return false;
2002 }
2003 // 39 RIGHT ARROW
2004 // go right only if current column is not the right-most column (first column)
2005 else if (e.keyCode == 39 && gSelectedColumn > 0) {
2006 $('li', $selectedUl).removeClass('jd-selected');
2007 gSelectedColumn--;
2008 $selectedUl = $columns[gSelectedColumn];
2009 // keep or reset the selected item to last item as appropriate
2010 gSelectedIndex = gSelectedIndex >
2011 $("li", $selectedUl).length-1 ?
2012 $("li", $selectedUl).length-1 : gSelectedIndex;
2013 // if the corresponding item is a header, move down
2014 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2015 gSelectedIndex++;
2016 }
Scott Main3b90aff2013-08-01 18:09:35 -07002017 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002018 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2019 return false;
2020 }
2021 }
2022
Scott Main719acb42013-12-05 16:05:09 -08002023 // if key-up event and not arrow down/up/left/right,
2024 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07002025 else if (!kd && (e.keyCode != 40)
2026 && (e.keyCode != 38)
2027 && (e.keyCode != 37)
2028 && (e.keyCode != 39)) {
2029 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07002030 gMatches = new Array();
2031 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002032 gGoogleMatches = new Array();
2033 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07002034 gDocsMatches = new Array();
2035 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002036
2037 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07002038 for (var i=0; i<DATA.length; i++) {
2039 var s = DATA[i];
2040 if (text.length != 0 &&
2041 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2042 gMatches[matchedCount] = s;
2043 matchedCount++;
2044 }
2045 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002046 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07002047 for (var i=0; i<gMatches.length; i++) {
2048 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08002049 }
2050
2051
2052 // Search for Google matches
2053 for (var i=0; i<GOOGLE_DATA.length; i++) {
2054 var s = GOOGLE_DATA[i];
2055 if (text.length != 0 &&
2056 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2057 gGoogleMatches[matchedCountGoogle] = s;
2058 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07002059 }
2060 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002061 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08002062 for (var i=0; i<gGoogleMatches.length; i++) {
2063 var s = gGoogleMatches[i];
2064 }
2065
Scott Mainf5089842012-08-14 16:31:07 -07002066 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07002067
2068
2069
Scott Main719acb42013-12-05 16:05:09 -08002070 // Search for matching JD docs
Dirk Dougherty9b7f8f22014-11-01 17:08:56 -07002071 if (text.length >= 2) {
Scott Main719acb42013-12-05 16:05:09 -08002072 // Regex to match only the beginning of a word
2073 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
2074
2075
2076 // Search for Training classes
2077 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002078 // current search comparison, with counters for tag and title,
2079 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002080 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002081 s.matched_tag = 0;
2082 s.matched_title = 0;
2083 var matched = false;
2084
2085 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08002086 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002087 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08002088 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002089 matched = true;
2090 s.matched_tag = j + 1; // add 1 to index position
2091 }
2092 }
Scott Main719acb42013-12-05 16:05:09 -08002093 // Don't consider doc title for lessons (only for class landing pages),
2094 // unless the lesson has a tag that already matches
2095 if ((s.lang == currentLang) &&
2096 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002097 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08002098 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002099 matched = true;
2100 s.matched_title = 1;
2101 }
2102 }
2103 if (matched) {
2104 gDocsMatches[matchedCountDocs] = s;
2105 matchedCountDocs++;
2106 }
2107 }
Scott Main719acb42013-12-05 16:05:09 -08002108
2109
2110 // Search for API Guides
2111 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2112 // current search comparison, with counters for tag and title,
2113 // used later to improve ranking
2114 var s = GUIDE_RESOURCES[i];
2115 s.matched_tag = 0;
2116 s.matched_title = 0;
2117 var matched = false;
2118
2119 // Check if query matches any tags; work backwards toward 1 to assist ranking
2120 for (var j = s.keywords.length - 1; j >= 0; j--) {
2121 // it matches a tag
2122 if (s.keywords[j].toLowerCase().match(textRegex)) {
2123 matched = true;
2124 s.matched_tag = j + 1; // add 1 to index position
2125 }
2126 }
2127 // Check if query matches the doc title, but only for current language
2128 if (s.lang == currentLang) {
2129 // if query matches the doc title
2130 if (s.title.toLowerCase().match(textRegex)) {
2131 matched = true;
2132 s.matched_title = 1;
2133 }
2134 }
2135 if (matched) {
2136 gDocsMatches[matchedCountDocs] = s;
2137 matchedCountDocs++;
2138 }
2139 }
2140
2141
2142 // Search for Tools Guides
2143 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2144 // current search comparison, with counters for tag and title,
2145 // used later to improve ranking
2146 var s = TOOLS_RESOURCES[i];
2147 s.matched_tag = 0;
2148 s.matched_title = 0;
2149 var matched = false;
2150
2151 // Check if query matches any tags; work backwards toward 1 to assist ranking
2152 for (var j = s.keywords.length - 1; j >= 0; j--) {
2153 // it matches a tag
2154 if (s.keywords[j].toLowerCase().match(textRegex)) {
2155 matched = true;
2156 s.matched_tag = j + 1; // add 1 to index position
2157 }
2158 }
2159 // Check if query matches the doc title, but only for current language
2160 if (s.lang == currentLang) {
2161 // if query matches the doc title
2162 if (s.title.toLowerCase().match(textRegex)) {
2163 matched = true;
2164 s.matched_title = 1;
2165 }
2166 }
2167 if (matched) {
2168 gDocsMatches[matchedCountDocs] = s;
2169 matchedCountDocs++;
2170 }
2171 }
2172
2173
2174 // Search for About docs
2175 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2176 // current search comparison, with counters for tag and title,
2177 // used later to improve ranking
2178 var s = ABOUT_RESOURCES[i];
2179 s.matched_tag = 0;
2180 s.matched_title = 0;
2181 var matched = false;
2182
2183 // Check if query matches any tags; work backwards toward 1 to assist ranking
2184 for (var j = s.keywords.length - 1; j >= 0; j--) {
2185 // it matches a tag
2186 if (s.keywords[j].toLowerCase().match(textRegex)) {
2187 matched = true;
2188 s.matched_tag = j + 1; // add 1 to index position
2189 }
2190 }
2191 // Check if query matches the doc title, but only for current language
2192 if (s.lang == currentLang) {
2193 // if query matches the doc title
2194 if (s.title.toLowerCase().match(textRegex)) {
2195 matched = true;
2196 s.matched_title = 1;
2197 }
2198 }
2199 if (matched) {
2200 gDocsMatches[matchedCountDocs] = s;
2201 matchedCountDocs++;
2202 }
2203 }
2204
2205
2206 // Search for Design guides
2207 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2208 // current search comparison, with counters for tag and title,
2209 // used later to improve ranking
2210 var s = DESIGN_RESOURCES[i];
2211 s.matched_tag = 0;
2212 s.matched_title = 0;
2213 var matched = false;
2214
2215 // Check if query matches any tags; work backwards toward 1 to assist ranking
2216 for (var j = s.keywords.length - 1; j >= 0; j--) {
2217 // it matches a tag
2218 if (s.keywords[j].toLowerCase().match(textRegex)) {
2219 matched = true;
2220 s.matched_tag = j + 1; // add 1 to index position
2221 }
2222 }
2223 // Check if query matches the doc title, but only for current language
2224 if (s.lang == currentLang) {
2225 // if query matches the doc title
2226 if (s.title.toLowerCase().match(textRegex)) {
2227 matched = true;
2228 s.matched_title = 1;
2229 }
2230 }
2231 if (matched) {
2232 gDocsMatches[matchedCountDocs] = s;
2233 matchedCountDocs++;
2234 }
2235 }
2236
2237
2238 // Search for Distribute guides
2239 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2240 // current search comparison, with counters for tag and title,
2241 // used later to improve ranking
2242 var s = DISTRIBUTE_RESOURCES[i];
2243 s.matched_tag = 0;
2244 s.matched_title = 0;
2245 var matched = false;
2246
2247 // Check if query matches any tags; work backwards toward 1 to assist ranking
2248 for (var j = s.keywords.length - 1; j >= 0; j--) {
2249 // it matches a tag
2250 if (s.keywords[j].toLowerCase().match(textRegex)) {
2251 matched = true;
2252 s.matched_tag = j + 1; // add 1 to index position
2253 }
2254 }
2255 // Check if query matches the doc title, but only for current language
2256 if (s.lang == currentLang) {
2257 // if query matches the doc title
2258 if (s.title.toLowerCase().match(textRegex)) {
2259 matched = true;
2260 s.matched_title = 1;
2261 }
2262 }
2263 if (matched) {
2264 gDocsMatches[matchedCountDocs] = s;
2265 matchedCountDocs++;
2266 }
2267 }
2268
2269
2270 // Search for Google guides
2271 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2272 // current search comparison, with counters for tag and title,
2273 // used later to improve ranking
2274 var s = GOOGLE_RESOURCES[i];
2275 s.matched_tag = 0;
2276 s.matched_title = 0;
2277 var matched = false;
2278
2279 // Check if query matches any tags; work backwards toward 1 to assist ranking
2280 for (var j = s.keywords.length - 1; j >= 0; j--) {
2281 // it matches a tag
2282 if (s.keywords[j].toLowerCase().match(textRegex)) {
2283 matched = true;
2284 s.matched_tag = j + 1; // add 1 to index position
2285 }
2286 }
2287 // Check if query matches the doc title, but only for current language
2288 if (s.lang == currentLang) {
2289 // if query matches the doc title
2290 if (s.title.toLowerCase().match(textRegex)) {
2291 matched = true;
2292 s.matched_title = 1;
2293 }
2294 }
2295 if (matched) {
2296 gDocsMatches[matchedCountDocs] = s;
2297 matchedCountDocs++;
2298 }
2299 }
2300
2301
2302 // Search for Samples
2303 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2304 // current search comparison, with counters for tag and title,
2305 // used later to improve ranking
2306 var s = SAMPLES_RESOURCES[i];
2307 s.matched_tag = 0;
2308 s.matched_title = 0;
2309 var matched = false;
2310 // Check if query matches any tags; work backwards toward 1 to assist ranking
2311 for (var j = s.keywords.length - 1; j >= 0; j--) {
2312 // it matches a tag
2313 if (s.keywords[j].toLowerCase().match(textRegex)) {
2314 matched = true;
2315 s.matched_tag = j + 1; // add 1 to index position
2316 }
2317 }
2318 // Check if query matches the doc title, but only for current language
2319 if (s.lang == currentLang) {
2320 // if query matches the doc title.t
2321 if (s.title.toLowerCase().match(textRegex)) {
2322 matched = true;
2323 s.matched_title = 1;
2324 }
2325 }
2326 if (matched) {
2327 gDocsMatches[matchedCountDocs] = s;
2328 matchedCountDocs++;
2329 }
2330 }
2331
2332 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002333 rank_autocomplete_doc_results(text, gDocsMatches);
2334 }
2335
2336 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002337 sync_selection_table(toroot);
2338 return true; // allow the event to bubble up to the search api
2339 }
2340}
2341
Scott Main0e76e7e2013-03-12 10:24:07 -07002342/* Order the jd doc result list based on match quality */
2343function rank_autocomplete_doc_results(query, matches) {
2344 query = query || '';
2345 if (!matches || !matches.length)
2346 return;
2347
2348 var _resultScoreFn = function(match) {
2349 var score = 1.0;
2350
2351 // if the query matched a tag
2352 if (match.matched_tag > 0) {
2353 // multiply score by factor relative to position in tags list (max of 3)
2354 score *= 3 / match.matched_tag;
2355
2356 // if it also matched the title
2357 if (match.matched_title > 0) {
2358 score *= 2;
2359 }
2360 } else if (match.matched_title > 0) {
2361 score *= 3;
2362 }
2363
2364 return score;
2365 };
2366
2367 for (var i=0; i<matches.length; i++) {
2368 matches[i].__resultScore = _resultScoreFn(matches[i]);
2369 }
2370
2371 matches.sort(function(a,b){
2372 var n = b.__resultScore - a.__resultScore;
2373 if (n == 0) // lexicographical sort if scores are the same
2374 n = (a.label < b.label) ? -1 : 1;
2375 return n;
2376 });
2377}
2378
Scott Main7e447ed2013-02-19 17:22:37 -08002379/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002380function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002381 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002382 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002383 return;
2384
2385 // helper function that gets the last occurence index of the given regex
2386 // in the given string, or -1 if not found
2387 var _lastSearch = function(s, re) {
2388 if (s == '')
2389 return -1;
2390 var l = -1;
2391 var tmp;
2392 while ((tmp = s.search(re)) >= 0) {
2393 if (l < 0) l = 0;
2394 l += tmp;
2395 s = s.substr(tmp + 1);
2396 }
2397 return l;
2398 };
2399
2400 // helper function that counts the occurrences of a given character in
2401 // a given string
2402 var _countChar = function(s, c) {
2403 var n = 0;
2404 for (var i=0; i<s.length; i++)
2405 if (s.charAt(i) == c) ++n;
2406 return n;
2407 };
2408
2409 var queryLower = query.toLowerCase();
2410 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2411 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2412 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2413
2414 var _resultScoreFn = function(result) {
2415 // scores are calculated based on exact and prefix matches,
2416 // and then number of path separators (dots) from the last
2417 // match (i.e. favoring classes and deep package names)
2418 var score = 1.0;
2419 var labelLower = result.label.toLowerCase();
2420 var t;
2421 t = _lastSearch(labelLower, partExactAlnumRE);
2422 if (t >= 0) {
2423 // exact part match
2424 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2425 score *= 200 / (partsAfter + 1);
2426 } else {
2427 t = _lastSearch(labelLower, partPrefixAlnumRE);
2428 if (t >= 0) {
2429 // part prefix match
2430 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2431 score *= 20 / (partsAfter + 1);
2432 }
2433 }
2434
2435 return score;
2436 };
2437
Scott Main7e447ed2013-02-19 17:22:37 -08002438 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002439 // if the API is deprecated, default score is 0; otherwise, perform scoring
2440 if (matches[i].deprecated == "true") {
2441 matches[i].__resultScore = 0;
2442 } else {
2443 matches[i].__resultScore = _resultScoreFn(matches[i]);
2444 }
Scott Mainf5089842012-08-14 16:31:07 -07002445 }
2446
Scott Main7e447ed2013-02-19 17:22:37 -08002447 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002448 var n = b.__resultScore - a.__resultScore;
2449 if (n == 0) // lexicographical sort if scores are the same
2450 n = (a.label < b.label) ? -1 : 1;
2451 return n;
2452 });
2453}
2454
Scott Main7e447ed2013-02-19 17:22:37 -08002455/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002456function highlight_autocomplete_result_labels(query) {
2457 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002458 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002459 return;
2460
2461 var queryLower = query.toLowerCase();
2462 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2463 var queryRE = new RegExp(
2464 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2465 for (var i=0; i<gMatches.length; i++) {
2466 gMatches[i].__hilabel = gMatches[i].label.replace(
2467 queryRE, '<b>$1</b>');
2468 }
Scott Main7e447ed2013-02-19 17:22:37 -08002469 for (var i=0; i<gGoogleMatches.length; i++) {
2470 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2471 queryRE, '<b>$1</b>');
2472 }
Scott Mainf5089842012-08-14 16:31:07 -07002473}
2474
2475function search_focus_changed(obj, focused)
2476{
Scott Main3b90aff2013-08-01 18:09:35 -07002477 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002478 if(obj.value == ""){
2479 $(".search .close").addClass("hide");
2480 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002481 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002482 }
2483}
2484
2485function submit_search() {
2486 var query = document.getElementById('search_autocomplete').value;
2487 location.hash = 'q=' + query;
2488 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002489 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002490 return false;
2491}
2492
2493
2494function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002495 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002496 $(".search .close").addClass("hide");
2497 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002498
Scott Mainf5089842012-08-14 16:31:07 -07002499 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002500
Scott Mainf5089842012-08-14 16:31:07 -07002501 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2502 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002503
2504 // forcefully regain key-up event control (previously jacked by search api)
2505 $("#search_autocomplete").keyup(function(event) {
2506 return search_changed(event, false, toRoot);
2507 });
2508
Scott Mainf5089842012-08-14 16:31:07 -07002509 return false;
2510}
2511
2512
2513
2514/* ########################################################## */
2515/* ################ CUSTOM SEARCH ENGINE ################## */
2516/* ########################################################## */
2517
Scott Mainf5089842012-08-14 16:31:07 -07002518var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002519google.load('search', '1', {"callback" : function() {
2520 searchControl = new google.search.SearchControl();
2521 } });
Scott Mainf5089842012-08-14 16:31:07 -07002522
2523function loadSearchResults() {
2524 document.getElementById("search_autocomplete").style.color = "#000";
2525
Scott Mainf5089842012-08-14 16:31:07 -07002526 searchControl = new google.search.SearchControl();
2527
2528 // use our existing search form and use tabs when multiple searchers are used
2529 drawOptions = new google.search.DrawOptions();
2530 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2531 drawOptions.setInput(document.getElementById("search_autocomplete"));
2532
2533 // configure search result options
2534 searchOptions = new google.search.SearcherOptions();
2535 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2536
2537 // configure each of the searchers, for each tab
2538 devSiteSearcher = new google.search.WebSearch();
2539 devSiteSearcher.setUserDefinedLabel("All");
2540 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2541
2542 designSearcher = new google.search.WebSearch();
2543 designSearcher.setUserDefinedLabel("Design");
2544 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2545
2546 trainingSearcher = new google.search.WebSearch();
2547 trainingSearcher.setUserDefinedLabel("Training");
2548 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2549
2550 guidesSearcher = new google.search.WebSearch();
2551 guidesSearcher.setUserDefinedLabel("Guides");
2552 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2553
2554 referenceSearcher = new google.search.WebSearch();
2555 referenceSearcher.setUserDefinedLabel("Reference");
2556 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2557
Scott Maindf08ada2012-12-03 08:54:37 -08002558 googleSearcher = new google.search.WebSearch();
2559 googleSearcher.setUserDefinedLabel("Google Services");
2560 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2561
Scott Mainf5089842012-08-14 16:31:07 -07002562 blogSearcher = new google.search.WebSearch();
2563 blogSearcher.setUserDefinedLabel("Blog");
2564 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2565
2566 // add each searcher to the search control
2567 searchControl.addSearcher(devSiteSearcher, searchOptions);
2568 searchControl.addSearcher(designSearcher, searchOptions);
2569 searchControl.addSearcher(trainingSearcher, searchOptions);
2570 searchControl.addSearcher(guidesSearcher, searchOptions);
2571 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002572 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002573 searchControl.addSearcher(blogSearcher, searchOptions);
2574
2575 // configure result options
2576 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2577 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2578 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2579 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2580
2581 // upon ajax search, refresh the url and search title
2582 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2583 updateResultTitle(query);
2584 var query = document.getElementById('search_autocomplete').value;
2585 location.hash = 'q=' + query;
2586 });
2587
Scott Mainde295272013-03-25 15:48:35 -07002588 // once search results load, set up click listeners
2589 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2590 addResultClickListeners();
2591 });
2592
Scott Mainf5089842012-08-14 16:31:07 -07002593 // draw the search results box
2594 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2595
2596 // get query and execute the search
2597 searchControl.execute(decodeURI(getQuery(location.hash)));
2598
2599 document.getElementById("search_autocomplete").focus();
2600 addTabListeners();
2601}
2602// End of loadSearchResults
2603
2604
2605google.setOnLoadCallback(function(){
2606 if (location.hash.indexOf("q=") == -1) {
2607 // if there's no query in the url, don't search and make sure results are hidden
2608 $('#searchResults').hide();
2609 return;
2610 } else {
2611 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002612 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002613 $(".search .close").removeClass("hide");
2614 loadSearchResults();
2615 }
2616}, true);
2617
smain@google.com9a818f52014-10-03 09:25:59 -07002618/* Adjust the scroll position to account for sticky header, only if the hash matches an id.
2619 This does not handle <a name=""> tags. Some CSS fixes those, but only for reference docs. */
Scott Mainb16376f2014-05-21 20:35:47 -07002620function offsetScrollForSticky() {
smain@google.com11fc0092014-10-16 22:10:00 -07002621 // Ignore if there's no search bar (some special pages have no header)
2622 if ($("#search-container").length < 1) return;
2623
smain@google.com3b77ab52014-06-17 11:57:27 -07002624 var hash = escape(location.hash.substr(1));
2625 var $matchingElement = $("#"+hash);
smain@google.com08f336ea2014-10-03 17:40:00 -07002626 // Sanity check that there's an element with that ID on the page
2627 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002628 // If the position of the target element is near the top of the page (<20px, where we expect it
2629 // to be because we need to move it down 60px to become in view), then move it down 60px
2630 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2631 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002632 }
2633 }
2634}
2635
Scott Mainf5089842012-08-14 16:31:07 -07002636// when an event on the browser history occurs (back, forward, load) requery hash and do search
2637$(window).hashchange( function(){
smain@google.com2f077192014-10-09 18:04:10 -07002638 // Ignore if there's no search bar (some special pages have no header)
2639 if ($("#search-container").length < 1) return;
2640
Dirk Doughertyc3921652014-05-13 16:55:26 -07002641 // If the hash isn't a search query or there's an error in the query,
2642 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002643 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2644 // If the results pane is open, close it.
2645 if (!$("#searchResults").is(":hidden")) {
2646 hideResults();
2647 }
Scott Mainb16376f2014-05-21 20:35:47 -07002648 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002649 return;
2650 }
2651
2652 // Otherwise, we have a search to do
2653 var query = decodeURI(getQuery(location.hash));
2654 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002655 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002656 $("#search_autocomplete").focus();
2657 $(".search .close").removeClass("hide");
2658
2659 updateResultTitle(query);
2660});
2661
2662function updateResultTitle(query) {
2663 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2664}
2665
2666// forcefully regain key-up event control (previously jacked by search api)
2667$("#search_autocomplete").keyup(function(event) {
2668 return search_changed(event, false, toRoot);
2669});
2670
2671// add event listeners to each tab so we can track the browser history
2672function addTabListeners() {
2673 var tabHeaders = $(".gsc-tabHeader");
2674 for (var i = 0; i < tabHeaders.length; i++) {
2675 $(tabHeaders[i]).attr("id",i).click(function() {
2676 /*
2677 // make a copy of the page numbers for the search left pane
2678 setTimeout(function() {
2679 // remove any residual page numbers
2680 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002681 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002682 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002683 // and because we're going to remove it (previous line),
2684 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002685 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2686 .clone().appendTo('#searchResults .gsc-tabsArea');
2687 }, 200);
2688 */
2689 });
2690 }
2691 setTimeout(function(){$(tabHeaders[0]).click()},200);
2692}
2693
Scott Mainde295272013-03-25 15:48:35 -07002694// add analytics tracking events to each result link
2695function addResultClickListeners() {
2696 $("#searchResults a.gs-title").each(function(index, link) {
2697 // When user clicks enter for Google search results, track it
2698 $(link).click(function() {
smain@google.com633f3222014-10-03 15:49:45 -07002699 ga('send', 'event', 'Google Click', 'clicked: ' + $(this).text(),
2700 'from: ' + $("#search_autocomplete").val());
Scott Mainde295272013-03-25 15:48:35 -07002701 });
2702 });
2703}
2704
Scott Mainf5089842012-08-14 16:31:07 -07002705
2706function getQuery(hash) {
2707 var queryParts = hash.split('=');
2708 return queryParts[1];
2709}
2710
2711/* returns the given string with all HTML brackets converted to entities
2712 TODO: move this to the site's JS library */
2713function escapeHTML(string) {
2714 return string.replace(/</g,"&lt;")
2715 .replace(/>/g,"&gt;");
2716}
2717
2718
2719
2720
2721
2722
2723
2724/* ######################################################## */
2725/* ################# JAVADOC REFERENCE ################### */
2726/* ######################################################## */
2727
Scott Main65511c02012-09-07 15:51:32 -07002728/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002729if (location.pathname.indexOf("/reference") == 0) {
2730 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2731 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2732 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002733 $(document).ready(function() {
2734 // init available apis based on user pref
2735 changeApiLevel();
2736 initSidenavHeightResize()
2737 });
2738 }
Scott Main65511c02012-09-07 15:51:32 -07002739}
Scott Mainf5089842012-08-14 16:31:07 -07002740
2741var API_LEVEL_COOKIE = "api_level";
2742var minLevel = 1;
2743var maxLevel = 1;
2744
2745/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002746
Scott Mainf5089842012-08-14 16:31:07 -07002747 function initSidenavHeightResize() {
2748 // Change the drag bar size to nicely fit the scrollbar positions
2749 var $dragBar = $(".ui-resizable-s");
2750 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002751
2752 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002753 containment: "#nav-panels",
2754 handles: "s",
2755 alsoResize: "#packages-nav",
2756 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2757 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2758 });
Scott Main3b90aff2013-08-01 18:09:35 -07002759
Scott Mainf5089842012-08-14 16:31:07 -07002760 }
Scott Main3b90aff2013-08-01 18:09:35 -07002761
Scott Mainf5089842012-08-14 16:31:07 -07002762function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002763 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002764 $('#devdoc-nav').css({
2765 'width' : $('#side-nav').css('width'),
2766 'margin' : $('#side-nav').css('margin')
2767 });
2768 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002769
Scott Mainf5089842012-08-14 16:31:07 -07002770 initSidenavHeightResize();
2771}
2772
2773function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002774 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002775 $('#devdoc-nav').css({
2776 'width' : $('#side-nav').css('width'),
2777 'margin' : $('#side-nav').css('margin')
2778 });
2779 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002780
Scott Mainf5089842012-08-14 16:31:07 -07002781 initSidenavHeightResize();
2782}
2783
2784function buildApiLevelSelector() {
2785 maxLevel = SINCE_DATA.length;
2786 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2787 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2788
2789 minLevel = parseInt($("#doc-api-level").attr("class"));
2790 // Handle provisional api levels; the provisional level will always be the highest possible level
2791 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2792 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2793 if (isNaN(minLevel) && minLevel.length) {
2794 minLevel = maxLevel;
2795 }
2796 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2797 for (var i = maxLevel-1; i >= 0; i--) {
2798 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2799 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2800 select.append(option);
2801 }
2802
2803 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2804 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2805 selectedLevelItem.setAttribute('selected',true);
2806}
2807
2808function changeApiLevel() {
2809 maxLevel = SINCE_DATA.length;
2810 var selectedLevel = maxLevel;
2811
2812 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2813 toggleVisisbleApis(selectedLevel, "body");
2814
smain@google.com6bdcb982014-11-14 11:53:07 -08002815 writeCookie(API_LEVEL_COOKIE, selectedLevel, null);
Scott Mainf5089842012-08-14 16:31:07 -07002816
2817 if (selectedLevel < minLevel) {
2818 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002819 $("#naMessage").show().html("<div><p><strong>This " + thing
2820 + " requires API level " + minLevel + " or higher.</strong></p>"
2821 + "<p>This document is hidden because your selected API level for the documentation is "
2822 + selectedLevel + ". You can change the documentation API level with the selector "
2823 + "above the left navigation.</p>"
2824 + "<p>For more information about specifying the API level your app requires, "
2825 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2826 + ">Supporting Different Platform Versions</a>.</p>"
2827 + "<input type='button' value='OK, make this page visible' "
2828 + "title='Change the API level to " + minLevel + "' "
2829 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2830 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002831 } else {
2832 $("#naMessage").hide();
2833 }
2834}
2835
2836function toggleVisisbleApis(selectedLevel, context) {
2837 var apis = $(".api",context);
2838 apis.each(function(i) {
2839 var obj = $(this);
2840 var className = obj.attr("class");
2841 var apiLevelIndex = className.lastIndexOf("-")+1;
2842 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2843 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2844 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2845 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2846 return;
2847 }
2848 apiLevel = parseInt(apiLevel);
2849
2850 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2851 var selectedLevelNum = parseInt(selectedLevel)
2852 var apiLevelNum = parseInt(apiLevel);
2853 if (isNaN(apiLevelNum)) {
2854 apiLevelNum = maxLevel;
2855 }
2856
2857 // Grey things out that aren't available and give a tooltip title
2858 if (apiLevelNum > selectedLevelNum) {
2859 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002860 + apiLevel + "\" or higher. To reveal, change the target API level "
2861 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002862 }
Scott Mainf5089842012-08-14 16:31:07 -07002863 else obj.removeClass("absent").removeAttr("title");
2864 });
2865}
2866
2867
2868
2869
2870/* ################# SIDENAV TREE VIEW ################### */
2871
2872function new_node(me, mom, text, link, children_data, api_level)
2873{
2874 var node = new Object();
2875 node.children = Array();
2876 node.children_data = children_data;
2877 node.depth = mom.depth + 1;
2878
2879 node.li = document.createElement("li");
2880 mom.get_children_ul().appendChild(node.li);
2881
2882 node.label_div = document.createElement("div");
2883 node.label_div.className = "label";
2884 if (api_level != null) {
2885 $(node.label_div).addClass("api");
2886 $(node.label_div).addClass("api-level-"+api_level);
2887 }
2888 node.li.appendChild(node.label_div);
2889
2890 if (children_data != null) {
2891 node.expand_toggle = document.createElement("a");
2892 node.expand_toggle.href = "javascript:void(0)";
2893 node.expand_toggle.onclick = function() {
2894 if (node.expanded) {
2895 $(node.get_children_ul()).slideUp("fast");
2896 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2897 node.expanded = false;
2898 } else {
2899 expand_node(me, node);
2900 }
2901 };
2902 node.label_div.appendChild(node.expand_toggle);
2903
2904 node.plus_img = document.createElement("img");
2905 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2906 node.plus_img.className = "plus";
2907 node.plus_img.width = "8";
2908 node.plus_img.border = "0";
2909 node.expand_toggle.appendChild(node.plus_img);
2910
2911 node.expanded = false;
2912 }
2913
2914 var a = document.createElement("a");
2915 node.label_div.appendChild(a);
2916 node.label = document.createTextNode(text);
2917 a.appendChild(node.label);
2918 if (link) {
2919 a.href = me.toroot + link;
2920 } else {
2921 if (children_data != null) {
2922 a.className = "nolink";
2923 a.href = "javascript:void(0)";
2924 a.onclick = node.expand_toggle.onclick;
2925 // This next line shouldn't be necessary. I'll buy a beer for the first
2926 // person who figures out how to remove this line and have the link
2927 // toggle shut on the first try. --joeo@android.com
2928 node.expanded = false;
2929 }
2930 }
Scott Main3b90aff2013-08-01 18:09:35 -07002931
Scott Mainf5089842012-08-14 16:31:07 -07002932
2933 node.children_ul = null;
2934 node.get_children_ul = function() {
2935 if (!node.children_ul) {
2936 node.children_ul = document.createElement("ul");
2937 node.children_ul.className = "children_ul";
2938 node.children_ul.style.display = "none";
2939 node.li.appendChild(node.children_ul);
2940 }
2941 return node.children_ul;
2942 };
2943
2944 return node;
2945}
2946
Robert Lyd2dd6e52012-11-29 21:28:48 -08002947
2948
2949
Scott Mainf5089842012-08-14 16:31:07 -07002950function expand_node(me, node)
2951{
2952 if (node.children_data && !node.expanded) {
2953 if (node.children_visited) {
2954 $(node.get_children_ul()).slideDown("fast");
2955 } else {
2956 get_node(me, node);
2957 if ($(node.label_div).hasClass("absent")) {
2958 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002959 }
Scott Mainf5089842012-08-14 16:31:07 -07002960 $(node.get_children_ul()).slideDown("fast");
2961 }
2962 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2963 node.expanded = true;
2964
2965 // perform api level toggling because new nodes are new to the DOM
2966 var selectedLevel = $("#apiLevelSelector option:selected").val();
2967 toggleVisisbleApis(selectedLevel, "#side-nav");
2968 }
2969}
2970
2971function get_node(me, mom)
2972{
2973 mom.children_visited = true;
2974 for (var i in mom.children_data) {
2975 var node_data = mom.children_data[i];
2976 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2977 node_data[2], node_data[3]);
2978 }
2979}
2980
2981function this_page_relative(toroot)
2982{
2983 var full = document.location.pathname;
2984 var file = "";
2985 if (toroot.substr(0, 1) == "/") {
2986 if (full.substr(0, toroot.length) == toroot) {
2987 return full.substr(toroot.length);
2988 } else {
2989 // the file isn't under toroot. Fail.
2990 return null;
2991 }
2992 } else {
2993 if (toroot != "./") {
2994 toroot = "./" + toroot;
2995 }
2996 do {
2997 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2998 var pos = full.lastIndexOf("/");
2999 file = full.substr(pos) + file;
3000 full = full.substr(0, pos);
3001 toroot = toroot.substr(0, toroot.length-3);
3002 }
3003 } while (toroot != "" && toroot != "/");
3004 return file.substr(1);
3005 }
3006}
3007
3008function find_page(url, data)
3009{
3010 var nodes = data;
3011 var result = null;
3012 for (var i in nodes) {
3013 var d = nodes[i];
3014 if (d[1] == url) {
3015 return new Array(i);
3016 }
3017 else if (d[2] != null) {
3018 result = find_page(url, d[2]);
3019 if (result != null) {
3020 return (new Array(i).concat(result));
3021 }
3022 }
3023 }
3024 return null;
3025}
3026
Scott Mainf5089842012-08-14 16:31:07 -07003027function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07003028 // load json file for navtree data
3029 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
3030 // when the file is loaded, initialize the tree
3031 if(jqxhr.status === 200) {
3032 init_navtree("tree-list", toroot, NAVTREE_DATA);
3033 }
3034 });
Scott Main3b90aff2013-08-01 18:09:35 -07003035
Scott Mainf5089842012-08-14 16:31:07 -07003036 // perform api level toggling because because the whole tree is new to the DOM
3037 var selectedLevel = $("#apiLevelSelector option:selected").val();
3038 toggleVisisbleApis(selectedLevel, "#side-nav");
3039}
3040
3041function init_navtree(navtree_id, toroot, root_nodes)
3042{
3043 var me = new Object();
3044 me.toroot = toroot;
3045 me.node = new Object();
3046
3047 me.node.li = document.getElementById(navtree_id);
3048 me.node.children_data = root_nodes;
3049 me.node.children = new Array();
3050 me.node.children_ul = document.createElement("ul");
3051 me.node.get_children_ul = function() { return me.node.children_ul; };
3052 //me.node.children_ul.className = "children_ul";
3053 me.node.li.appendChild(me.node.children_ul);
3054 me.node.depth = 0;
3055
3056 get_node(me, me.node);
3057
3058 me.this_page = this_page_relative(toroot);
3059 me.breadcrumbs = find_page(me.this_page, root_nodes);
3060 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
3061 var mom = me.node;
3062 for (var i in me.breadcrumbs) {
3063 var j = me.breadcrumbs[i];
3064 mom = mom.children[j];
3065 expand_node(me, mom);
3066 }
3067 mom.label_div.className = mom.label_div.className + " selected";
3068 addLoadEvent(function() {
3069 scrollIntoView("nav-tree");
3070 });
3071 }
3072}
3073
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003074
3075
3076
3077
3078
3079
3080
Robert Lyd2dd6e52012-11-29 21:28:48 -08003081/* TODO: eliminate redundancy with non-google functions */
3082function init_google_navtree(navtree_id, toroot, root_nodes)
3083{
3084 var me = new Object();
3085 me.toroot = toroot;
3086 me.node = new Object();
3087
3088 me.node.li = document.getElementById(navtree_id);
3089 me.node.children_data = root_nodes;
3090 me.node.children = new Array();
3091 me.node.children_ul = document.createElement("ul");
3092 me.node.get_children_ul = function() { return me.node.children_ul; };
3093 //me.node.children_ul.className = "children_ul";
3094 me.node.li.appendChild(me.node.children_ul);
3095 me.node.depth = 0;
3096
3097 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003098}
3099
3100function new_google_node(me, mom, text, link, children_data, api_level)
3101{
3102 var node = new Object();
3103 var child;
3104 node.children = Array();
3105 node.children_data = children_data;
3106 node.depth = mom.depth + 1;
3107 node.get_children_ul = function() {
3108 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003109 node.children_ul = document.createElement("ul");
3110 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003111 node.li.appendChild(node.children_ul);
3112 }
3113 return node.children_ul;
3114 };
3115 node.li = document.createElement("li");
3116
3117 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003118
3119
Robert Lyd2dd6e52012-11-29 21:28:48 -08003120 if(link) {
3121 child = document.createElement("a");
3122
3123 }
3124 else {
3125 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003126 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003127
3128 }
3129 if (children_data != null) {
3130 node.li.className="nav-section";
3131 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003132 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003133 node.li.appendChild(node.label_div);
3134 get_google_node(me, node);
3135 node.label_div.appendChild(child);
3136 }
3137 else {
3138 node.li.appendChild(child);
3139 }
3140 if(link) {
3141 child.href = me.toroot + link;
3142 }
3143 node.label = document.createTextNode(text);
3144 child.appendChild(node.label);
3145
3146 node.children_ul = null;
3147
3148 return node;
3149}
3150
3151function get_google_node(me, mom)
3152{
3153 mom.children_visited = true;
3154 var linkText;
3155 for (var i in mom.children_data) {
3156 var node_data = mom.children_data[i];
3157 linkText = node_data[0];
3158
3159 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3160 linkText = linkText.substr(19, linkText.length);
3161 }
3162 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3163 node_data[2], node_data[3]);
3164 }
3165}
Scott Mainad08f072013-08-20 16:49:57 -07003166
3167
3168
3169
3170
3171
3172/****** NEW version of script to build google and sample navs dynamically ******/
3173// TODO: update Google reference docs to tolerate this new implementation
3174
Scott Maine624b3f2013-09-12 12:56:41 -07003175var NODE_NAME = 0;
3176var NODE_HREF = 1;
3177var NODE_GROUP = 2;
3178var NODE_TAGS = 3;
3179var NODE_CHILDREN = 4;
3180
Scott Mainad08f072013-08-20 16:49:57 -07003181function init_google_navtree2(navtree_id, data)
3182{
3183 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003184 for (var i in data) {
3185 var node_data = data[i];
3186 $containerUl.append(new_google_node2(node_data));
3187 }
3188
Scott Main70557ee2013-10-30 14:47:40 -07003189 // Make all third-generation list items 'sticky' to prevent them from collapsing
3190 $containerUl.find('li li li.nav-section').addClass('sticky');
3191
Scott Mainad08f072013-08-20 16:49:57 -07003192 initExpandableNavItems("#"+navtree_id);
3193}
3194
3195function new_google_node2(node_data)
3196{
Scott Maine624b3f2013-09-12 12:56:41 -07003197 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003198 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3199 linkText = linkText.substr(19, linkText.length);
3200 }
3201 var $li = $('<li>');
3202 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003203 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003204 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3205 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003206 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003207 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3208 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003209 }
3210 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003211 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003212 $li.addClass("nav-section");
3213 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003214 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003215
Scott Maine624b3f2013-09-12 12:56:41 -07003216 for (var i in node_data[NODE_CHILDREN]) {
3217 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003218 $childUl.append(new_google_node2(child_node_data));
3219 }
3220 $li.append($childUl);
3221 }
3222 $li.prepend($a);
3223
3224 return $li;
3225}
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
Robert Lyd2dd6e52012-11-29 21:28:48 -08003237function showGoogleRefTree() {
3238 init_default_google_navtree(toRoot);
3239 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003240}
3241
3242function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003243 // load json file for navtree data
3244 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3245 // when the file is loaded, initialize the tree
3246 if(jqxhr.status === 200) {
3247 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3248 highlightSidenav();
3249 resizeNav();
3250 }
3251 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003252}
3253
3254function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003255 // load json file for navtree data
3256 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3257 // when the file is loaded, initialize the tree
3258 if(jqxhr.status === 200) {
3259 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3260 highlightSidenav();
3261 resizeNav();
3262 }
3263 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003264}
3265
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003266function showSamplesRefTree() {
3267 init_default_samples_navtree(toRoot);
3268}
3269
3270function init_default_samples_navtree(toroot) {
3271 // load json file for navtree data
3272 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3273 // when the file is loaded, initialize the tree
3274 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003275 // hack to remove the "about the samples" link then put it back in
3276 // after we nuke the list to remove the dummy static list of samples
3277 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3278 $("#nav.samples-nav").empty();
3279 $("#nav.samples-nav").append($firstLi);
3280
Scott Mainad08f072013-08-20 16:49:57 -07003281 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003282 highlightSidenav();
3283 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003284 if ($("#jd-content #samples").length) {
3285 showSamples();
3286 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003287 }
3288 });
3289}
3290
Scott Mainf5089842012-08-14 16:31:07 -07003291/* TOGGLE INHERITED MEMBERS */
3292
3293/* Toggle an inherited class (arrow toggle)
3294 * @param linkObj The link that was clicked.
3295 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3296 * 'null' to simply toggle.
3297 */
3298function toggleInherited(linkObj, expand) {
3299 var base = linkObj.getAttribute("id");
3300 var list = document.getElementById(base + "-list");
3301 var summary = document.getElementById(base + "-summary");
3302 var trigger = document.getElementById(base + "-trigger");
3303 var a = $(linkObj);
3304 if ( (expand == null && a.hasClass("closed")) || expand ) {
3305 list.style.display = "none";
3306 summary.style.display = "block";
3307 trigger.src = toRoot + "assets/images/triangle-opened.png";
3308 a.removeClass("closed");
3309 a.addClass("opened");
3310 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3311 list.style.display = "block";
3312 summary.style.display = "none";
3313 trigger.src = toRoot + "assets/images/triangle-closed.png";
3314 a.removeClass("opened");
3315 a.addClass("closed");
3316 }
3317 return false;
3318}
3319
3320/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3321 * @param linkObj The link that was clicked.
3322 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3323 * 'null' to simply toggle.
3324 */
3325function toggleAllInherited(linkObj, expand) {
3326 var a = $(linkObj);
3327 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3328 var expandos = $(".jd-expando-trigger", table);
3329 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3330 expandos.each(function(i) {
3331 toggleInherited(this, true);
3332 });
3333 a.text("[Collapse]");
3334 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3335 expandos.each(function(i) {
3336 toggleInherited(this, false);
3337 });
3338 a.text("[Expand]");
3339 }
3340 return false;
3341}
3342
3343/* Toggle all inherited members in the class (link in the class title)
3344 */
3345function toggleAllClassInherited() {
3346 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3347 var toggles = $(".toggle-all", $("#body-content"));
3348 if (a.text() == "[Expand All]") {
3349 toggles.each(function(i) {
3350 toggleAllInherited(this, true);
3351 });
3352 a.text("[Collapse All]");
3353 } else {
3354 toggles.each(function(i) {
3355 toggleAllInherited(this, false);
3356 });
3357 a.text("[Expand All]");
3358 }
3359 return false;
3360}
3361
3362/* Expand all inherited members in the class. Used when initiating page search */
3363function ensureAllInheritedExpanded() {
3364 var toggles = $(".toggle-all", $("#body-content"));
3365 toggles.each(function(i) {
3366 toggleAllInherited(this, true);
3367 });
3368 $("#toggleAllClassInherited").text("[Collapse All]");
3369}
3370
3371
3372/* HANDLE KEY EVENTS
3373 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3374 */
3375var agent = navigator['userAgent'].toLowerCase();
3376var mac = agent.indexOf("macintosh") != -1;
3377
3378$(document).keydown( function(e) {
3379var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3380 if (control && e.which == 70) { // 70 is "F"
3381 ensureAllInheritedExpanded();
3382 }
3383});
Scott Main498d7102013-08-21 15:47:38 -07003384
3385
3386
3387
3388
3389
3390/* On-demand functions */
3391
3392/** Move sample code line numbers out of PRE block and into non-copyable column */
3393function initCodeLineNumbers() {
3394 var numbers = $("#codesample-block a.number");
3395 if (numbers.length) {
3396 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3397 }
3398
3399 $(document).ready(function() {
3400 // select entire line when clicked
3401 $("span.code-line").click(function() {
3402 if (!shifted) {
3403 selectText(this);
3404 }
3405 });
3406 // invoke line link on double click
3407 $(".code-line").dblclick(function() {
3408 document.location.hash = $(this).attr('id');
3409 });
3410 // highlight the line when hovering on the number
3411 $("#codesample-line-numbers a.number").mouseover(function() {
3412 var id = $(this).attr('href');
3413 $(id).css('background','#e7e7e7');
3414 });
3415 $("#codesample-line-numbers a.number").mouseout(function() {
3416 var id = $(this).attr('href');
3417 $(id).css('background','none');
3418 });
3419 });
3420}
3421
3422// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3423var shifted = false;
3424$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3425
3426// courtesy of jasonedelman.com
3427function selectText(element) {
3428 var doc = document
3429 , range, selection
3430 ;
3431 if (doc.body.createTextRange) { //ms
3432 range = doc.body.createTextRange();
3433 range.moveToElementText(element);
3434 range.select();
3435 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003436 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003437 range = doc.createRange();
3438 range.selectNodeContents(element);
3439 selection.removeAllRanges();
3440 selection.addRange(range);
3441 }
Scott Main285f0772013-08-22 23:22:09 +00003442}
Scott Main03aca9a2013-10-31 07:20:55 -07003443
3444
3445
3446
3447/** Display links and other information about samples that match the
3448 group specified by the URL */
3449function showSamples() {
3450 var group = $("#samples").attr('class');
3451 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3452
3453 var $ul = $("<ul>");
3454 $selectedLi = $("#nav li.selected");
3455
3456 $selectedLi.children("ul").children("li").each(function() {
3457 var $li = $("<li>").append($(this).find("a").first().clone());
3458 $ul.append($li);
3459 });
3460
3461 $("#samples").append($ul);
3462
3463}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003464
3465
3466
3467/* ########################################################## */
3468/* ################### RESOURCE CARDS ##################### */
3469/* ########################################################## */
3470
3471/** Handle resource queries, collections, and grids (sections). Requires
3472 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3473
3474(function() {
3475 // Prevent the same resource from being loaded more than once per page.
3476 var addedPageResources = {};
3477
3478 $(document).ready(function() {
3479 $('.resource-widget').each(function() {
3480 initResourceWidget(this);
3481 });
3482
3483 /* Pass the line height to ellipsisfade() to adjust the height of the
3484 text container to show the max number of lines possible, without
3485 showing lines that are cut off. This works with the css ellipsis
3486 classes to fade last text line and apply an ellipsis char. */
3487
Scott Mainb16376f2014-05-21 20:35:47 -07003488 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003489 var lineHeight = 15;
3490 $('.card-info .text').ellipsisfade(lineHeight);
3491 });
3492
3493 /*
3494 Three types of resource layouts:
3495 Flow - Uses a fixed row-height flow using float left style.
3496 Carousel - Single card slideshow all same dimension absolute.
3497 Stack - Uses fixed columns and flexible element height.
3498 */
3499 function initResourceWidget(widget) {
3500 var $widget = $(widget);
3501 var isFlow = $widget.hasClass('resource-flow-layout'),
3502 isCarousel = $widget.hasClass('resource-carousel-layout'),
3503 isStack = $widget.hasClass('resource-stack-layout');
3504
3505 // find size of widget by pulling out its class name
3506 var sizeCols = 1;
3507 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3508 if (m) {
3509 sizeCols = parseInt(m[1], 10);
3510 }
3511
3512 var opts = {
3513 cardSizes: ($widget.data('cardsizes') || '').split(','),
3514 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3515 itemsPerPage: $widget.data('itemsperpage'),
3516 sortOrder: $widget.data('sortorder'),
3517 query: $widget.data('query'),
3518 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003519 sizeCols: sizeCols,
3520 /* Added by LFL 6/6/14 */
3521 resourceStyle: $widget.data('resourcestyle') || 'card',
3522 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003523 };
3524
3525 // run the search for the set of resources to show
3526
3527 var resources = buildResourceList(opts);
3528
3529 if (isFlow) {
3530 drawResourcesFlowWidget($widget, opts, resources);
3531 } else if (isCarousel) {
3532 drawResourcesCarouselWidget($widget, opts, resources);
3533 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003534 /* Looks like this got removed and is not used, so repurposing for the
3535 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003536 Modified by LFL 6/6/14
3537 */
3538 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003539 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003540 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003541 }
3542 }
3543
3544 /* Initializes a Resource Carousel Widget */
3545 function drawResourcesCarouselWidget($widget, opts, resources) {
3546 $widget.empty();
3547 var plusone = true; //always show plusone on carousel
3548
3549 $widget.addClass('resource-card slideshow-container')
3550 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3551 .append($('<a>').addClass('slideshow-next').text('Next'));
3552
3553 var css = { 'width': $widget.width() + 'px',
3554 'height': $widget.height() + 'px' };
3555
3556 var $ul = $('<ul>');
3557
3558 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003559 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003560 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003561 .decorateResourceCard(resources[i],plusone);
3562
3563 $('<li>').css(css)
3564 .append($card)
3565 .appendTo($ul);
3566 }
3567
3568 $('<div>').addClass('frame')
3569 .append($ul)
3570 .appendTo($widget);
3571
3572 $widget.dacSlideshow({
3573 auto: true,
3574 btnPrev: '.slideshow-prev',
3575 btnNext: '.slideshow-next'
3576 });
3577 };
3578
Robert Lye7eeb402014-06-03 19:35:24 -07003579 /* Initializes a Resource Card Stack Widget (column-based layout)
3580 Modified by LFL 6/6/14
3581 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003582 function drawResourcesStackWidget($widget, opts, resources, sections) {
3583 // Don't empty widget, grab all items inside since they will be the first
3584 // items stacked, followed by the resource query
3585 var plusone = true; //by default show plusone on section cards
3586 var cards = $widget.find('.resource-card').detach().toArray();
3587 var numStacks = opts.numStacks || 1;
3588 var $stacks = [];
3589 var urlString;
3590
3591 for (var i = 0; i < numStacks; ++i) {
3592 $stacks[i] = $('<div>').addClass('resource-card-stack')
3593 .appendTo($widget);
3594 }
3595
3596 var sectionResources = [];
3597
3598 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003599 if (sections) {
3600 for (var i = 0; i < sections.length; ++i) {
3601 if (!sections[i].sections || !sections[i].sections.length) {
3602 // Render it as a resource card
3603 sectionResources.push(
3604 $('<a>')
3605 .addClass('resource-card section-card')
3606 .attr('href', cleanUrl(sections[i].resource.url))
3607 .decorateResourceCard(sections[i].resource,plusone)[0]
3608 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003609
Robert Lye7eeb402014-06-03 19:35:24 -07003610 } else {
3611 cards.push(
3612 $('<div>')
3613 .addClass('resource-card section-card-menu')
3614 .decorateResourceSection(sections[i],plusone)[0]
3615 );
3616 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003617 }
3618 }
3619
3620 cards = cards.concat(sectionResources);
3621
3622 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003623 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003624
Robert Lye7eeb402014-06-03 19:35:24 -07003625 if (opts.resourceStyle.indexOf('related') > -1) {
3626 $card.addClass('related-card');
3627 }
smain@google.com95948b82014-06-16 19:24:25 -07003628
Dirk Doughertyc3921652014-05-13 16:55:26 -07003629 cards.push($card[0]);
3630 }
3631
Robert Lye7eeb402014-06-03 19:35:24 -07003632 if (opts.stackSort != 'false') {
3633 for (var i = 0; i < cards.length; ++i) {
3634 // Find the stack with the shortest height, but give preference to
3635 // left to right order.
3636 var minHeight = $stacks[0].height();
3637 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003638
Robert Lye7eeb402014-06-03 19:35:24 -07003639 for (var j = 1; j < numStacks; ++j) {
3640 var height = $stacks[j].height();
3641 if (height < minHeight - 45) {
3642 minHeight = height;
3643 minIndex = j;
3644 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003645 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003646
Robert Lye7eeb402014-06-03 19:35:24 -07003647 $stacks[minIndex].append($(cards[i]));
3648 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003649 }
3650
3651 };
smain@google.com95948b82014-06-16 19:24:25 -07003652
3653 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003654 Create a resource card using the given resource object and a list of html
3655 configured options. Returns a jquery object containing the element.
3656 */
smain@google.com95948b82014-06-16 19:24:25 -07003657 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003658 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003659
Robert Lye7eeb402014-06-03 19:35:24 -07003660 // The difference here is that generic cards are not entirely clickable
3661 // so its a div instead of an a tag, also the generic one is not given
3662 // the resource-card class so it appears with a transparent background
3663 // and can be styled in whatever way the css setup.
3664 if (opts.resourceStyle == 'generic') {
3665 $el = $('<div>')
3666 .addClass('resource')
3667 .attr('href', cleanUrl(resource.url))
3668 .decorateResource(resource, opts);
3669 } else {
3670 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003671
Robert Lye7eeb402014-06-03 19:35:24 -07003672 $el = $('<a>')
3673 .addClass(cls)
3674 .attr('href', cleanUrl(resource.url))
3675 .decorateResourceCard(resource, plusone);
3676 }
smain@google.com95948b82014-06-16 19:24:25 -07003677
Robert Lye7eeb402014-06-03 19:35:24 -07003678 return $el;
3679 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003680
3681 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3682 function drawResourcesFlowWidget($widget, opts, resources) {
3683 $widget.empty();
3684 var cardSizes = opts.cardSizes || ['6x6'];
3685 var i = 0, j = 0;
3686 var plusone = true; // by default show plusone on resource cards
3687
3688 while (i < resources.length) {
3689 var cardSize = cardSizes[j++ % cardSizes.length];
3690 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003691 // Some card sizes do not get a plusone button, such as where space is constrained
3692 // or for cards commonly embedded in docs (to improve overall page speed).
3693 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3694 (cardSize == "9x2") || (cardSize == "9x3") ||
3695 (cardSize == "12x2") || (cardSize == "12x3"));
3696
3697 // A stack has a third dimension which is the number of stacked items
3698 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3699 var stackCount = 0;
3700 var $stackDiv = null;
3701
3702 if (isStack) {
3703 // Create a stack container which should have the dimensions defined
3704 // by the product of the items inside.
3705 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3706 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3707 }
3708
3709 // Build each stack item or just a single item
3710 do {
3711 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003712
Robert Lye7eeb402014-06-03 19:35:24 -07003713 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003714
3715 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003716 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003717
Dirk Doughertyc3921652014-05-13 16:55:26 -07003718 if (isStack) {
3719 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3720 if (++stackCount == parseInt(isStack[3])) {
3721 $card.addClass('resource-card-row-stack-last');
3722 stackCount = 0;
3723 }
3724 } else {
3725 stackCount = 0;
3726 }
3727
Robert Lye7eeb402014-06-03 19:35:24 -07003728 $card.appendTo($stackDiv || $widget);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003729
3730 } while (++i < resources.length && stackCount > 0);
3731 }
3732 }
3733
3734 /* Build a site map of resources using a section as a root. */
3735 function buildSectionList(opts) {
3736 if (opts.section && SECTION_BY_ID[opts.section]) {
3737 return SECTION_BY_ID[opts.section].sections || [];
3738 }
3739 return [];
3740 }
3741
3742 function buildResourceList(opts) {
3743 var maxResults = opts.maxResults || 100;
3744
3745 var query = opts.query || '';
3746 var expressions = parseResourceQuery(query);
3747 var addedResourceIndices = {};
3748 var results = [];
3749
3750 for (var i = 0; i < expressions.length; i++) {
3751 var clauses = expressions[i];
3752
3753 // build initial set of resources from first clause
3754 var firstClause = clauses[0];
3755 var resources = [];
3756 switch (firstClause.attr) {
3757 case 'type':
3758 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3759 break;
3760 case 'lang':
3761 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3762 break;
3763 case 'tag':
3764 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3765 break;
3766 case 'collection':
3767 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3768 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3769 break;
3770 case 'section':
3771 var urls = SITE_MAP[firstClause.value].sections || [];
3772 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3773 break;
3774 }
3775 // console.log(firstClause.attr + ':' + firstClause.value);
3776 resources = resources || [];
3777
3778 // use additional clauses to filter corpus
3779 if (clauses.length > 1) {
3780 var otherClauses = clauses.slice(1);
3781 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3782 }
3783
3784 // filter out resources already added
3785 if (i > 1) {
3786 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3787 }
3788
3789 // add to list of already added indices
3790 for (var j = 0; j < resources.length; j++) {
3791 // console.log(resources[j].title);
3792 addedResourceIndices[resources[j].index] = 1;
3793 }
3794
3795 // concat to final results list
3796 results = results.concat(resources);
3797 }
3798
3799 if (opts.sortOrder && results.length) {
3800 var attr = opts.sortOrder;
3801
3802 if (opts.sortOrder == 'random') {
3803 var i = results.length, j, temp;
3804 while (--i) {
3805 j = Math.floor(Math.random() * (i + 1));
3806 temp = results[i];
3807 results[i] = results[j];
3808 results[j] = temp;
3809 }
3810 } else {
3811 var desc = attr.charAt(0) == '-';
3812 if (desc) {
3813 attr = attr.substring(1);
3814 }
3815 results = results.sort(function(x,y) {
3816 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3817 });
3818 }
3819 }
3820
3821 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3822 results = results.slice(0, maxResults);
3823
3824 for (var j = 0; j < results.length; ++j) {
3825 addedPageResources[results[j].index] = 1;
3826 }
3827
3828 return results;
3829 }
3830
3831
3832 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3833 return function(resource) {
3834 return !addedResourceIndices[resource.index];
3835 };
3836 }
3837
3838
3839 function getResourceMatchesClausesFilter(clauses) {
3840 return function(resource) {
3841 return doesResourceMatchClauses(resource, clauses);
3842 };
3843 }
3844
3845
3846 function doesResourceMatchClauses(resource, clauses) {
3847 for (var i = 0; i < clauses.length; i++) {
3848 var map;
3849 switch (clauses[i].attr) {
3850 case 'type':
3851 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3852 break;
3853 case 'lang':
3854 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3855 break;
3856 case 'tag':
3857 map = IS_RESOURCE_TAGGED[clauses[i].value];
3858 break;
3859 }
3860
3861 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3862 return clauses[i].negative;
3863 }
3864 }
3865 return true;
3866 }
smain@google.com95948b82014-06-16 19:24:25 -07003867
Robert Lye7eeb402014-06-03 19:35:24 -07003868 function cleanUrl(url)
3869 {
3870 if (url && url.indexOf('//') === -1) {
3871 url = toRoot + url;
3872 }
smain@google.com95948b82014-06-16 19:24:25 -07003873
Robert Lye7eeb402014-06-03 19:35:24 -07003874 return url;
3875 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003876
3877
3878 function parseResourceQuery(query) {
3879 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3880 var expressions = [];
3881 var expressionStrs = query.split(',') || [];
3882 for (var i = 0; i < expressionStrs.length; i++) {
3883 var expr = expressionStrs[i] || '';
3884
3885 // Break expression into clauses (clause e.g. 'tag:foo')
3886 var clauses = [];
3887 var clauseStrs = expr.split(/(?=[\+\-])/);
3888 for (var j = 0; j < clauseStrs.length; j++) {
3889 var clauseStr = clauseStrs[j] || '';
3890
3891 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3892 var parts = clauseStr.split(':');
3893 var clause = {};
3894
3895 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3896 if (clause.attr) {
3897 if (clause.attr.charAt(0) == '+') {
3898 clause.attr = clause.attr.substring(1);
3899 } else if (clause.attr.charAt(0) == '-') {
3900 clause.negative = true;
3901 clause.attr = clause.attr.substring(1);
3902 }
3903 }
3904
3905 if (parts.length > 1) {
3906 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3907 }
3908
3909 clauses.push(clause);
3910 }
3911
3912 if (!clauses.length) {
3913 continue;
3914 }
3915
3916 expressions.push(clauses);
3917 }
3918
3919 return expressions;
3920 }
3921})();
3922
3923(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003924
smain@google.com95948b82014-06-16 19:24:25 -07003925 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003926 Utility method for creating dom for the description area of a card.
3927 Used in decorateResourceCard and decorateResource.
3928 */
3929 function buildResourceCardDescription(resource, plusone) {
3930 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07003931
Robert Lye7eeb402014-06-03 19:35:24 -07003932 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07003933
Robert Lye7eeb402014-06-03 19:35:24 -07003934 if (resource.cta) {
3935 $description.append($('<a>').addClass('cta').html(resource.cta));
3936 }
smain@google.com95948b82014-06-16 19:24:25 -07003937
Robert Lye7eeb402014-06-03 19:35:24 -07003938 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07003939 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07003940 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07003941
Robert Lye7eeb402014-06-03 19:35:24 -07003942 $description.append($('<div>').addClass('util')
3943 .append($('<div>').addClass('g-plusone')
3944 .attr('data-size', 'small')
3945 .attr('data-align', 'right')
3946 .attr('data-href', plusurl)));
3947 }
smain@google.com95948b82014-06-16 19:24:25 -07003948
Robert Lye7eeb402014-06-03 19:35:24 -07003949 return $description;
3950 }
smain@google.com95948b82014-06-16 19:24:25 -07003951
3952
Dirk Doughertyc3921652014-05-13 16:55:26 -07003953 /* Simple jquery function to create dom for a standard resource card */
3954 $.fn.decorateResourceCard = function(resource,plusone) {
3955 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07003956 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07003957 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07003958
Robert Lye7eeb402014-06-03 19:35:24 -07003959 if (imgUrl.indexOf('//') === -1) {
3960 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003961 }
Robert Lye7eeb402014-06-03 19:35:24 -07003962
3963 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07003964 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07003965 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07003966 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07003967
Robert Lye7eeb402014-06-03 19:35:24 -07003968 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3969 .append($('<div>').addClass('section').text(section))
3970 .append($('<div>').addClass('title').html(resource.title))
3971 .append(buildResourceCardDescription(resource, plusone))
3972 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003973
3974 return this;
3975 };
3976
3977 /* Simple jquery function to create dom for a resource section card (menu) */
3978 $.fn.decorateResourceSection = function(section,plusone) {
3979 var resource = section.resource;
3980 //keep url clean for matching and offline mode handling
3981 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3982 var $base = $('<a>')
3983 .addClass('card-bg')
3984 .attr('href', resource.url)
3985 .append($('<div>').addClass('card-section-icon')
3986 .append($('<div>').addClass('icon'))
3987 .append($('<div>').addClass('section').html(resource.title)))
3988 .appendTo(this);
3989
3990 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3991
3992 if (section.sections && section.sections.length) {
3993 // Recurse the section sub-tree to find a resource image.
3994 var stack = [section];
3995
3996 while (stack.length) {
3997 if (stack[0].resource.image) {
3998 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3999 break;
4000 }
4001
4002 if (stack[0].sections) {
4003 stack = stack.concat(stack[0].sections);
4004 }
4005
4006 stack.shift();
4007 }
4008
4009 var $ul = $('<ul>')
4010 .appendTo($cardInfo);
4011
4012 var max = section.sections.length > 3 ? 3 : section.sections.length;
4013
4014 for (var i = 0; i < max; ++i) {
4015
4016 var subResource = section.sections[i];
4017 if (!plusone) {
4018 $('<li>')
4019 .append($('<a>').attr('href', subResource.url)
4020 .append($('<div>').addClass('title').html(subResource.title))
4021 .append($('<div>').addClass('description ellipsis')
4022 .append($('<div>').addClass('text').html(subResource.summary))
4023 .append($('<div>').addClass('util'))))
4024 .appendTo($ul);
4025 } else {
4026 $('<li>')
4027 .append($('<a>').attr('href', subResource.url)
4028 .append($('<div>').addClass('title').html(subResource.title))
4029 .append($('<div>').addClass('description ellipsis')
4030 .append($('<div>').addClass('text').html(subResource.summary))
4031 .append($('<div>').addClass('util')
4032 .append($('<div>').addClass('g-plusone')
4033 .attr('data-size', 'small')
4034 .attr('data-align', 'right')
4035 .attr('data-href', resource.url)))))
4036 .appendTo($ul);
4037 }
4038 }
4039
4040 // Add a more row
4041 if (max < section.sections.length) {
4042 $('<li>')
4043 .append($('<a>').attr('href', resource.url)
4044 .append($('<div>')
4045 .addClass('title')
4046 .text('More')))
4047 .appendTo($ul);
4048 }
4049 } else {
4050 // No sub-resources, just render description?
4051 }
4052
4053 return this;
4054 };
smain@google.com95948b82014-06-16 19:24:25 -07004055
4056
4057
4058
Robert Lye7eeb402014-06-03 19:35:24 -07004059 /* Render other types of resource styles that are not cards. */
4060 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07004061 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004062 'assets/images/resource-card-default-android.jpg';
4063 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07004064
Robert Lye7eeb402014-06-03 19:35:24 -07004065 if (imgUrl.indexOf('//') === -1) {
4066 imgUrl = toRoot + imgUrl;
4067 }
smain@google.com95948b82014-06-16 19:24:25 -07004068
Robert Lye7eeb402014-06-03 19:35:24 -07004069 if (linkUrl && linkUrl.indexOf('//') === -1) {
4070 linkUrl = toRoot + linkUrl;
4071 }
4072
4073 $(this).append(
4074 $('<div>').addClass('image')
4075 .css('background-image', 'url(' + imgUrl + ')'),
4076 $('<div>').addClass('info').append(
4077 $('<h4>').addClass('title').html(resource.title),
4078 $('<p>').addClass('summary').html(resource.summary),
4079 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4080 )
4081 );
4082
4083 return this;
4084 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004085})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004086
4087
Dirk Doughertyc3921652014-05-13 16:55:26 -07004088/* Calculate the vertical area remaining */
4089(function($) {
4090 $.fn.ellipsisfade= function(lineHeight) {
4091 this.each(function() {
4092 // get element text
4093 var $this = $(this);
4094 var remainingHeight = $this.parent().parent().height();
4095 $this.parent().siblings().each(function ()
smain@google.comc91ecb72014-06-23 10:22:23 -07004096 {
smain@google.comcda1a9a2014-06-19 17:07:46 -07004097 if ($(this).is(":visible")) {
4098 var h = $(this).height();
4099 remainingHeight = remainingHeight - h;
4100 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07004101 });
4102
4103 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4104 $this.parent().css({'height': adjustedRemainingHeight});
4105 $this.css({'height': "auto"});
4106 });
4107
4108 return this;
4109 };
4110}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004111
4112/*
4113 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004114
Robert Lye7eeb402014-06-03 19:35:24 -07004115 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004116 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004117 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004118
Robert Lye7eeb402014-06-03 19:35:24 -07004119 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004120
Robert Lye7eeb402014-06-03 19:35:24 -07004121 <div class="fullscreen-carousel">
4122 <div class="fullscreen-carousel-content">
4123 <!-- content here -->
4124 </div>
4125 <div class="fullscreen-carousel-content">
4126 <!-- content here -->
4127 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004128
Robert Lye7eeb402014-06-03 19:35:24 -07004129 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004130
Robert Lye7eeb402014-06-03 19:35:24 -07004131 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004132
Robert Lye7eeb402014-06-03 19:35:24 -07004133 Control over how the carousel takes over the screen can mostly be defined in
4134 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004135 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004136 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004137 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004138 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004139
Robert Lye7eeb402014-06-03 19:35:24 -07004140 There is limited functionality for having multiple sections since that request
4141 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4142 scroll between multiple content areas.
4143*/
4144
4145(function() {
4146 $(document).ready(function() {
4147 $('.fullscreen-carousel').each(function() {
4148 initWidget(this);
4149 });
4150 });
4151
4152 function initWidget(widget) {
4153 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004154
Robert Lye7eeb402014-06-03 19:35:24 -07004155 var topOffset = $widget.offset().top;
4156 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4157 var maxHeight = 0;
4158 var minHeight = 0;
4159 var $content = $widget.find('.fullscreen-carousel-content');
4160 var $nextArrow = $widget.find('.next-arrow');
4161 var $prevArrow = $widget.find('.prev-arrow');
4162 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004163
Robert Lye7eeb402014-06-03 19:35:24 -07004164 if ($content.length <= 1) {
4165 $nextArrow.hide();
4166 $prevArrow.hide();
4167 } else {
4168 $nextArrow.click(function() {
4169 var index = ($content.index($curSection) + 1);
4170 $curSection.hide();
4171 $curSection = $($content[index >= $content.length ? 0 : index]);
4172 $curSection.show();
4173 });
smain@google.com95948b82014-06-16 19:24:25 -07004174
Robert Lye7eeb402014-06-03 19:35:24 -07004175 $prevArrow.click(function() {
4176 var index = ($content.index($curSection) - 1);
4177 $curSection.hide();
4178 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4179 $curSection.show();
4180 });
4181 }
4182
4183 // Just hide all content sections except first.
4184 $content.each(function(index) {
4185 if ($(this).height() > minHeight) minHeight = $(this).height();
4186 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4187 });
4188
4189 // Register for changes to window size, and trigger.
4190 $(window).resize(resizeWidget);
4191 resizeWidget();
4192
4193 function resizeWidget() {
4194 var height = $(window).height() - topOffset - padBottom;
4195 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004196 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004197 (maxHeight && height > maxHeight ? maxHeight : height));
4198 }
smain@google.com95948b82014-06-16 19:24:25 -07004199 }
Robert Lye7eeb402014-06-03 19:35:24 -07004200})();
4201
4202
4203
4204
4205
4206/*
4207 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004208
Robert Lye7eeb402014-06-03 19:35:24 -07004209 The following allows tab widgets to be installed via the html below. Each
4210 tab content section should have a data-tab attribute matching one of the
4211 nav items'. Also each tab content section should have a width matching the
4212 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004213
Robert Lye7eeb402014-06-03 19:35:24 -07004214 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004215
Robert Lye7eeb402014-06-03 19:35:24 -07004216 <div class="tab-carousel">
4217 <ul class="tab-nav">
4218 <li><a href="#" data-tab="handsets">Handsets</a>
4219 <li><a href="#" data-tab="wearable">Wearable</a>
4220 <li><a href="#" data-tab="tv">TV</a>
4221 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004222
Robert Lye7eeb402014-06-03 19:35:24 -07004223 <div class="tab-carousel-content">
4224 <div data-tab="handsets">
4225 <!--Full width content here-->
4226 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004227
Robert Lye7eeb402014-06-03 19:35:24 -07004228 <div data-tab="wearable">
4229 <!--Full width content here-->
4230 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004231
Robert Lye7eeb402014-06-03 19:35:24 -07004232 <div data-tab="tv">
4233 <!--Full width content here-->
4234 </div>
4235 </div>
4236 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004237
Robert Lye7eeb402014-06-03 19:35:24 -07004238*/
4239(function() {
4240 $(document).ready(function() {
4241 $('.tab-carousel').each(function() {
4242 initWidget(this);
4243 });
4244 });
4245
4246 function initWidget(widget) {
4247 var $widget = $(widget);
4248 var $nav = $widget.find('.tab-nav');
4249 var $anchors = $nav.find('[data-tab]');
4250 var $li = $nav.find('li');
4251 var $contentContainer = $widget.find('.tab-carousel-content');
4252 var $tabs = $contentContainer.find('[data-tab]');
4253 var $curTab = $($tabs[0]); // Current tab is first tab.
4254 var width = $widget.width();
4255
4256 // Setup nav interactivity.
4257 $anchors.click(function(evt) {
4258 evt.preventDefault();
4259 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004260 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004261 });
smain@google.com95948b82014-06-16 19:24:25 -07004262
Robert Lye7eeb402014-06-03 19:35:24 -07004263 // Add highlight for navigation on first item.
4264 var $highlight = $('<div>').addClass('highlight')
4265 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4266 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004267
Robert Lye7eeb402014-06-03 19:35:24 -07004268 // Store height since we will change contents to absolute.
4269 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004270
Robert Lye7eeb402014-06-03 19:35:24 -07004271 // Absolutely position tabs so they're ready for transition.
4272 $tabs.each(function(index) {
4273 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4274 });
smain@google.com95948b82014-06-16 19:24:25 -07004275
Robert Lye7eeb402014-06-03 19:35:24 -07004276 function transitionWidget($toTab) {
4277 if (!$curTab.is($toTab)) {
4278 var curIndex = $tabs.index($curTab[0]);
4279 var toIndex = $tabs.index($toTab[0]);
4280 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004281
Robert Lye7eeb402014-06-03 19:35:24 -07004282 // Animate content sections.
4283 $toTab.css({left:(width * dir) + 'px'});
4284 $curTab.animate({left:(width * -dir) + 'px'});
4285 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004286
Robert Lye7eeb402014-06-03 19:35:24 -07004287 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004288 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004289 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004290
Robert Lye7eeb402014-06-03 19:35:24 -07004291 // Store new current section.
4292 $curTab = $toTab;
4293 }
4294 }
smain@google.com95948b82014-06-16 19:24:25 -07004295 }
Dirk Doughertyb87e3002014-11-18 19:34:34 -08004296})();