blob: c30284ba05f38ee9ef08a4a42a05e370724a1cd1 [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) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -070059 if (event.which == 191 && $(event.target).is(':not(:input)')) {
Scott Main0e76e7e2013-03-12 10:24:07 -070060 $('#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
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -070074 if (window.innerWidth >= 720) {
75 $('.scroll-pane').jScrollPane({verticalGutter: 0});
76 }
Scott Maine4d8f1b2012-06-21 18:03:05 -070077
78 // set up the search close button
Dirk Doughertycbe032f2015-05-22 11:41:40 -070079 $('#search-close').on('click touchend', function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -070080 $searchInput = $('#search_autocomplete');
81 $searchInput.attr('value', '');
82 $(this).addClass("hide");
83 $("#search-container").removeClass('active');
84 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070085 search_focus_changed($searchInput.get(), false);
86 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070087 });
88
Scott Main3b90aff2013-08-01 18:09:35 -070089
Scott Maine4d8f1b2012-06-21 18:03:05 -070090 //Set up search
91 $("#search_autocomplete").focus(function() {
92 $("#search-container").addClass('active');
93 })
Dirk Doughertycbe032f2015-05-22 11:41:40 -070094 $("#search-container").on('mouseover touchend', function(e) {
95 if ($(e.target).is('#search-close')) { return; }
Scott Maine4d8f1b2012-06-21 18:03:05 -070096 $("#search-container").addClass('active');
97 $("#search_autocomplete").focus();
98 })
99 $("#search-container").mouseout(function() {
100 if ($("#search_autocomplete").is(":focus")) return;
101 if ($("#search_autocomplete").val() == '') {
102 setTimeout(function(){
103 $("#search-container").removeClass('active');
104 $("#search_autocomplete").blur();
105 },250);
106 }
107 })
108 $("#search_autocomplete").blur(function() {
109 if ($("#search_autocomplete").val() == '') {
110 $("#search-container").removeClass('active');
111 }
112 })
113
Scott Main3b90aff2013-08-01 18:09:35 -0700114
Scott Maine4d8f1b2012-06-21 18:03:05 -0700115 // prep nav expandos
116 var pagePath = document.location.pathname;
117 // account for intl docs by removing the intl/*/ path
118 if (pagePath.indexOf("/intl/") == 0) {
119 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
120 }
Scott Mainac2aef52013-02-12 14:15:23 -0800121
Scott Maine4d8f1b2012-06-21 18:03:05 -0700122 if (pagePath.indexOf(SITE_ROOT) == 0) {
123 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
124 pagePath += 'index.html';
125 }
126 }
127
Scott Main01a25452013-02-12 17:32:27 -0800128 // Need a copy of the pagePath before it gets changed in the next block;
129 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
130 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700131 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
132 // If running locally, SITE_ROOT will be a relative path, so account for that by
133 // finding the relative URL to this page. This will allow us to find links on the page
134 // leading back to this page.
135 var pathParts = pagePath.split('/');
136 var relativePagePathParts = [];
137 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
138 for (var i = 0; i < upDirs; i++) {
139 relativePagePathParts.push('..');
140 }
141 for (var i = 0; i < upDirs; i++) {
142 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
143 }
144 relativePagePathParts.push(pathParts[pathParts.length - 1]);
145 pagePath = relativePagePathParts.join('/');
146 } else {
147 // Otherwise the page path is already an absolute URL
148 }
149
Scott Mainac2aef52013-02-12 14:15:23 -0800150 // Highlight the header tabs...
151 // highlight Design tab
Dirk Dougherty29e93432015-05-05 18:17:13 -0700152 var urlSegments = pagePathOriginal.split('/');
153 var navEl = $(".dac-nav-list");
154 var subNavEl = navEl.find(".dac-nav-secondary");
155 var parentNavEl;
Scott Mainac2aef52013-02-12 14:15:23 -0800156
Dirk Dougherty29e93432015-05-05 18:17:13 -0700157 if ($("body").hasClass("design")) {
158 navEl.find("> li.design > a").addClass("selected");
smain@google.com6040ffa2014-06-13 15:06:23 -0700159 // highlight About tabs
160 } else if ($("body").hasClass("about")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700161 if (urlSegments[1] == "about" || urlSegments[1] == "wear" || urlSegments[1] == "tv" || urlSegments[1] == "auto") {
162 navEl.find("> li.home > a").addClass('has-subnav');
163 subNavEl.find("li." + urlSegments[1] + " > a").addClass("selected");
164 } else {
165 navEl.find("> li.home > a").addClass('selected');
smain@google.com6040ffa2014-06-13 15:06:23 -0700166 }
Dirk Doughertycf7a3b92015-05-21 00:52:33 -0700167
168// highlight NDK tabs
169 } else if ($("body").hasClass("ndk")) {
170 parentNavEl = navEl.find("> li.ndk > a");
171 parentNavEl.addClass('has-subnav');
172 if ($("body").hasClass("guide")) {
173 navEl.find("> li.guides > a").addClass("selected ndk");
174 } else if ($("body").hasClass("reference")) {
175 navEl.find("> li.reference > a").addClass("selected ndk");
176 } else if ($("body").hasClass("samples")) {
177 navEl.find("> li.samples > a").addClass("selected ndk");
178 } else if ($("body").hasClass("downloads")) {
179 navEl.find("> li.downloads > a").addClass("selected ndk");
180 }
181
Scott Mainac2aef52013-02-12 14:15:23 -0800182 // highlight Develop tab
183 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700184 parentNavEl = navEl.find("> li.develop > a");
185 parentNavEl.addClass('has-subnav');
186
Scott Mainac2aef52013-02-12 14:15:23 -0800187 // In Develop docs, also highlight appropriate sub-tab
Dirk Dougherty29e93432015-05-05 18:17:13 -0700188 if (urlSegments[1] == "training") {
189 subNavEl.find("li.training > a").addClass("selected");
190 } else if (urlSegments[1] == "guide") {
191 subNavEl.find("li.guide > a").addClass("selected");
192 } else if (urlSegments[1] == "reference") {
Scott Mainac2aef52013-02-12 14:15:23 -0800193 // If the root is reference, but page is also part of Google Services, select Google
194 if ($("body").hasClass("google")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700195 subNavEl.find("li.google > a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800196 } else {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700197 subNavEl.find("li.reference > a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800198 }
Dirk Dougherty29e93432015-05-05 18:17:13 -0700199 } else if ((urlSegments[1] == "tools") || (urlSegments[1] == "sdk")) {
200 subNavEl.find("li.tools > a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800201 } else if ($("body").hasClass("google")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700202 subNavEl.find("li.google > a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700203 } else if ($("body").hasClass("samples")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700204 subNavEl.find("li.samples > a").addClass("selected");
205 } else {
206 parentNavEl.removeClass('has-subnav').addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800207 }
Scott Mainac2aef52013-02-12 14:15:23 -0800208 // highlight Distribute tab
209 } else if ($("body").hasClass("distribute")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700210 parentNavEl = navEl.find("> li.distribute > a");
211 parentNavEl.addClass('has-subnav');
Dirk Doughertyc3921652014-05-13 16:55:26 -0700212
Dirk Dougherty29e93432015-05-05 18:17:13 -0700213 if (urlSegments[2] == "users") {
214 subNavEl.find("li.users > a").addClass("selected");
215 } else if (urlSegments[2] == "engage") {
216 subNavEl.find("li.engage > a").addClass("selected");
217 } else if (urlSegments[2] == "monetize") {
218 subNavEl.find("li.monetize > a").addClass("selected");
219 } else if (urlSegments[2] == "analyze") {
220 subNavEl.find("li.analyze > a").addClass("selected");
221 } else if (urlSegments[2] == "tools") {
Dirk Dougherty825c1aa2015-05-26 14:45:09 -0700222 subNavEl.find("li.essentials > a").addClass("selected");
Dirk Dougherty29e93432015-05-05 18:17:13 -0700223 } else if (urlSegments[2] == "stories") {
224 subNavEl.find("li.stories > a").addClass("selected");
225 } else if (urlSegments[2] == "essentials") {
226 subNavEl.find("li.essentials > a").addClass("selected");
227 } else if (urlSegments[2] == "googleplay") {
228 subNavEl.find("li.googleplay > a").addClass("selected");
229 } else {
230 parentNavEl.removeClass('has-subnav').addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700231 }
Scott Mainb16376f2014-05-21 20:35:47 -0700232 }
Scott Mainac2aef52013-02-12 14:15:23 -0800233
Scott Mainf6145542013-04-01 16:38:11 -0700234 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
235 // and highlight the sidenav
236 mPagePath = pagePath;
237 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700238 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800239
Scott Mainf6145542013-04-01 16:38:11 -0700240 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700241 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700242 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700243 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800244 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700245
246 // set up prev links
247 var $prevLink = [];
248 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700249
Scott Maine4d8f1b2012-06-21 18:03:05 -0700250 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
251false; // navigate across topic boundaries only in design docs
252 if ($prevListItem.length) {
smain@google.comc91ecb72014-06-23 10:22:23 -0700253 if ($prevListItem.hasClass('nav-section') || crossBoundaries) {
Scott Main5a1123e2012-09-26 12:51:28 -0700254 // jump to last topic of previous section
255 $prevLink = $prevListItem.find('a:last');
256 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700257 // jump to previous topic in this section
258 $prevLink = $prevListItem.find('a:eq(0)');
259 }
260 } else {
261 // jump to this section's index page (if it exists)
262 var $parentListItem = $selListItem.parents('li');
263 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700264
Scott Maine4d8f1b2012-06-21 18:03:05 -0700265 // except if cross boundaries aren't allowed, and we're at the top of a section already
266 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700267 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700268 && $selListItem.hasClass('nav-section')) {
269 $prevLink = [];
270 }
271 }
272
Scott Maine4d8f1b2012-06-21 18:03:05 -0700273 // set up next links
274 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700275 var startClass = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700276 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700277
Scott Main1a00f7f2013-10-29 11:11:19 -0700278 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700279 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700280 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700281
282 // if there aren't any children, go to the next section (required for About pages)
283 if($nextLink.length == 0) {
284 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700285 } else if ($('.topic-start-link').length) {
286 // as long as there's a child link and there is a "topic start link" (we're on a landing)
287 // then set the landing page "start link" text to be the first doc title
288 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700289 }
Scott Main3b90aff2013-08-01 18:09:35 -0700290
Scott Main5a1123e2012-09-26 12:51:28 -0700291 // If the selected page has a description, then it's a class or article homepage
292 if ($selListItem.find('a[description]').length) {
293 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700294 startClass = true;
295 }
296 } else {
297 // jump to the next topic in this section (if it exists)
298 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700299 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700300 isCrossingBoundary = true;
301 // no more topics in this section, jump to the first topic in the next section
smain@google.comabf34112014-06-23 11:39:02 -0700302 $nextLink = $selListItem.parents('li:eq(0)').next('li').find('a:eq(0)');
Scott Main5a1123e2012-09-26 12:51:28 -0700303 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
304 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700305 if ($nextLink.length == 0) {
306 // if that doesn't work, we're at the end of the list, so disable NEXT link
307 $('.next-page-link').attr('href','').addClass("disabled")
308 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700309 // and completely hide the one in the footer
310 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700311 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700312 }
313 }
314 }
Scott Main5a1123e2012-09-26 12:51:28 -0700315
316 if (startClass) {
317 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
318
Scott Main3b90aff2013-08-01 18:09:35 -0700319 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700320 // then we need to add a bottom border to button
321 if (!$("#tb").length) {
322 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700323 }
Scott Main5a1123e2012-09-26 12:51:28 -0700324 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
325 $('.content-footer.next-class').show();
326 $('.next-page-link').attr('href','')
327 .removeClass("hide").addClass("disabled")
328 .click(function() { return false; });
smain@google.comc91ecb72014-06-23 10:22:23 -0700329 // and completely hide the one in the footer
330 $('.content-footer .next-page-link').hide();
Scott Main1a00f7f2013-10-29 11:11:19 -0700331 if ($nextLink.length) {
332 $('.next-class-link').attr('href',$nextLink.attr('href'))
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700333 .removeClass("hide")
334 .append(": " + $nextLink.html());
Scott Main1a00f7f2013-10-29 11:11:19 -0700335 $('.next-class-link').find('.new').empty();
336 }
Scott Main5a1123e2012-09-26 12:51:28 -0700337 } else {
smain@google.com5bc3a1a2014-06-17 20:02:53 -0700338 $('.next-page-link').attr('href', $nextLink.attr('href'))
339 .removeClass("hide");
340 // for the footer link, also add the next page title
341 $('.content-footer .next-page-link').append(": " + $nextLink.html());
Scott Main5a1123e2012-09-26 12:51:28 -0700342 }
343
344 if (!startClass && $prevLink.length) {
345 var prevHref = $prevLink.attr('href');
346 if (prevHref == SITE_ROOT + 'index.html') {
347 // Don't show Previous when it leads to the homepage
348 } else {
349 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
350 }
Scott Main3b90aff2013-08-01 18:09:35 -0700351 }
Scott Main5a1123e2012-09-26 12:51:28 -0700352
Scott Maine4d8f1b2012-06-21 18:03:05 -0700353 }
Scott Main3b90aff2013-08-01 18:09:35 -0700354
355
356
Scott Main5a1123e2012-09-26 12:51:28 -0700357 // Set up the course landing pages for Training with class names and descriptions
358 if ($('body.trainingcourse').length) {
359 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700360
361 // create an array for all the class descriptions
362 var $classDescriptions = new Array($classLinks.length);
363 var lang = getLangPref();
364 $classLinks.each(function(index) {
365 var langDescr = $(this).attr(lang + "-description");
366 if (typeof langDescr !== 'undefined' && langDescr !== false) {
367 // if there's a class description in the selected language, use that
368 $classDescriptions[index] = langDescr;
369 } else {
370 // otherwise, use the default english description
371 $classDescriptions[index] = $(this).attr("description");
372 }
373 });
Scott Main3b90aff2013-08-01 18:09:35 -0700374
Scott Main5a1123e2012-09-26 12:51:28 -0700375 var $olClasses = $('<ol class="class-list"></ol>');
376 var $liClass;
Scott Main5a1123e2012-09-26 12:51:28 -0700377 var $h2Title;
378 var $pSummary;
379 var $olLessons;
380 var $liLesson;
381 $classLinks.each(function(index) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700382 $liClass = $('<li class="clearfix"></li>');
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700383 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2 class="norule">' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700384 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700385
Scott Main5a1123e2012-09-26 12:51:28 -0700386 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700387
Scott Main5a1123e2012-09-26 12:51:28 -0700388 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700389
Scott Main5a1123e2012-09-26 12:51:28 -0700390 if ($lessons.length) {
Scott Main5a1123e2012-09-26 12:51:28 -0700391 $lessons.each(function(index) {
392 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
393 });
394 } else {
Scott Main5a1123e2012-09-26 12:51:28 -0700395 $pSummary.addClass('article');
396 }
397
Dirk Dougherty29e93432015-05-05 18:17:13 -0700398 $liClass.append($h2Title).append($pSummary).append($olLessons);
Scott Main5a1123e2012-09-26 12:51:28 -0700399 $olClasses.append($liClass);
400 });
401 $('.jd-descr').append($olClasses);
402 }
403
Scott Maine4d8f1b2012-06-21 18:03:05 -0700404 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700405 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700406
Scott Main3b90aff2013-08-01 18:09:35 -0700407
Scott Maine4d8f1b2012-06-21 18:03:05 -0700408 $(".scroll-pane").scroll(function(event) {
409 event.preventDefault();
410 return false;
411 });
412
413 /* Resize nav height when window height changes */
414 $(window).resize(function() {
415 if ($('#side-nav').length == 0) return;
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700416 setNavBarDimensions(); // do this even if sidenav isn't fixed because it could become fixed
Scott Maine4d8f1b2012-06-21 18:03:05 -0700417 // make sidenav behave when resizing the window and side-scolling is a concern
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700418 updateSideNavDimensions();
419 checkSticky();
420 resizeNav(250);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700421 });
422
Scott Maine4d8f1b2012-06-21 18:03:05 -0700423 if ($('#devdoc-nav').length) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700424 setNavBarDimensions();
Scott Maine4d8f1b2012-06-21 18:03:05 -0700425 }
426
427
Scott Maine4d8f1b2012-06-21 18:03:05 -0700428 // Set up play-on-hover <video> tags.
429 $('video.play-on-hover').bind('click', function(){
430 $(this).get(0).load(); // in case the video isn't seekable
431 $(this).get(0).play();
432 });
433
434 // Set up tooltips
435 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700436 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700437 var $target = $(this);
438 var $tooltip = $('<div>')
439 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700440 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700441 .hide()
442 .appendTo('body');
443 $target.removeAttr('title');
444
445 $target.hover(function() {
446 // in
447 var targetRect = $target.offset();
448 targetRect.width = $target.width();
449 targetRect.height = $target.height();
450
451 $tooltip.css({
452 left: targetRect.left,
453 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
454 });
455 $tooltip.addClass('below');
456 $tooltip.show();
457 }, function() {
458 // out
459 $tooltip.hide();
460 });
461 });
462
463 // Set up <h2> deeplinks
464 $('h2').click(function() {
465 var id = $(this).attr('id');
466 if (id) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700467 if (history && history.replaceState) {
468 // Change url without scrolling.
469 history.replaceState({}, '', '#' + id);
470 } else {
471 document.location.hash = id;
472 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700473 }
474 });
475
476 //Loads the +1 button
477 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
478 po.src = 'https://apis.google.com/js/plusone.js';
479 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
480
Scott Maine4d8f1b2012-06-21 18:03:05 -0700481 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700482
Scott Maine4d8f1b2012-06-21 18:03:05 -0700483 if ($(".scroll-pane").length > 1) {
484 // Check if there's a user preference for the panel heights
485 var cookieHeight = readCookie("reference_height");
486 if (cookieHeight) {
487 restoreHeight(cookieHeight);
488 }
489 }
Scott Main3b90aff2013-08-01 18:09:35 -0700490
Scott Main06f3f2c2014-05-30 11:23:00 -0700491 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700492 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700493 // Check if there's an anchor that we need to scroll into view.
494 // A delay is needed, because some browsers do not immediately scroll down to the anchor
495 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700496
Scott Main015d6162013-01-29 09:01:52 -0800497 /* init the language selector based on user cookie for lang */
498 loadLangPref();
499 changeNavLang(getLangPref());
500
501 /* setup event handlers to ensure the overflow menu is visible while picking lang */
502 $("#language select")
503 .mousedown(function() {
504 $("div.morehover").addClass("hover"); })
505 .blur(function() {
506 $("div.morehover").removeClass("hover"); });
507
508 /* some global variable setup */
509 resizePackagesNav = $("#resize-packages-nav");
510 classesNav = $("#classes-nav");
511 devdocNav = $("#devdoc-nav");
512
513 var cookiePath = "";
514 if (location.href.indexOf("/reference/") != -1) {
515 cookiePath = "reference_";
516 } else if (location.href.indexOf("/guide/") != -1) {
517 cookiePath = "guide_";
518 } else if (location.href.indexOf("/tools/") != -1) {
519 cookiePath = "tools_";
520 } else if (location.href.indexOf("/training/") != -1) {
521 cookiePath = "training_";
522 } else if (location.href.indexOf("/design/") != -1) {
523 cookiePath = "design_";
524 } else if (location.href.indexOf("/distribute/") != -1) {
525 cookiePath = "distribute_";
526 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700527
smain@google.com698fff02014-11-20 20:39:33 -0800528
529 /* setup shadowbox for any videos that want it */
smain@google.comf75ee212014-11-24 09:42:59 -0800530 var $videoLinks = $("a.video-shadowbox-button, a.notice-developers-video");
smain@google.com698fff02014-11-20 20:39:33 -0800531 if ($videoLinks.length) {
532 // if there's at least one, add the shadowbox HTML to the body
533 $('body').prepend(
534'<div id="video-container">'+
535 '<div id="video-frame">'+
536 '<div class="video-close">'+
537 '<span id="icon-video-close" onclick="closeVideo()">&nbsp;</span>'+
538 '</div>'+
539 '<div id="youTubePlayer"></div>'+
540 '</div>'+
541'</div>');
542
543 // loads the IFrame Player API code asynchronously.
544 $.getScript("https://www.youtube.com/iframe_api");
545
546 $videoLinks.each(function() {
547 var videoId = $(this).attr('href').split('?v=')[1];
548 $(this).click(function(event) {
549 event.preventDefault();
550 startYouTubePlayer(videoId);
551 });
552 });
smain@google.com698fff02014-11-20 20:39:33 -0800553 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700554});
Scott Main7e447ed2013-02-19 17:22:37 -0800555// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700556
557
smain@google.com698fff02014-11-20 20:39:33 -0800558var youTubePlayer;
559function onYouTubeIframeAPIReady() {
560}
561
smain@google.com3de83c12014-12-12 19:06:52 -0800562/* Returns the height the shadowbox video should be. It's based on the current
563 height of the "video-frame" element, which is 100% height for the window.
564 Then minus the margin so the video isn't actually the full window height. */
565function getVideoHeight() {
566 var frameHeight = $("#video-frame").height();
567 var marginTop = $("#video-frame").css('margin-top').split('px')[0];
568 return frameHeight - (marginTop * 2);
569}
570
smain@google.comd162be52015-02-05 13:27:16 -0800571var mPlayerPaused = false;
572
smain@google.com698fff02014-11-20 20:39:33 -0800573function startYouTubePlayer(videoId) {
smain@google.com3de83c12014-12-12 19:06:52 -0800574 $("#video-container").show();
575 $("#video-frame").show();
smain@google.comd162be52015-02-05 13:27:16 -0800576 mPlayerPaused = false;
smain@google.com3de83c12014-12-12 19:06:52 -0800577
578 // compute the size of the player so it's centered in window
579 var maxWidth = 940; // the width of the web site content
580 var videoAspect = .5625; // based on 1280x720 resolution
581 var maxHeight = maxWidth * videoAspect;
582 var videoHeight = getVideoHeight();
583 var videoWidth = videoHeight / videoAspect;
584 if (videoWidth > maxWidth) {
585 videoWidth = maxWidth;
586 videoHeight = maxHeight;
smain@google.com570c2212014-11-25 19:01:40 -0800587 }
smain@google.com3de83c12014-12-12 19:06:52 -0800588 $("#video-frame").css('width', videoWidth);
589
590 // check if we've already created this player
smain@google.com698fff02014-11-20 20:39:33 -0800591 if (youTubePlayer == null) {
smain@google.com3de83c12014-12-12 19:06:52 -0800592 // check if there's a start time specified
593 var idAndHash = videoId.split("#");
594 var startTime = 0;
595 if (idAndHash.length > 1) {
596 startTime = idAndHash[1].split("t=")[1] != undefined ? idAndHash[1].split("t=")[1] : 0;
597 }
598 // enable localized player
599 var lang = getLangPref();
600 var captionsOn = lang == 'en' ? 0 : 1;
601
smain@google.com698fff02014-11-20 20:39:33 -0800602 youTubePlayer = new YT.Player('youTubePlayer', {
smain@google.com3de83c12014-12-12 19:06:52 -0800603 height: videoHeight,
604 width: videoWidth,
smain@google.com570c2212014-11-25 19:01:40 -0800605 videoId: idAndHash[0],
smain@google.com481f15c2014-12-12 11:57:27 -0800606 playerVars: {start: startTime, hl: lang, cc_load_policy: captionsOn},
smain@google.com698fff02014-11-20 20:39:33 -0800607 events: {
smain@google.comf75ee212014-11-24 09:42:59 -0800608 'onReady': onPlayerReady,
609 'onStateChange': onPlayerStateChange
smain@google.com698fff02014-11-20 20:39:33 -0800610 }
611 });
612 } else {
smain@google.com3de83c12014-12-12 19:06:52 -0800613 // reset the size in case the user adjusted the window since last play
614 youTubePlayer.setSize(videoWidth, videoHeight);
smain@google.com94e0b532014-12-16 19:07:08 -0800615 // if a video different from the one already playing was requested, cue it up
smain@google.comd162be52015-02-05 13:27:16 -0800616 if (videoId != youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0]) {
smain@google.com94e0b532014-12-16 19:07:08 -0800617 youTubePlayer.cueVideoById(videoId);
618 }
smain@google.com698fff02014-11-20 20:39:33 -0800619 youTubePlayer.playVideo();
620 }
smain@google.com698fff02014-11-20 20:39:33 -0800621}
622
623function onPlayerReady(event) {
624 event.target.playVideo();
smain@google.comd162be52015-02-05 13:27:16 -0800625 mPlayerPaused = false;
smain@google.com698fff02014-11-20 20:39:33 -0800626}
627
628function closeVideo() {
629 try {
smain@google.comf75ee212014-11-24 09:42:59 -0800630 youTubePlayer.pauseVideo();
smain@google.com698fff02014-11-20 20:39:33 -0800631 } catch(e) {
smain@google.com698fff02014-11-20 20:39:33 -0800632 }
smain@google.com3de83c12014-12-12 19:06:52 -0800633 $("#video-container").fadeOut(200);
smain@google.com698fff02014-11-20 20:39:33 -0800634}
635
smain@google.comf75ee212014-11-24 09:42:59 -0800636/* Track youtube playback for analytics */
637function onPlayerStateChange(event) {
638 // Video starts, send the video ID
639 if (event.data == YT.PlayerState.PLAYING) {
smain@google.comd162be52015-02-05 13:27:16 -0800640 if (mPlayerPaused) {
641 ga('send', 'event', 'Videos', 'Resume',
642 youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0]);
643 } else {
644 // track the start playing event so we know from which page the video was selected
645 ga('send', 'event', 'Videos', 'Start: ' +
646 youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
647 'on: ' + document.location.href);
648 }
649 mPlayerPaused = false;
smain@google.comf75ee212014-11-24 09:42:59 -0800650 }
651 // Video paused, send video ID and video elapsed time
652 if (event.data == YT.PlayerState.PAUSED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800653 ga('send', 'event', 'Videos', 'Paused',
smain@google.comd162be52015-02-05 13:27:16 -0800654 youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
655 youTubePlayer.getCurrentTime());
656 mPlayerPaused = true;
smain@google.comf75ee212014-11-24 09:42:59 -0800657 }
658 // Video finished, send video ID and video elapsed time
659 if (event.data == YT.PlayerState.ENDED) {
smain@google.comd24088c2014-12-12 11:31:13 -0800660 ga('send', 'event', 'Videos', 'Finished',
smain@google.comd162be52015-02-05 13:27:16 -0800661 youTubePlayer.getVideoUrl().split('?v=')[1].split('&')[0].split('%')[0],
662 youTubePlayer.getCurrentTime());
663 mPlayerPaused = true;
smain@google.comf75ee212014-11-24 09:42:59 -0800664 }
665}
666
smain@google.com698fff02014-11-20 20:39:33 -0800667
668
Scott Mainad08f072013-08-20 16:49:57 -0700669function initExpandableNavItems(rootTag) {
670 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
671 var section = $(this).closest('li.nav-section');
672 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700673 /* hide me and descendants */
674 section.find('ul').slideUp(250, function() {
675 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700676 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700677 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700678 resizeNav();
679 });
680 } else {
681 /* show me */
682 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700683 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700684 $others.removeClass('expanded').children('ul').slideUp(250);
685
686 // now expand me
687 section.closest('li').addClass('expanded');
688 section.children('ul').slideDown(250, function() {
689 resizeNav();
690 });
691 }
692 });
Scott Mainf0093852013-08-22 11:37:11 -0700693
694 // Stop expand/collapse behavior when clicking on nav section links
695 // (since we're navigating away from the page)
696 // This selector captures the first instance of <a>, but not those with "#" as the href.
697 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
698 window.location.href = $(this).attr('href');
699 return false;
700 });
Scott Mainad08f072013-08-20 16:49:57 -0700701}
702
Dirk Doughertyc3921652014-05-13 16:55:26 -0700703
704/** Create the list of breadcrumb links in the sticky header */
705function buildBreadcrumbs() {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700706 var $breadcrumbUl = $(".dac-header-crumbs");
707 var primaryNavLink = ".dac-nav-list > .dac-nav-item > .dac-nav-link";
708
Dirk Doughertyc3921652014-05-13 16:55:26 -0700709 // Add the secondary horizontal nav item, if provided
Dirk Dougherty29e93432015-05-05 18:17:13 -0700710 var $selectedSecondNav = $(".dac-nav-secondary .dac-nav-link.selected").clone()
711 .attr('class', 'dac-header-crumbs-link');
712
Dirk Doughertyc3921652014-05-13 16:55:26 -0700713 if ($selectedSecondNav.length) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700714 $breadcrumbUl.prepend($('<li class="dac-header-crumbs-item">').append($selectedSecondNav));
Dirk Doughertyc3921652014-05-13 16:55:26 -0700715 }
Dirk Dougherty29e93432015-05-05 18:17:13 -0700716
Dirk Doughertyc3921652014-05-13 16:55:26 -0700717 // Add the primary horizontal nav
Dirk Dougherty29e93432015-05-05 18:17:13 -0700718 var $selectedFirstNav = $(primaryNavLink + ".selected, " + primaryNavLink + ".has-subnav").clone()
719 .attr('class', 'dac-header-crumbs-link');
720
Dirk Doughertyc3921652014-05-13 16:55:26 -0700721 // If there's no header nav item, use the logo link and title from alt text
722 if ($selectedFirstNav.length < 1) {
Dirk Dougherty29e93432015-05-05 18:17:13 -0700723 $selectedFirstNav = $('<a class="dac-header-crumbs-link">')
Dirk Doughertyc3921652014-05-13 16:55:26 -0700724 .attr('href', $("div#header .logo a").attr('href'))
725 .text($("div#header .logo img").attr('alt'));
726 }
Dirk Dougherty29e93432015-05-05 18:17:13 -0700727 $breadcrumbUl.prepend($('<li class="dac-header-crumbs-item">').append($selectedFirstNav));
Dirk Doughertyc3921652014-05-13 16:55:26 -0700728}
729
730
731
Scott Maine624b3f2013-09-12 12:56:41 -0700732/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700733function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700734 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
735 if ($("ul#nav li.selected").length) {
736 unHighlightSidenav();
737 }
738 // look for URL in sidenav, including the hash
739 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
740
741 // If the selNavLink is still empty, look for it without the hash
742 if ($selNavLink.length == 0) {
743 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
744 }
745
Scott Mainf6145542013-04-01 16:38:11 -0700746 var $selListItem;
747 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700748 // Find this page's <li> in sidenav and set selected
749 $selListItem = $selNavLink.closest('li');
750 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700751
Scott Mainf6145542013-04-01 16:38:11 -0700752 // Traverse up the tree and expand all parent nav-sections
753 $selNavLink.parents('li.nav-section').each(function() {
754 $(this).addClass('expanded');
755 $(this).children('ul').show();
756 });
757 }
758}
759
Scott Maine624b3f2013-09-12 12:56:41 -0700760function unHighlightSidenav() {
761 $("ul#nav li.selected").removeClass("selected");
762 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
763}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700764
765function toggleFullscreen(enable) {
766 var delay = 20;
767 var enabled = true;
768 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
769 if (enable) {
770 // Currently NOT USING fullscreen; enable fullscreen
771 stylesheet.removeAttr('disabled');
772 $('#nav-swap .fullscreen').removeClass('disabled');
773 $('#devdoc-nav').css({left:''});
774 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
775 enabled = true;
776 } else {
777 // Currently USING fullscreen; disable fullscreen
778 stylesheet.attr('disabled', 'disabled');
779 $('#nav-swap .fullscreen').addClass('disabled');
780 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
781 enabled = false;
782 }
smain@google.com6bdcb982014-11-14 11:53:07 -0800783 writeCookie("fullscreen", enabled, null);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700784 setNavBarDimensions();
Scott Maine4d8f1b2012-06-21 18:03:05 -0700785 resizeNav(delay);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700786 updateSideNavDimensions();
Scott Maine4d8f1b2012-06-21 18:03:05 -0700787 setTimeout(initSidenavHeightResize,delay);
788}
789
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700790// TODO: Refactor into a closure.
791var navBarLeftPos;
792var navBarWidth;
793function setNavBarDimensions() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700794 navBarLeftPos = $('#body-content').offset().left;
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700795 navBarWidth = $('#side-nav').width();
Scott Maine4d8f1b2012-06-21 18:03:05 -0700796}
797
798
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700799function updateSideNavDimensions() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700800 var newLeft = $(window).scrollLeft() - navBarLeftPos;
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700801 $('#devdoc-nav').css({left: -newLeft, width: navBarWidth});
Dirk Dougherty29e93432015-05-05 18:17:13 -0700802 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('padding-left')))});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700803}
Scott Main3b90aff2013-08-01 18:09:35 -0700804
Scott Maine4d8f1b2012-06-21 18:03:05 -0700805// TODO: use $(document).ready instead
806function addLoadEvent(newfun) {
807 var current = window.onload;
808 if (typeof window.onload != 'function') {
809 window.onload = newfun;
810 } else {
811 window.onload = function() {
812 current();
813 newfun();
814 }
815 }
816}
817
818var agent = navigator['userAgent'].toLowerCase();
819// If a mobile phone, set flag and do mobile setup
820if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
821 (agent.indexOf("blackberry") != -1) ||
822 (agent.indexOf("webos") != -1) ||
823 (agent.indexOf("mini") != -1)) { // opera mini browsers
824 isMobile = true;
825}
826
827
Scott Main498d7102013-08-21 15:47:38 -0700828$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700829 $("pre:not(.no-pretty-print)").addClass("prettyprint");
830 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700831});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700832
Scott Maine4d8f1b2012-06-21 18:03:05 -0700833
834
835
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700836/* ######### RESIZE THE SIDENAV ########## */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700837
838function resizeNav(delay) {
839 var $nav = $("#devdoc-nav");
840 var $window = $(window);
841 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700842
Scott Maine4d8f1b2012-06-21 18:03:05 -0700843 // Get the height of entire window and the total header height.
844 // Then figure out based on scroll position whether the header is visible
845 var windowHeight = $window.height();
846 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700847 var headerHeight = $('#header-wrapper').outerHeight();
848 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700849
850 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700851 // Could be either margin or top position, depending on whether the nav is fixed.
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700852 var topMargin = (parseInt($nav.css('top')) || 20) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700853 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700854
Scott Maine4d8f1b2012-06-21 18:03:05 -0700855 // Depending on whether the header is visible, set the side nav's height.
856 if (headerVisible) {
857 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700858 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700859 } else {
860 // Once header is off screen, the nav height is almost full window height
861 navHeight = windowHeight - topMargin;
862 }
Scott Main3b90aff2013-08-01 18:09:35 -0700863
864
865
Scott Maine4d8f1b2012-06-21 18:03:05 -0700866 $scrollPanes = $(".scroll-pane");
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700867 if ($window.width() < 720) {
868 $nav.css('height', '');
869 } else if ($scrollPanes.length > 1) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700870 // subtract the height of the api level widget and nav swapper from the available nav height
871 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700872
Scott Maine4d8f1b2012-06-21 18:03:05 -0700873 $("#swapper").css({height:navHeight + "px"});
874 if ($("#nav-tree").is(":visible")) {
875 $("#nav-tree").css({height:navHeight});
876 }
Scott Main3b90aff2013-08-01 18:09:35 -0700877
878 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700879 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700880
881 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700882 // then the package panel should begin to shrink
883 if (parseInt(classesHeight) <= 0) {
884 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
885 $("#packages-nav").css({height:navHeight - 10});
886 }
Scott Main3b90aff2013-08-01 18:09:35 -0700887
Scott Maine4d8f1b2012-06-21 18:03:05 -0700888 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
889 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700890
891
Scott Maine4d8f1b2012-06-21 18:03:05 -0700892 } else {
893 $nav.height(navHeight);
894 }
Scott Main3b90aff2013-08-01 18:09:35 -0700895
Scott Maine4d8f1b2012-06-21 18:03:05 -0700896 if (delay) {
897 updateFromResize = true;
898 delayedReInitScrollbars(delay);
899 } else {
900 reInitScrollbars();
901 }
Scott Main3b90aff2013-08-01 18:09:35 -0700902
Scott Maine4d8f1b2012-06-21 18:03:05 -0700903}
904
905var updateScrollbars = false;
906var updateFromResize = false;
907
908/* Re-initialize the scrollbars to account for changed nav size.
909 * This method postpones the actual update by a 1/4 second in order to optimize the
910 * scroll performance while the header is still visible, because re-initializing the
911 * scroll panes is an intensive process.
912 */
913function delayedReInitScrollbars(delay) {
914 // If we're scheduled for an update, but have received another resize request
915 // before the scheduled resize has occured, just ignore the new request
916 // (and wait for the scheduled one).
917 if (updateScrollbars && updateFromResize) {
918 updateFromResize = false;
919 return;
920 }
Scott Main3b90aff2013-08-01 18:09:35 -0700921
Scott Maine4d8f1b2012-06-21 18:03:05 -0700922 // We're scheduled for an update and the update request came from this method's setTimeout
923 if (updateScrollbars && !updateFromResize) {
924 reInitScrollbars();
925 updateScrollbars = false;
926 } else {
927 updateScrollbars = true;
928 updateFromResize = false;
929 setTimeout('delayedReInitScrollbars()',delay);
930 }
931}
932
933/* Re-initialize the scrollbars to account for changed nav size. */
934function reInitScrollbars() {
935 var pane = $(".scroll-pane").each(function(){
936 var api = $(this).data('jsp');
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700937 if (!api) {return;}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700938 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700939 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700940 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
941}
942
943
944/* Resize the height of the nav panels in the reference,
945 * and save the new size to a cookie */
946function saveNavPanels() {
947 var basePath = getBaseUri(location.pathname);
948 var section = basePath.substring(1,basePath.indexOf("/",1));
smain@google.com6bdcb982014-11-14 11:53:07 -0800949 writeCookie("height", resizePackagesNav.css("height"), section);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700950}
951
952
953
954function restoreHeight(packageHeight) {
955 $("#resize-packages-nav").height(packageHeight);
956 $("#packages-nav").height(packageHeight);
957 // var classesHeight = navHeight - packageHeight;
958 // $("#classes-nav").css({height:classesHeight});
959 // $("#classes-nav .jspContainer").css({height:classesHeight});
960}
961
962
963
964/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
965
966
967
968
969
Scott Main3b90aff2013-08-01 18:09:35 -0700970/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700971 This is called when the page finished loading. */
972function scrollIntoView(nav) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -0700973 return;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700974 var $nav = $("#"+nav);
975 var element = $nav.jScrollPane({/* ...settings... */});
976 var api = element.data('jsp');
977
978 if ($nav.is(':visible')) {
979 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700980 if ($selected.length == 0) {
981 // If no selected item found, exit
982 return;
983 }
Scott Main52dd2062013-08-15 12:22:28 -0700984 // get the selected item's offset from its container nav by measuring the item's offset
985 // relative to the document then subtract the container nav's offset relative to the document
986 var selectedOffset = $selected.offset().top - $nav.offset().top;
987 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
988 // if it's more than 80% down the nav
989 // scroll the item up by an amount equal to 80% the container nav's height
990 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700991 }
992 }
993}
994
995
996
997
998
999
1000/* Show popup dialogs */
1001function showDialog(id) {
1002 $dialog = $("#"+id);
1003 $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>');
1004 $dialog.wrapInner('<div/>');
1005 $dialog.removeClass("hide");
1006}
1007
1008
1009
1010
1011
1012/* ######### COOKIES! ########## */
1013
1014function readCookie(cookie) {
1015 var myCookie = cookie_namespace+"_"+cookie+"=";
1016 if (document.cookie) {
1017 var index = document.cookie.indexOf(myCookie);
1018 if (index != -1) {
1019 var valStart = index + myCookie.length;
1020 var valEnd = document.cookie.indexOf(";", valStart);
1021 if (valEnd == -1) {
1022 valEnd = document.cookie.length;
1023 }
1024 var val = document.cookie.substring(valStart, valEnd);
1025 return val;
1026 }
1027 }
1028 return 0;
1029}
1030
smain@google.com6bdcb982014-11-14 11:53:07 -08001031function writeCookie(cookie, val, section) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001032 if (val==undefined) return;
1033 section = section == null ? "_" : "_"+section+"_";
smain@google.com6bdcb982014-11-14 11:53:07 -08001034 var age = 2*365*24*60*60; // set max-age to 2 years
Scott Main3b90aff2013-08-01 18:09:35 -07001035 var cookieValue = cookie_namespace + section + cookie + "=" + val
smain@google.com80e38f42014-11-03 10:47:12 -08001036 + "; max-age=" + age +"; path=/";
Scott Maine4d8f1b2012-06-21 18:03:05 -07001037 document.cookie = cookieValue;
1038}
1039
1040/* ######### END COOKIES! ########## */
1041
1042
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001043var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -07001044var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001045var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -07001046/* Sets the vertical scoll position at which the sticky bar should appear.
1047 This method is called to reset the position when search results appear or hide */
1048function setStickyTop() {
Dirk Dougherty29e93432015-05-05 18:17:13 -07001049 stickyTop = $('#header-wrapper').outerHeight() - $('#header > .dac-header-inner').outerHeight();
Dirk Doughertyc3921652014-05-13 16:55:26 -07001050}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001051
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001052/*
Scott Mainb16376f2014-05-21 20:35:47 -07001053 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -07001054 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001055$(window).scroll(function(event) {
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001056 // Exit if the mouse target is a DIV, because that means the event is coming
1057 // from a scrollable div and so there's no need to make adjustments to our layout
1058 if ($(event.target).nodeName == "DIV") {
1059 return;
1060 }
1061
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001062 checkSticky();
1063});
1064
1065function checkSticky() {
1066 setStickyTop();
1067 var $headerEl = $('#header');
1068 // Exit if there's no sidenav
1069 if ($('#side-nav').length == 0) return;
1070
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001071 var top = $(window).scrollTop();
1072 // we set the navbar fixed when the scroll position is beyond the height of the site header...
Dirk Doughertycf7a3b92015-05-21 00:52:33 -07001073 var shouldBeSticky = top > stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001074 // ... except if the document content is shorter than the sidenav height.
1075 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
1076 if ($("#doc-col").height() < $("#side-nav").height()) {
1077 shouldBeSticky = false;
1078 }
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001079 // Nor on mobile
1080 if (window.innerWidth < 720) {
1081 shouldBeSticky = false;
1082 }
Scott Mainf5257812014-05-22 17:26:38 -07001083 // Account for horizontal scroll
1084 var scrollLeft = $(window).scrollLeft();
1085 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
1086 if (sticky && (scrollLeft != prevScrollLeft)) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001087 updateSideNavDimensions();
Scott Mainf5257812014-05-22 17:26:38 -07001088 prevScrollLeft = scrollLeft;
1089 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001090
1091 // Don't continue if the header is sufficently far away
1092 // (to avoid intensive resizing that slows scrolling)
1093 if (sticky == shouldBeSticky) {
1094 return;
1095 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001096
1097 // If sticky header visible and position is now near top, hide sticky
1098 if (sticky && !shouldBeSticky) {
1099 sticky = false;
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001100 // make the sidenav static again
1101 $('#devdoc-nav')
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001102 .removeClass('fixed')
1103 .css({'width':'auto','margin':''});
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001104 // delay hide the sticky
Dirk Dougherty29e93432015-05-05 18:17:13 -07001105 $headerEl.removeClass('is-sticky');
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001106
1107 // update the sidenaav position for side scrolling
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001108 updateSideNavDimensions();
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001109 } else if (!sticky && shouldBeSticky) {
1110 sticky = true;
Dirk Dougherty29e93432015-05-05 18:17:13 -07001111 $headerEl.addClass('is-sticky');
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001112
1113 // make the sidenav fixed
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001114 $('#devdoc-nav')
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001115 .addClass('fixed');
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001116
1117 // update the sidenaav position for side scrolling
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001118 updateSideNavDimensions();
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001119
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001120 }
1121 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001122}
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001123
1124/*
1125 * Manages secion card states and nav resize to conclude loading
1126 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07001127(function() {
1128 $(document).ready(function() {
1129
Dirk Doughertyc3921652014-05-13 16:55:26 -07001130 // Stack hover states
1131 $('.section-card-menu').each(function(index, el) {
1132 var height = $(el).height();
1133 $(el).css({height:height+'px', position:'relative'});
1134 var $cardInfo = $(el).find('.card-info');
1135
1136 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1137 });
1138
Dirk Doughertyc3921652014-05-13 16:55:26 -07001139 });
1140
1141})();
1142
Scott Maine4d8f1b2012-06-21 18:03:05 -07001143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
Scott Maind7026f72013-06-17 15:08:49 -07001156/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001157
1158
1159
1160
1161
1162function toggle(obj, slide) {
1163 var ul = $("ul:first", obj);
1164 var li = ul.parent();
1165 if (li.hasClass("closed")) {
1166 if (slide) {
1167 ul.slideDown("fast");
1168 } else {
1169 ul.show();
1170 }
1171 li.removeClass("closed");
1172 li.addClass("open");
1173 $(".toggle-img", li).attr("title", "hide pages");
1174 } else {
1175 ul.slideUp("fast");
1176 li.removeClass("open");
1177 li.addClass("closed");
1178 $(".toggle-img", li).attr("title", "show pages");
1179 }
1180}
1181
1182
Scott Maine4d8f1b2012-06-21 18:03:05 -07001183function buildToggleLists() {
1184 $(".toggle-list").each(
1185 function(i) {
1186 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1187 $(this).addClass("closed");
1188 });
1189}
1190
1191
1192
Scott Maind7026f72013-06-17 15:08:49 -07001193function hideNestedItems(list, toggle) {
1194 $list = $(list);
1195 // hide nested lists
1196 if($list.hasClass('showing')) {
1197 $("li ol", $list).hide('fast');
1198 $list.removeClass('showing');
1199 // show nested lists
1200 } else {
1201 $("li ol", $list).show('fast');
1202 $list.addClass('showing');
1203 }
1204 $(".more,.less",$(toggle)).toggle();
1205}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001206
1207
smain@google.com95948b82014-06-16 19:24:25 -07001208/* Call this to add listeners to a <select> element for Studio/Eclipse/Other docs */
1209function setupIdeDocToggle() {
1210 $( "select.ide" ).change(function() {
1211 var selected = $(this).find("option:selected").attr("value");
1212 $(".select-ide").hide();
1213 $(".select-ide."+selected).show();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001214
smain@google.com95948b82014-06-16 19:24:25 -07001215 $("select.ide").val(selected);
1216 });
1217}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242/* REFERENCE NAV SWAP */
1243
1244
1245function getNavPref() {
1246 var v = readCookie('reference_nav');
1247 if (v != NAV_PREF_TREE) {
1248 v = NAV_PREF_PANELS;
1249 }
1250 return v;
1251}
1252
1253function chooseDefaultNav() {
1254 nav_pref = getNavPref();
1255 if (nav_pref == NAV_PREF_TREE) {
1256 $("#nav-panels").toggle();
1257 $("#panel-link").toggle();
1258 $("#nav-tree").toggle();
1259 $("#tree-link").toggle();
1260 }
1261}
1262
1263function swapNav() {
1264 if (nav_pref == NAV_PREF_TREE) {
1265 nav_pref = NAV_PREF_PANELS;
1266 } else {
1267 nav_pref = NAV_PREF_TREE;
1268 init_default_navtree(toRoot);
1269 }
smain@google.com6bdcb982014-11-14 11:53:07 -08001270 writeCookie("nav", nav_pref, "reference");
Scott Maine4d8f1b2012-06-21 18:03:05 -07001271
1272 $("#nav-panels").toggle();
1273 $("#panel-link").toggle();
1274 $("#nav-tree").toggle();
1275 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001276
Scott Maine4d8f1b2012-06-21 18:03:05 -07001277 resizeNav();
1278
1279 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1280 $("#nav-tree .jspContainer:visible")
1281 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1282 // Another nasty hack to make the scrollbar appear now that we have height
1283 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001284
Scott Maine4d8f1b2012-06-21 18:03:05 -07001285 if ($("#nav-tree").is(':visible')) {
1286 scrollIntoView("nav-tree");
1287 } else {
1288 scrollIntoView("packages-nav");
1289 scrollIntoView("classes-nav");
1290 }
1291}
1292
1293
1294
Scott Mainf5089842012-08-14 16:31:07 -07001295/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001296/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001297/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001298
1299function getBaseUri(uri) {
1300 var intlUrl = (uri.substring(0,6) == "/intl/");
1301 if (intlUrl) {
1302 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1303 base = base.substring(base.indexOf('/')+1, base.length);
1304 //alert("intl, returning base url: /" + base);
1305 return ("/" + base);
1306 } else {
1307 //alert("not intl, returning uri as found.");
1308 return uri;
1309 }
1310}
1311
1312function requestAppendHL(uri) {
1313//append "?hl=<lang> to an outgoing request (such as to blog)
1314 var lang = getLangPref();
1315 if (lang) {
1316 var q = 'hl=' + lang;
1317 uri += '?' + q;
1318 window.location = uri;
1319 return false;
1320 } else {
1321 return true;
1322 }
1323}
1324
1325
Scott Maine4d8f1b2012-06-21 18:03:05 -07001326function changeNavLang(lang) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001327 if (lang === 'en') { return; }
1328
Dirk Doughertya2c9b912015-10-05 16:45:19 -07001329 var $links = $("a[" + lang + "-lang],p[" + lang + "-lang]");
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001330 $links.each(function(){ // for each link with a translation
Scott Main6eb95f12012-10-02 17:12:23 -07001331 var $link = $(this);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07001332 // put the desired language from the attribute as the text
1333 $link.text($link.attr(lang + '-lang'))
Scott Main6eb95f12012-10-02 17:12:23 -07001334 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001335}
1336
Scott Main015d6162013-01-29 09:01:52 -08001337function changeLangPref(lang, submit) {
smain@google.com6bdcb982014-11-14 11:53:07 -08001338 writeCookie("pref_lang", lang, null);
Scott Main015d6162013-01-29 09:01:52 -08001339
1340 // ####### TODO: Remove this condition once we're stable on devsite #######
1341 // This condition is only needed if we still need to support legacy GAE server
1342 if (devsite) {
1343 // Switch language when on Devsite server
1344 if (submit) {
1345 $("#setlang").submit();
1346 }
1347 } else {
1348 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001349 if (submit) {
1350 window.location = getBaseUri(location.pathname);
1351 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001352 }
1353}
1354
1355function loadLangPref() {
1356 var lang = readCookie("pref_lang");
1357 if (lang != 0) {
1358 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1359 }
1360}
1361
1362function getLangPref() {
1363 var lang = $("#language").find(":selected").attr("value");
1364 if (!lang) {
1365 lang = readCookie("pref_lang");
1366 }
1367 return (lang != 0) ? lang : 'en';
1368}
1369
1370/* ########## END LOCALIZATION ############ */
1371
1372
1373
1374
1375
1376
1377/* Used to hide and reveal supplemental content, such as long code samples.
1378 See the companion CSS in android-developer-docs.css */
1379function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001380 var div = $(obj).closest(".toggle-content");
1381 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001382 if (div.hasClass("closed")) { // if it's closed, open it
1383 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001384 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001385 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001386 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001387 + "assets/images/triangle-opened.png");
1388 } else { // if it's open, close it
1389 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001390 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001391 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001392 div.find(".toggle-content").removeClass("open").addClass("closed")
1393 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001394 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001395 + "assets/images/triangle-closed.png");
1396 });
1397 }
1398 return false;
1399}
Scott Mainf5089842012-08-14 16:31:07 -07001400
1401
Scott Maindb3678b2012-10-23 14:13:41 -07001402/* New version of expandable content */
1403function toggleExpandable(link,id) {
1404 if($(id).is(':visible')) {
1405 $(id).slideUp();
1406 $(link).removeClass('expanded');
1407 } else {
1408 $(id).slideDown();
1409 $(link).addClass('expanded');
1410 }
1411}
1412
1413function hideExpandable(ids) {
1414 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001415 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001416}
1417
Scott Mainf5089842012-08-14 16:31:07 -07001418
1419
1420
1421
Scott Main3b90aff2013-08-01 18:09:35 -07001422/*
Scott Mainf5089842012-08-14 16:31:07 -07001423 * Slideshow 1.0
1424 * Used on /index.html and /develop/index.html for carousel
1425 *
1426 * Sample usage:
1427 * HTML -
1428 * <div class="slideshow-container">
1429 * <a href="" class="slideshow-prev">Prev</a>
1430 * <a href="" class="slideshow-next">Next</a>
1431 * <ul>
1432 * <li class="item"><img src="images/marquee1.jpg"></li>
1433 * <li class="item"><img src="images/marquee2.jpg"></li>
1434 * <li class="item"><img src="images/marquee3.jpg"></li>
1435 * <li class="item"><img src="images/marquee4.jpg"></li>
1436 * </ul>
1437 * </div>
1438 *
1439 * <script type="text/javascript">
1440 * $('.slideshow-container').dacSlideshow({
1441 * auto: true,
1442 * btnPrev: '.slideshow-prev',
1443 * btnNext: '.slideshow-next'
1444 * });
1445 * </script>
1446 *
1447 * Options:
1448 * btnPrev: optional identifier for previous button
1449 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001450 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001451 * auto: whether or not to auto-proceed
1452 * speed: animation speed
1453 * autoTime: time between auto-rotation
1454 * easing: easing function for transition
1455 * start: item to select by default
1456 * scroll: direction to scroll in
1457 * pagination: whether or not to include dotted pagination
1458 *
1459 */
1460
1461 (function($) {
1462 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001463
Scott Mainf5089842012-08-14 16:31:07 -07001464 //Options - see above
1465 o = $.extend({
1466 btnPrev: null,
1467 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001468 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001469 auto: true,
1470 speed: 500,
1471 autoTime: 12000,
1472 easing: null,
1473 start: 0,
1474 scroll: 1,
1475 pagination: true
1476
1477 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001478
1479 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001480 return this.each(function() {
1481
1482 var running = false;
1483 var animCss = o.vertical ? "top" : "left";
1484 var sizeCss = o.vertical ? "height" : "width";
1485 var div = $(this);
1486 var ul = $("ul", div);
1487 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001488 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001489 var timer = null;
1490
1491 var li = $("li", ul);
1492 var itemLength = li.size();
1493 var curr = o.start;
1494
1495 li.css({float: o.vertical ? "none" : "left"});
1496 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1497 div.css({position: "relative", "z-index": "2", left: "0px"});
1498
1499 var liSize = o.vertical ? height(li) : width(li);
1500 var ulSize = liSize * itemLength;
1501 var divSize = liSize;
1502
1503 li.css({width: li.width(), height: li.height()});
1504 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1505
1506 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001507
Scott Mainf5089842012-08-14 16:31:07 -07001508 //Pagination
1509 if (o.pagination) {
1510 var pagination = $("<div class='pagination'></div>");
1511 var pag_ul = $("<ul></ul>");
1512 if (tl > 1) {
1513 for (var i=0;i<tl;i++) {
1514 var li = $("<li>"+i+"</li>");
1515 pag_ul.append(li);
1516 if (i==o.start) li.addClass('active');
1517 li.click(function() {
1518 go(parseInt($(this).text()));
1519 })
1520 }
1521 pagination.append(pag_ul);
1522 div.append(pagination);
1523 }
1524 }
Scott Main3b90aff2013-08-01 18:09:35 -07001525
Scott Mainf5089842012-08-14 16:31:07 -07001526 //Previous button
1527 if(o.btnPrev)
1528 $(o.btnPrev).click(function(e) {
1529 e.preventDefault();
1530 return go(curr-o.scroll);
1531 });
1532
1533 //Next button
1534 if(o.btnNext)
1535 $(o.btnNext).click(function(e) {
1536 e.preventDefault();
1537 return go(curr+o.scroll);
1538 });
Scott Maineb410352013-01-14 19:03:40 -08001539
1540 //Pause button
1541 if(o.btnPause)
1542 $(o.btnPause).click(function(e) {
1543 e.preventDefault();
1544 if ($(this).hasClass('paused')) {
1545 startRotateTimer();
1546 } else {
1547 pauseRotateTimer();
1548 }
1549 });
Scott Main3b90aff2013-08-01 18:09:35 -07001550
Scott Mainf5089842012-08-14 16:31:07 -07001551 //Auto rotation
1552 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001553
Scott Mainf5089842012-08-14 16:31:07 -07001554 function startRotateTimer() {
1555 clearInterval(timer);
1556 timer = setInterval(function() {
1557 if (curr == tl-1) {
1558 go(0);
1559 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001560 go(curr+o.scroll);
1561 }
Scott Mainf5089842012-08-14 16:31:07 -07001562 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001563 $(o.btnPause).removeClass('paused');
1564 }
1565
1566 function pauseRotateTimer() {
1567 clearInterval(timer);
1568 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001569 }
1570
1571 //Go to an item
1572 function go(to) {
1573 if(!running) {
1574
1575 if(to<0) {
1576 to = itemLength-1;
1577 } else if (to>itemLength-1) {
1578 to = 0;
1579 }
1580 curr = to;
1581
1582 running = true;
1583
1584 ul.animate(
1585 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1586 function() {
1587 running = false;
1588 }
1589 );
1590
1591 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1592 $( (curr-o.scroll<0 && o.btnPrev)
1593 ||
1594 (curr+o.scroll > itemLength && o.btnNext)
1595 ||
1596 []
1597 ).addClass("disabled");
1598
Scott Main3b90aff2013-08-01 18:09:35 -07001599
Scott Mainf5089842012-08-14 16:31:07 -07001600 var nav_items = $('li', pagination);
1601 nav_items.removeClass('active');
1602 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001603
Scott Mainf5089842012-08-14 16:31:07 -07001604
1605 }
1606 if(o.auto) startRotateTimer();
1607 return false;
1608 };
1609 });
1610 };
1611
1612 function css(el, prop) {
1613 return parseInt($.css(el[0], prop)) || 0;
1614 };
1615 function width(el) {
1616 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1617 };
1618 function height(el) {
1619 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1620 };
1621
1622 })(jQuery);
1623
1624
Scott Main3b90aff2013-08-01 18:09:35 -07001625/*
Scott Mainf5089842012-08-14 16:31:07 -07001626 * dacSlideshow 1.0
1627 * Used on develop/index.html for side-sliding tabs
1628 *
1629 * Sample usage:
1630 * HTML -
1631 * <div class="slideshow-container">
1632 * <a href="" class="slideshow-prev">Prev</a>
1633 * <a href="" class="slideshow-next">Next</a>
1634 * <ul>
1635 * <li class="item"><img src="images/marquee1.jpg"></li>
1636 * <li class="item"><img src="images/marquee2.jpg"></li>
1637 * <li class="item"><img src="images/marquee3.jpg"></li>
1638 * <li class="item"><img src="images/marquee4.jpg"></li>
1639 * </ul>
1640 * </div>
1641 *
1642 * <script type="text/javascript">
1643 * $('.slideshow-container').dacSlideshow({
1644 * auto: true,
1645 * btnPrev: '.slideshow-prev',
1646 * btnNext: '.slideshow-next'
1647 * });
1648 * </script>
1649 *
1650 * Options:
1651 * btnPrev: optional identifier for previous button
1652 * btnNext: optional identifier for next button
1653 * auto: whether or not to auto-proceed
1654 * speed: animation speed
1655 * autoTime: time between auto-rotation
1656 * easing: easing function for transition
1657 * start: item to select by default
1658 * scroll: direction to scroll in
1659 * pagination: whether or not to include dotted pagination
1660 *
1661 */
1662 (function($) {
1663 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001664
Scott Mainf5089842012-08-14 16:31:07 -07001665 //Options - see above
1666 o = $.extend({
1667 speed : 250,
1668 easing: null,
1669 nav_id: null,
1670 frame_id: null
1671 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001672
1673 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001674 return this.each(function() {
1675
1676 var curr = 0;
1677 var running = false;
1678 var animCss = "margin-left";
1679 var sizeCss = "width";
1680 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001681
Scott Mainf5089842012-08-14 16:31:07 -07001682 var nav = $(o.nav_id, div);
1683 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001684 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001685 var frame = div.find(o.frame_id);
1686 var content_width = $(frame).find('ul').width();
1687 //Buttons
1688 $(nav_li).click(function(e) {
1689 go($(nav_li).index($(this)));
1690 })
Scott Main3b90aff2013-08-01 18:09:35 -07001691
Scott Mainf5089842012-08-14 16:31:07 -07001692 //Go to an item
1693 function go(to) {
1694 if(!running) {
1695 curr = to;
1696 running = true;
1697
1698 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1699 function() {
1700 running = false;
1701 }
1702 );
1703
Scott Main3b90aff2013-08-01 18:09:35 -07001704
Scott Mainf5089842012-08-14 16:31:07 -07001705 nav_li.removeClass('active');
1706 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001707
Scott Mainf5089842012-08-14 16:31:07 -07001708
1709 }
1710 return false;
1711 };
1712 });
1713 };
1714
1715 function css(el, prop) {
1716 return parseInt($.css(el[0], prop)) || 0;
1717 };
1718 function width(el) {
1719 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1720 };
1721 function height(el) {
1722 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1723 };
1724
1725 })(jQuery);
1726
1727
1728
1729
1730
1731/* ######################################################## */
1732/* ################ SEARCH SUGGESTIONS ################## */
1733/* ######################################################## */
1734
1735
Scott Main7e447ed2013-02-19 17:22:37 -08001736
Scott Main0e76e7e2013-03-12 10:24:07 -07001737var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1738var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1739
Scott Mainf5089842012-08-14 16:31:07 -07001740var gMatches = new Array();
1741var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001742var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001743var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1744var gListLength = 0;
1745
1746
1747var gGoogleMatches = new Array();
1748var ROW_COUNT_GOOGLE = 15; // max number of results in list
1749var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001750
Scott Main0e76e7e2013-03-12 10:24:07 -07001751var gDocsMatches = new Array();
1752var ROW_COUNT_DOCS = 100; // max number of results in list
1753var gDocsListLength = 0;
1754
Scott Mainde295272013-03-25 15:48:35 -07001755function onSuggestionClick(link) {
1756 // When user clicks a suggested document, track it
smain@google.comed677d72014-12-12 10:19:17 -08001757 ga('send', 'event', 'Suggestion Click', 'clicked: ' + $(link).attr('href'),
1758 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07001759}
1760
Scott Mainf5089842012-08-14 16:31:07 -07001761function set_item_selected($li, selected)
1762{
1763 if (selected) {
1764 $li.attr('class','jd-autocomplete jd-selected');
1765 } else {
1766 $li.attr('class','jd-autocomplete');
1767 }
1768}
1769
1770function set_item_values(toroot, $li, match)
1771{
1772 var $link = $('a',$li);
1773 $link.html(match.__hilabel || match.label);
1774 $link.attr('href',toroot + match.link);
1775}
1776
Scott Main719acb42013-12-05 16:05:09 -08001777function set_item_values_jd(toroot, $li, match)
1778{
1779 var $link = $('a',$li);
1780 $link.html(match.title);
1781 $link.attr('href',toroot + match.url);
1782}
1783
Scott Main0e76e7e2013-03-12 10:24:07 -07001784function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001785 var $li = $("<li class='jd-autocomplete'></li>");
1786 $list.append($li);
1787
1788 $li.mousedown(function() {
1789 window.location = this.firstChild.getAttribute("href");
1790 });
1791 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001792 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001793 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001794 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1795 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001796 });
Scott Mainde295272013-03-25 15:48:35 -07001797 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001798 $li.attr('class','show-item');
1799 return $li;
1800}
1801
Scott Mainf5089842012-08-14 16:31:07 -07001802function sync_selection_table(toroot)
1803{
Scott Mainf5089842012-08-14 16:31:07 -07001804 var $li; //list item jquery object
1805 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001806
Scott Main0e76e7e2013-03-12 10:24:07 -07001807 // if there are NO results at all, hide all columns
1808 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1809 $('.suggest-card').hide(300);
1810 return;
1811 }
1812
1813 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001814 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001815 // reveal suggestion list
Scott Main0e76e7e2013-03-12 10:24:07 -07001816 $('.suggest-card.reference').show();
1817 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001818
Scott Main0e76e7e2013-03-12 10:24:07 -07001819 // reset the lists
Dirk Dougherty29e93432015-05-05 18:17:13 -07001820 $(".suggest-card.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001821
Scott Main0e76e7e2013-03-12 10:24:07 -07001822 // ########### ANDROID RESULTS #############
1823 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001824
Scott Main0e76e7e2013-03-12 10:24:07 -07001825 // determine android results to show
1826 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1827 gMatches.length : ROW_COUNT_FRAMEWORK;
1828 for (i=0; i<gListLength; i++) {
1829 var $li = new_suggestion($(".suggest-card.reference ul"));
1830 set_item_values(toroot, $li, gMatches[i]);
1831 set_item_selected($li, i == gSelectedIndex);
1832 }
1833 }
Scott Main7e447ed2013-02-19 17:22:37 -08001834
Scott Main0e76e7e2013-03-12 10:24:07 -07001835 // ########### GOOGLE RESULTS #############
1836 if (gGoogleMatches.length > 0) {
1837 // show header for list
1838 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001839
Scott Main0e76e7e2013-03-12 10:24:07 -07001840 // determine google results to show
1841 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1842 for (i=0; i<gGoogleListLength; i++) {
1843 var $li = new_suggestion($(".suggest-card.reference ul"));
1844 set_item_values(toroot, $li, gGoogleMatches[i]);
1845 set_item_selected($li, i == gSelectedIndex);
1846 }
1847 }
Scott Mainf5089842012-08-14 16:31:07 -07001848 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001849 $('.suggest-card.reference').hide();
Scott Main0e76e7e2013-03-12 10:24:07 -07001850 }
1851
1852 // ########### JD DOC RESULTS #############
1853 if (gDocsMatches.length > 0) {
1854 // reset the lists
Dirk Dougherty29e93432015-05-05 18:17:13 -07001855 $(".suggest-card:not(.reference) li").remove();
Scott Main0e76e7e2013-03-12 10:24:07 -07001856
1857 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001858 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1859 // The order must match the reverse order that each section appears as a card in
1860 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001861 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1862 for (i=0; i<gDocsListLength; i++) {
1863 var sugg = gDocsMatches[i];
1864 var $li;
1865 if (sugg.type == "design") {
1866 $li = new_suggestion($(".suggest-card.design ul"));
1867 } else
1868 if (sugg.type == "distribute") {
1869 $li = new_suggestion($(".suggest-card.distribute ul"));
1870 } else
Scott Main719acb42013-12-05 16:05:09 -08001871 if (sugg.type == "samples") {
1872 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1873 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001874 if (sugg.type == "training") {
1875 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1876 } else
Scott Main719acb42013-12-05 16:05:09 -08001877 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001878 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1879 } else {
1880 continue;
1881 }
1882
Scott Main719acb42013-12-05 16:05:09 -08001883 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001884 set_item_selected($li, i == gSelectedIndex);
1885 }
1886
1887 // add heading and show or hide card
1888 if ($(".suggest-card.design li").length > 0) {
1889 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1890 $(".suggest-card.design").show(300);
1891 } else {
1892 $('.suggest-card.design').hide(300);
1893 }
1894 if ($(".suggest-card.distribute li").length > 0) {
1895 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1896 $(".suggest-card.distribute").show(300);
1897 } else {
1898 $('.suggest-card.distribute').hide(300);
1899 }
1900 if ($(".child-card.guides li").length > 0) {
1901 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1902 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1903 }
1904 if ($(".child-card.training li").length > 0) {
1905 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1906 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1907 }
Scott Main719acb42013-12-05 16:05:09 -08001908 if ($(".child-card.samples li").length > 0) {
1909 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1910 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1911 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001912
1913 if ($(".suggest-card.develop li").length > 0) {
1914 $(".suggest-card.develop").show(300);
1915 } else {
1916 $('.suggest-card.develop').hide(300);
1917 }
1918
1919 } else {
Dirk Dougherty29e93432015-05-05 18:17:13 -07001920 $('.suggest-card:not(.reference)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001921 }
1922}
1923
Scott Main0e76e7e2013-03-12 10:24:07 -07001924/** Called by the search input's onkeydown and onkeyup events.
1925 * Handles navigation with keyboard arrows, Enter key to invoke search,
1926 * otherwise invokes search suggestions on key-up event.
1927 * @param e The JS event
1928 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001929 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001930 * @returns True if the event should bubble up
1931 */
Scott Mainf5089842012-08-14 16:31:07 -07001932function search_changed(e, kd, toroot)
1933{
Scott Main719acb42013-12-05 16:05:09 -08001934 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001935 var search = document.getElementById("search_autocomplete");
1936 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001937 // get the ul hosting the currently selected item
1938 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1939 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1940 var $selectedUl = $columns[gSelectedColumn];
1941
Scott Mainf5089842012-08-14 16:31:07 -07001942 // show/hide the close button
1943 if (text != '') {
Dirk Dougherty29e93432015-05-05 18:17:13 -07001944 $("#search-close").removeClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07001945 } else {
Dirk Dougherty29e93432015-05-05 18:17:13 -07001946 $("#search-close").addClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07001947 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001948 // 27 = esc
1949 if (e.keyCode == 27) {
1950 // close all search results
Dirk Dougherty29e93432015-05-05 18:17:13 -07001951 if (kd) $('#search-close').trigger('click');
Scott Main0e76e7e2013-03-12 10:24:07 -07001952 return true;
1953 }
Scott Mainf5089842012-08-14 16:31:07 -07001954 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001955 else if (e.keyCode == 13) {
1956 if (gSelectedIndex < 0) {
1957 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001958 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1959 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001960 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001961 return true;
1962 } else {
1963 // otherwise, results are already showing, so allow ajax to auto refresh the results
1964 // and ignore this Enter press to avoid the reload.
1965 return false;
1966 }
1967 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001968 // click the link corresponding to selected item
1969 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001970 return false;
1971 }
1972 }
Scott Mainb16376f2014-05-21 20:35:47 -07001973 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001974 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001975 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001976 if ((sticky ) && (search.value != "")) {
1977 $('body,html').animate({scrollTop:0}, '500', 'swing');
1978 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001979 return true;
1980 }
1981 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001982 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001983 // if the next item is a header, skip it
1984 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001985 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001986 }
1987 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001988 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001989 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001990 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1991 // If user reaches top, reset selected column
1992 if (gSelectedIndex < 0) {
1993 gSelectedColumn = -1;
1994 }
Scott Mainf5089842012-08-14 16:31:07 -07001995 }
1996 return false;
1997 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001998 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001999 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002000 // if the next item is a header, skip it
2001 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07002002 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08002003 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002004 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
2005 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
2006 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08002007 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07002008 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07002009 }
2010 return false;
2011 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002012 // Consider left/right arrow navigation
2013 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
2014 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
2015 // 37 LEFT ARROW
2016 // go left only if current column is not left-most column (last column)
2017 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
2018 $('li', $selectedUl).removeClass('jd-selected');
2019 gSelectedColumn++;
2020 $selectedUl = $columns[gSelectedColumn];
2021 // keep or reset the selected item to last item as appropriate
2022 gSelectedIndex = gSelectedIndex >
2023 $("li", $selectedUl).length-1 ?
2024 $("li", $selectedUl).length-1 : gSelectedIndex;
2025 // if the corresponding item is a header, move down
2026 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2027 gSelectedIndex++;
2028 }
Scott Main3b90aff2013-08-01 18:09:35 -07002029 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002030 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2031 return false;
2032 }
2033 // 39 RIGHT ARROW
2034 // go right only if current column is not the right-most column (first column)
2035 else if (e.keyCode == 39 && gSelectedColumn > 0) {
2036 $('li', $selectedUl).removeClass('jd-selected');
2037 gSelectedColumn--;
2038 $selectedUl = $columns[gSelectedColumn];
2039 // keep or reset the selected item to last item as appropriate
2040 gSelectedIndex = gSelectedIndex >
2041 $("li", $selectedUl).length-1 ?
2042 $("li", $selectedUl).length-1 : gSelectedIndex;
2043 // if the corresponding item is a header, move down
2044 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
2045 gSelectedIndex++;
2046 }
Scott Main3b90aff2013-08-01 18:09:35 -07002047 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07002048 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
2049 return false;
2050 }
2051 }
2052
Scott Main719acb42013-12-05 16:05:09 -08002053 // if key-up event and not arrow down/up/left/right,
2054 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07002055 else if (!kd && (e.keyCode != 40)
2056 && (e.keyCode != 38)
2057 && (e.keyCode != 37)
2058 && (e.keyCode != 39)) {
2059 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07002060 gMatches = new Array();
2061 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002062 gGoogleMatches = new Array();
2063 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07002064 gDocsMatches = new Array();
2065 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08002066
2067 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07002068 for (var i=0; i<DATA.length; i++) {
2069 var s = DATA[i];
2070 if (text.length != 0 &&
2071 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2072 gMatches[matchedCount] = s;
2073 matchedCount++;
2074 }
2075 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002076 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07002077 for (var i=0; i<gMatches.length; i++) {
2078 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08002079 }
2080
2081
2082 // Search for Google matches
2083 for (var i=0; i<GOOGLE_DATA.length; i++) {
2084 var s = GOOGLE_DATA[i];
2085 if (text.length != 0 &&
2086 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
2087 gGoogleMatches[matchedCountGoogle] = s;
2088 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07002089 }
2090 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002091 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08002092 for (var i=0; i<gGoogleMatches.length; i++) {
2093 var s = gGoogleMatches[i];
2094 }
2095
Scott Mainf5089842012-08-14 16:31:07 -07002096 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07002097
2098
2099
Scott Main719acb42013-12-05 16:05:09 -08002100 // Search for matching JD docs
Dirk Dougherty9b7f8f22014-11-01 17:08:56 -07002101 if (text.length >= 2) {
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002102 // match only the beginning of a word
2103 var queryStr = text.toLowerCase();
Scott Main719acb42013-12-05 16:05:09 -08002104
2105 // Search for Training classes
2106 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002107 // current search comparison, with counters for tag and title,
2108 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08002109 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07002110 s.matched_tag = 0;
2111 s.matched_title = 0;
2112 var matched = false;
2113
2114 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08002115 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002116 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002117 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002118 matched = true;
2119 s.matched_tag = j + 1; // add 1 to index position
2120 }
2121 }
Scott Main719acb42013-12-05 16:05:09 -08002122 // Don't consider doc title for lessons (only for class landing pages),
2123 // unless the lesson has a tag that already matches
2124 if ((s.lang == currentLang) &&
2125 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002126 // it matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002127 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002128 matched = true;
2129 s.matched_title = 1;
2130 }
2131 }
2132 if (matched) {
2133 gDocsMatches[matchedCountDocs] = s;
2134 matchedCountDocs++;
2135 }
2136 }
Scott Main719acb42013-12-05 16:05:09 -08002137
2138
2139 // Search for API Guides
2140 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2141 // current search comparison, with counters for tag and title,
2142 // used later to improve ranking
2143 var s = GUIDE_RESOURCES[i];
2144 s.matched_tag = 0;
2145 s.matched_title = 0;
2146 var matched = false;
2147
2148 // Check if query matches any tags; work backwards toward 1 to assist ranking
2149 for (var j = s.keywords.length - 1; j >= 0; j--) {
2150 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002151 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
2152
Scott Main719acb42013-12-05 16:05:09 -08002153 matched = true;
2154 s.matched_tag = j + 1; // add 1 to index position
2155 }
2156 }
2157 // Check if query matches the doc title, but only for current language
2158 if (s.lang == currentLang) {
2159 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002160 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002161 matched = true;
2162 s.matched_title = 1;
2163 }
2164 }
2165 if (matched) {
2166 gDocsMatches[matchedCountDocs] = s;
2167 matchedCountDocs++;
2168 }
2169 }
2170
2171
2172 // Search for Tools Guides
2173 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2174 // current search comparison, with counters for tag and title,
2175 // used later to improve ranking
2176 var s = TOOLS_RESOURCES[i];
2177 s.matched_tag = 0;
2178 s.matched_title = 0;
2179 var matched = false;
2180
2181 // Check if query matches any tags; work backwards toward 1 to assist ranking
2182 for (var j = s.keywords.length - 1; j >= 0; j--) {
2183 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002184 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002185 matched = true;
2186 s.matched_tag = j + 1; // add 1 to index position
2187 }
2188 }
2189 // Check if query matches the doc title, but only for current language
2190 if (s.lang == currentLang) {
2191 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002192 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002193 matched = true;
2194 s.matched_title = 1;
2195 }
2196 }
2197 if (matched) {
2198 gDocsMatches[matchedCountDocs] = s;
2199 matchedCountDocs++;
2200 }
2201 }
2202
2203
2204 // Search for About docs
2205 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2206 // current search comparison, with counters for tag and title,
2207 // used later to improve ranking
2208 var s = ABOUT_RESOURCES[i];
2209 s.matched_tag = 0;
2210 s.matched_title = 0;
2211 var matched = false;
2212
2213 // Check if query matches any tags; work backwards toward 1 to assist ranking
2214 for (var j = s.keywords.length - 1; j >= 0; j--) {
2215 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002216 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002217 matched = true;
2218 s.matched_tag = j + 1; // add 1 to index position
2219 }
2220 }
2221 // Check if query matches the doc title, but only for current language
2222 if (s.lang == currentLang) {
2223 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002224 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002225 matched = true;
2226 s.matched_title = 1;
2227 }
2228 }
2229 if (matched) {
2230 gDocsMatches[matchedCountDocs] = s;
2231 matchedCountDocs++;
2232 }
2233 }
2234
2235
2236 // Search for Design guides
2237 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2238 // current search comparison, with counters for tag and title,
2239 // used later to improve ranking
2240 var s = DESIGN_RESOURCES[i];
2241 s.matched_tag = 0;
2242 s.matched_title = 0;
2243 var matched = false;
2244
2245 // Check if query matches any tags; work backwards toward 1 to assist ranking
2246 for (var j = s.keywords.length - 1; j >= 0; j--) {
2247 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002248 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002249 matched = true;
2250 s.matched_tag = j + 1; // add 1 to index position
2251 }
2252 }
2253 // Check if query matches the doc title, but only for current language
2254 if (s.lang == currentLang) {
2255 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002256 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002257 matched = true;
2258 s.matched_title = 1;
2259 }
2260 }
2261 if (matched) {
2262 gDocsMatches[matchedCountDocs] = s;
2263 matchedCountDocs++;
2264 }
2265 }
2266
2267
2268 // Search for Distribute guides
2269 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2270 // current search comparison, with counters for tag and title,
2271 // used later to improve ranking
2272 var s = DISTRIBUTE_RESOURCES[i];
2273 s.matched_tag = 0;
2274 s.matched_title = 0;
2275 var matched = false;
2276
2277 // Check if query matches any tags; work backwards toward 1 to assist ranking
2278 for (var j = s.keywords.length - 1; j >= 0; j--) {
2279 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002280 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002281 matched = true;
2282 s.matched_tag = j + 1; // add 1 to index position
2283 }
2284 }
2285 // Check if query matches the doc title, but only for current language
2286 if (s.lang == currentLang) {
2287 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002288 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002289 matched = true;
2290 s.matched_title = 1;
2291 }
2292 }
2293 if (matched) {
2294 gDocsMatches[matchedCountDocs] = s;
2295 matchedCountDocs++;
2296 }
2297 }
2298
2299
2300 // Search for Google guides
2301 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2302 // current search comparison, with counters for tag and title,
2303 // used later to improve ranking
2304 var s = GOOGLE_RESOURCES[i];
2305 s.matched_tag = 0;
2306 s.matched_title = 0;
2307 var matched = false;
2308
2309 // Check if query matches any tags; work backwards toward 1 to assist ranking
2310 for (var j = s.keywords.length - 1; j >= 0; j--) {
2311 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002312 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002313 matched = true;
2314 s.matched_tag = j + 1; // add 1 to index position
2315 }
2316 }
2317 // Check if query matches the doc title, but only for current language
2318 if (s.lang == currentLang) {
2319 // if query matches the doc title
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002320 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002321 matched = true;
2322 s.matched_title = 1;
2323 }
2324 }
2325 if (matched) {
2326 gDocsMatches[matchedCountDocs] = s;
2327 matchedCountDocs++;
2328 }
2329 }
2330
2331
2332 // Search for Samples
2333 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2334 // current search comparison, with counters for tag and title,
2335 // used later to improve ranking
2336 var s = SAMPLES_RESOURCES[i];
2337 s.matched_tag = 0;
2338 s.matched_title = 0;
2339 var matched = false;
2340 // Check if query matches any tags; work backwards toward 1 to assist ranking
2341 for (var j = s.keywords.length - 1; j >= 0; j--) {
2342 // it matches a tag
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002343 if (s.keywords[j].toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002344 matched = true;
2345 s.matched_tag = j + 1; // add 1 to index position
2346 }
2347 }
2348 // Check if query matches the doc title, but only for current language
2349 if (s.lang == currentLang) {
2350 // if query matches the doc title.t
Dirk Dougherty0b9e2042015-07-02 14:21:55 -07002351 if (s.title.toLowerCase().indexOf(queryStr) == 0) {
Scott Main719acb42013-12-05 16:05:09 -08002352 matched = true;
2353 s.matched_title = 1;
2354 }
2355 }
2356 if (matched) {
2357 gDocsMatches[matchedCountDocs] = s;
2358 matchedCountDocs++;
2359 }
2360 }
2361
2362 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002363 rank_autocomplete_doc_results(text, gDocsMatches);
2364 }
2365
2366 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002367 sync_selection_table(toroot);
2368 return true; // allow the event to bubble up to the search api
2369 }
2370}
2371
Scott Main0e76e7e2013-03-12 10:24:07 -07002372/* Order the jd doc result list based on match quality */
2373function rank_autocomplete_doc_results(query, matches) {
2374 query = query || '';
2375 if (!matches || !matches.length)
2376 return;
2377
2378 var _resultScoreFn = function(match) {
2379 var score = 1.0;
2380
2381 // if the query matched a tag
2382 if (match.matched_tag > 0) {
2383 // multiply score by factor relative to position in tags list (max of 3)
2384 score *= 3 / match.matched_tag;
2385
2386 // if it also matched the title
2387 if (match.matched_title > 0) {
2388 score *= 2;
2389 }
2390 } else if (match.matched_title > 0) {
2391 score *= 3;
2392 }
2393
2394 return score;
2395 };
2396
2397 for (var i=0; i<matches.length; i++) {
2398 matches[i].__resultScore = _resultScoreFn(matches[i]);
2399 }
2400
2401 matches.sort(function(a,b){
2402 var n = b.__resultScore - a.__resultScore;
2403 if (n == 0) // lexicographical sort if scores are the same
2404 n = (a.label < b.label) ? -1 : 1;
2405 return n;
2406 });
2407}
2408
Scott Main7e447ed2013-02-19 17:22:37 -08002409/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002410function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002411 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002412 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002413 return;
2414
2415 // helper function that gets the last occurence index of the given regex
2416 // in the given string, or -1 if not found
2417 var _lastSearch = function(s, re) {
2418 if (s == '')
2419 return -1;
2420 var l = -1;
2421 var tmp;
2422 while ((tmp = s.search(re)) >= 0) {
2423 if (l < 0) l = 0;
2424 l += tmp;
2425 s = s.substr(tmp + 1);
2426 }
2427 return l;
2428 };
2429
2430 // helper function that counts the occurrences of a given character in
2431 // a given string
2432 var _countChar = function(s, c) {
2433 var n = 0;
2434 for (var i=0; i<s.length; i++)
2435 if (s.charAt(i) == c) ++n;
2436 return n;
2437 };
2438
2439 var queryLower = query.toLowerCase();
2440 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2441 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2442 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2443
2444 var _resultScoreFn = function(result) {
2445 // scores are calculated based on exact and prefix matches,
2446 // and then number of path separators (dots) from the last
2447 // match (i.e. favoring classes and deep package names)
2448 var score = 1.0;
2449 var labelLower = result.label.toLowerCase();
2450 var t;
2451 t = _lastSearch(labelLower, partExactAlnumRE);
2452 if (t >= 0) {
2453 // exact part match
2454 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2455 score *= 200 / (partsAfter + 1);
2456 } else {
2457 t = _lastSearch(labelLower, partPrefixAlnumRE);
2458 if (t >= 0) {
2459 // part prefix match
2460 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2461 score *= 20 / (partsAfter + 1);
2462 }
2463 }
2464
2465 return score;
2466 };
2467
Scott Main7e447ed2013-02-19 17:22:37 -08002468 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002469 // if the API is deprecated, default score is 0; otherwise, perform scoring
2470 if (matches[i].deprecated == "true") {
2471 matches[i].__resultScore = 0;
2472 } else {
2473 matches[i].__resultScore = _resultScoreFn(matches[i]);
2474 }
Scott Mainf5089842012-08-14 16:31:07 -07002475 }
2476
Scott Main7e447ed2013-02-19 17:22:37 -08002477 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002478 var n = b.__resultScore - a.__resultScore;
2479 if (n == 0) // lexicographical sort if scores are the same
2480 n = (a.label < b.label) ? -1 : 1;
2481 return n;
2482 });
2483}
2484
Scott Main7e447ed2013-02-19 17:22:37 -08002485/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002486function highlight_autocomplete_result_labels(query) {
2487 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002488 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002489 return;
2490
2491 var queryLower = query.toLowerCase();
2492 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2493 var queryRE = new RegExp(
2494 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2495 for (var i=0; i<gMatches.length; i++) {
2496 gMatches[i].__hilabel = gMatches[i].label.replace(
2497 queryRE, '<b>$1</b>');
2498 }
Scott Main7e447ed2013-02-19 17:22:37 -08002499 for (var i=0; i<gGoogleMatches.length; i++) {
2500 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2501 queryRE, '<b>$1</b>');
2502 }
Scott Mainf5089842012-08-14 16:31:07 -07002503}
2504
2505function search_focus_changed(obj, focused)
2506{
Scott Main3b90aff2013-08-01 18:09:35 -07002507 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002508 if(obj.value == ""){
Dirk Dougherty29e93432015-05-05 18:17:13 -07002509 $("#search-close").addClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07002510 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002511 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002512 }
2513}
2514
2515function submit_search() {
2516 var query = document.getElementById('search_autocomplete').value;
2517 location.hash = 'q=' + query;
2518 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002519 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002520 return false;
2521}
2522
2523
2524function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002525 $("#searchResults").slideUp('fast', setStickyTop);
Dirk Dougherty29e93432015-05-05 18:17:13 -07002526 $("#search-close").addClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07002527 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002528
Scott Mainf5089842012-08-14 16:31:07 -07002529 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002530
Scott Mainf5089842012-08-14 16:31:07 -07002531 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2532 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002533
2534 // forcefully regain key-up event control (previously jacked by search api)
2535 $("#search_autocomplete").keyup(function(event) {
2536 return search_changed(event, false, toRoot);
2537 });
2538
Scott Mainf5089842012-08-14 16:31:07 -07002539 return false;
2540}
2541
2542
2543
2544/* ########################################################## */
2545/* ################ CUSTOM SEARCH ENGINE ################## */
2546/* ########################################################## */
2547
Scott Mainf5089842012-08-14 16:31:07 -07002548var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002549google.load('search', '1', {"callback" : function() {
2550 searchControl = new google.search.SearchControl();
2551 } });
Scott Mainf5089842012-08-14 16:31:07 -07002552
2553function loadSearchResults() {
2554 document.getElementById("search_autocomplete").style.color = "#000";
2555
Scott Mainf5089842012-08-14 16:31:07 -07002556 searchControl = new google.search.SearchControl();
2557
2558 // use our existing search form and use tabs when multiple searchers are used
2559 drawOptions = new google.search.DrawOptions();
2560 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2561 drawOptions.setInput(document.getElementById("search_autocomplete"));
2562
2563 // configure search result options
2564 searchOptions = new google.search.SearcherOptions();
2565 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2566
2567 // configure each of the searchers, for each tab
2568 devSiteSearcher = new google.search.WebSearch();
2569 devSiteSearcher.setUserDefinedLabel("All");
2570 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2571
2572 designSearcher = new google.search.WebSearch();
2573 designSearcher.setUserDefinedLabel("Design");
2574 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2575
2576 trainingSearcher = new google.search.WebSearch();
2577 trainingSearcher.setUserDefinedLabel("Training");
2578 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2579
2580 guidesSearcher = new google.search.WebSearch();
2581 guidesSearcher.setUserDefinedLabel("Guides");
2582 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2583
2584 referenceSearcher = new google.search.WebSearch();
2585 referenceSearcher.setUserDefinedLabel("Reference");
2586 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2587
Scott Maindf08ada2012-12-03 08:54:37 -08002588 googleSearcher = new google.search.WebSearch();
2589 googleSearcher.setUserDefinedLabel("Google Services");
2590 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2591
Scott Mainf5089842012-08-14 16:31:07 -07002592 blogSearcher = new google.search.WebSearch();
2593 blogSearcher.setUserDefinedLabel("Blog");
2594 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2595
2596 // add each searcher to the search control
2597 searchControl.addSearcher(devSiteSearcher, searchOptions);
2598 searchControl.addSearcher(designSearcher, searchOptions);
2599 searchControl.addSearcher(trainingSearcher, searchOptions);
2600 searchControl.addSearcher(guidesSearcher, searchOptions);
2601 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002602 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002603 searchControl.addSearcher(blogSearcher, searchOptions);
2604
2605 // configure result options
2606 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2607 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2608 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2609 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2610
2611 // upon ajax search, refresh the url and search title
2612 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2613 updateResultTitle(query);
2614 var query = document.getElementById('search_autocomplete').value;
2615 location.hash = 'q=' + query;
2616 });
2617
Scott Mainde295272013-03-25 15:48:35 -07002618 // once search results load, set up click listeners
2619 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2620 addResultClickListeners();
2621 });
2622
Scott Mainf5089842012-08-14 16:31:07 -07002623 // draw the search results box
2624 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2625
2626 // get query and execute the search
2627 searchControl.execute(decodeURI(getQuery(location.hash)));
2628
2629 document.getElementById("search_autocomplete").focus();
2630 addTabListeners();
2631}
2632// End of loadSearchResults
2633
2634
2635google.setOnLoadCallback(function(){
2636 if (location.hash.indexOf("q=") == -1) {
2637 // if there's no query in the url, don't search and make sure results are hidden
2638 $('#searchResults').hide();
2639 return;
2640 } else {
2641 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002642 $('#searchResults').slideDown('slow', setStickyTop);
Dirk Dougherty29e93432015-05-05 18:17:13 -07002643 $("#search-close").removeClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07002644 loadSearchResults();
2645 }
2646}, true);
2647
smain@google.com9a818f52014-10-03 09:25:59 -07002648/* Adjust the scroll position to account for sticky header, only if the hash matches an id.
2649 This does not handle <a name=""> tags. Some CSS fixes those, but only for reference docs. */
Scott Mainb16376f2014-05-21 20:35:47 -07002650function offsetScrollForSticky() {
smain@google.com11fc0092014-10-16 22:10:00 -07002651 // Ignore if there's no search bar (some special pages have no header)
2652 if ($("#search-container").length < 1) return;
2653
smain@google.com3b77ab52014-06-17 11:57:27 -07002654 var hash = escape(location.hash.substr(1));
2655 var $matchingElement = $("#"+hash);
smain@google.com08f336ea2014-10-03 17:40:00 -07002656 // Sanity check that there's an element with that ID on the page
2657 if ($matchingElement.length) {
Scott Mainb16376f2014-05-21 20:35:47 -07002658 // If the position of the target element is near the top of the page (<20px, where we expect it
2659 // to be because we need to move it down 60px to become in view), then move it down 60px
2660 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2661 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002662 }
2663 }
2664}
2665
Scott Mainf5089842012-08-14 16:31:07 -07002666// when an event on the browser history occurs (back, forward, load) requery hash and do search
2667$(window).hashchange( function(){
smain@google.com2f077192014-10-09 18:04:10 -07002668 // Ignore if there's no search bar (some special pages have no header)
2669 if ($("#search-container").length < 1) return;
2670
Dirk Doughertyc3921652014-05-13 16:55:26 -07002671 // If the hash isn't a search query or there's an error in the query,
2672 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002673 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2674 // If the results pane is open, close it.
2675 if (!$("#searchResults").is(":hidden")) {
2676 hideResults();
2677 }
Scott Mainb16376f2014-05-21 20:35:47 -07002678 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002679 return;
2680 }
2681
2682 // Otherwise, we have a search to do
2683 var query = decodeURI(getQuery(location.hash));
2684 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002685 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002686 $("#search_autocomplete").focus();
Dirk Dougherty29e93432015-05-05 18:17:13 -07002687 $("#search-close").removeClass("hide");
Scott Mainf5089842012-08-14 16:31:07 -07002688
2689 updateResultTitle(query);
2690});
2691
2692function updateResultTitle(query) {
2693 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2694}
2695
2696// forcefully regain key-up event control (previously jacked by search api)
2697$("#search_autocomplete").keyup(function(event) {
2698 return search_changed(event, false, toRoot);
2699});
2700
2701// add event listeners to each tab so we can track the browser history
2702function addTabListeners() {
2703 var tabHeaders = $(".gsc-tabHeader");
2704 for (var i = 0; i < tabHeaders.length; i++) {
2705 $(tabHeaders[i]).attr("id",i).click(function() {
2706 /*
2707 // make a copy of the page numbers for the search left pane
2708 setTimeout(function() {
2709 // remove any residual page numbers
2710 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002711 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002712 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002713 // and because we're going to remove it (previous line),
2714 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002715 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2716 .clone().appendTo('#searchResults .gsc-tabsArea');
2717 }, 200);
2718 */
2719 });
2720 }
2721 setTimeout(function(){$(tabHeaders[0]).click()},200);
2722}
2723
Scott Mainde295272013-03-25 15:48:35 -07002724// add analytics tracking events to each result link
2725function addResultClickListeners() {
2726 $("#searchResults a.gs-title").each(function(index, link) {
2727 // When user clicks enter for Google search results, track it
2728 $(link).click(function() {
smain@google.comed677d72014-12-12 10:19:17 -08002729 ga('send', 'event', 'Google Click', 'clicked: ' + $(this).attr('href'),
2730 'query: ' + $("#search_autocomplete").val().toLowerCase());
Scott Mainde295272013-03-25 15:48:35 -07002731 });
2732 });
2733}
2734
Scott Mainf5089842012-08-14 16:31:07 -07002735
2736function getQuery(hash) {
2737 var queryParts = hash.split('=');
2738 return queryParts[1];
2739}
2740
2741/* returns the given string with all HTML brackets converted to entities
2742 TODO: move this to the site's JS library */
2743function escapeHTML(string) {
2744 return string.replace(/</g,"&lt;")
2745 .replace(/>/g,"&gt;");
2746}
2747
2748
2749
2750
2751
2752
2753
2754/* ######################################################## */
2755/* ################# JAVADOC REFERENCE ################### */
2756/* ######################################################## */
2757
Scott Main65511c02012-09-07 15:51:32 -07002758/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002759if (location.pathname.indexOf("/reference") == 0) {
2760 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2761 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2762 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002763 $(document).ready(function() {
2764 // init available apis based on user pref
2765 changeApiLevel();
2766 initSidenavHeightResize()
2767 });
2768 }
Scott Main65511c02012-09-07 15:51:32 -07002769}
Scott Mainf5089842012-08-14 16:31:07 -07002770
2771var API_LEVEL_COOKIE = "api_level";
2772var minLevel = 1;
2773var maxLevel = 1;
2774
2775/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002776
Scott Mainf5089842012-08-14 16:31:07 -07002777 function initSidenavHeightResize() {
2778 // Change the drag bar size to nicely fit the scrollbar positions
2779 var $dragBar = $(".ui-resizable-s");
2780 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002781
2782 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002783 containment: "#nav-panels",
2784 handles: "s",
2785 alsoResize: "#packages-nav",
2786 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2787 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2788 });
Scott Main3b90aff2013-08-01 18:09:35 -07002789
Scott Mainf5089842012-08-14 16:31:07 -07002790 }
Scott Main3b90aff2013-08-01 18:09:35 -07002791
Scott Mainf5089842012-08-14 16:31:07 -07002792function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002793 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002794 $('#devdoc-nav').css({
2795 'width' : $('#side-nav').css('width'),
2796 'margin' : $('#side-nav').css('margin')
2797 });
2798 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002799
Scott Mainf5089842012-08-14 16:31:07 -07002800 initSidenavHeightResize();
2801}
2802
2803function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002804 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002805 $('#devdoc-nav').css({
2806 'width' : $('#side-nav').css('width'),
2807 'margin' : $('#side-nav').css('margin')
2808 });
2809 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002810
Scott Mainf5089842012-08-14 16:31:07 -07002811 initSidenavHeightResize();
2812}
2813
2814function buildApiLevelSelector() {
2815 maxLevel = SINCE_DATA.length;
2816 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2817 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2818
2819 minLevel = parseInt($("#doc-api-level").attr("class"));
2820 // Handle provisional api levels; the provisional level will always be the highest possible level
2821 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2822 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2823 if (isNaN(minLevel) && minLevel.length) {
2824 minLevel = maxLevel;
2825 }
2826 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2827 for (var i = maxLevel-1; i >= 0; i--) {
2828 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2829 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2830 select.append(option);
2831 }
2832
2833 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2834 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2835 selectedLevelItem.setAttribute('selected',true);
2836}
2837
2838function changeApiLevel() {
2839 maxLevel = SINCE_DATA.length;
2840 var selectedLevel = maxLevel;
2841
2842 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2843 toggleVisisbleApis(selectedLevel, "body");
2844
smain@google.com6bdcb982014-11-14 11:53:07 -08002845 writeCookie(API_LEVEL_COOKIE, selectedLevel, null);
Scott Mainf5089842012-08-14 16:31:07 -07002846
2847 if (selectedLevel < minLevel) {
2848 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002849 $("#naMessage").show().html("<div><p><strong>This " + thing
2850 + " requires API level " + minLevel + " or higher.</strong></p>"
2851 + "<p>This document is hidden because your selected API level for the documentation is "
2852 + selectedLevel + ". You can change the documentation API level with the selector "
2853 + "above the left navigation.</p>"
2854 + "<p>For more information about specifying the API level your app requires, "
2855 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2856 + ">Supporting Different Platform Versions</a>.</p>"
2857 + "<input type='button' value='OK, make this page visible' "
2858 + "title='Change the API level to " + minLevel + "' "
2859 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2860 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002861 } else {
2862 $("#naMessage").hide();
2863 }
2864}
2865
2866function toggleVisisbleApis(selectedLevel, context) {
2867 var apis = $(".api",context);
2868 apis.each(function(i) {
2869 var obj = $(this);
2870 var className = obj.attr("class");
2871 var apiLevelIndex = className.lastIndexOf("-")+1;
2872 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2873 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2874 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2875 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2876 return;
2877 }
2878 apiLevel = parseInt(apiLevel);
2879
2880 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2881 var selectedLevelNum = parseInt(selectedLevel)
2882 var apiLevelNum = parseInt(apiLevel);
2883 if (isNaN(apiLevelNum)) {
2884 apiLevelNum = maxLevel;
2885 }
2886
2887 // Grey things out that aren't available and give a tooltip title
2888 if (apiLevelNum > selectedLevelNum) {
2889 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002890 + apiLevel + "\" or higher. To reveal, change the target API level "
2891 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002892 }
Scott Mainf5089842012-08-14 16:31:07 -07002893 else obj.removeClass("absent").removeAttr("title");
2894 });
2895}
2896
2897
2898
2899
2900/* ################# SIDENAV TREE VIEW ################### */
2901
2902function new_node(me, mom, text, link, children_data, api_level)
2903{
2904 var node = new Object();
2905 node.children = Array();
2906 node.children_data = children_data;
2907 node.depth = mom.depth + 1;
2908
2909 node.li = document.createElement("li");
2910 mom.get_children_ul().appendChild(node.li);
2911
2912 node.label_div = document.createElement("div");
2913 node.label_div.className = "label";
2914 if (api_level != null) {
2915 $(node.label_div).addClass("api");
2916 $(node.label_div).addClass("api-level-"+api_level);
2917 }
2918 node.li.appendChild(node.label_div);
2919
2920 if (children_data != null) {
2921 node.expand_toggle = document.createElement("a");
2922 node.expand_toggle.href = "javascript:void(0)";
2923 node.expand_toggle.onclick = function() {
2924 if (node.expanded) {
2925 $(node.get_children_ul()).slideUp("fast");
2926 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2927 node.expanded = false;
2928 } else {
2929 expand_node(me, node);
2930 }
2931 };
2932 node.label_div.appendChild(node.expand_toggle);
2933
2934 node.plus_img = document.createElement("img");
2935 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2936 node.plus_img.className = "plus";
2937 node.plus_img.width = "8";
2938 node.plus_img.border = "0";
2939 node.expand_toggle.appendChild(node.plus_img);
2940
2941 node.expanded = false;
2942 }
2943
2944 var a = document.createElement("a");
2945 node.label_div.appendChild(a);
2946 node.label = document.createTextNode(text);
2947 a.appendChild(node.label);
2948 if (link) {
2949 a.href = me.toroot + link;
2950 } else {
2951 if (children_data != null) {
2952 a.className = "nolink";
2953 a.href = "javascript:void(0)";
2954 a.onclick = node.expand_toggle.onclick;
2955 // This next line shouldn't be necessary. I'll buy a beer for the first
2956 // person who figures out how to remove this line and have the link
2957 // toggle shut on the first try. --joeo@android.com
2958 node.expanded = false;
2959 }
2960 }
Scott Main3b90aff2013-08-01 18:09:35 -07002961
Scott Mainf5089842012-08-14 16:31:07 -07002962
2963 node.children_ul = null;
2964 node.get_children_ul = function() {
2965 if (!node.children_ul) {
2966 node.children_ul = document.createElement("ul");
2967 node.children_ul.className = "children_ul";
2968 node.children_ul.style.display = "none";
2969 node.li.appendChild(node.children_ul);
2970 }
2971 return node.children_ul;
2972 };
2973
2974 return node;
2975}
2976
Robert Lyd2dd6e52012-11-29 21:28:48 -08002977
2978
2979
Scott Mainf5089842012-08-14 16:31:07 -07002980function expand_node(me, node)
2981{
2982 if (node.children_data && !node.expanded) {
2983 if (node.children_visited) {
2984 $(node.get_children_ul()).slideDown("fast");
2985 } else {
2986 get_node(me, node);
2987 if ($(node.label_div).hasClass("absent")) {
2988 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002989 }
Scott Mainf5089842012-08-14 16:31:07 -07002990 $(node.get_children_ul()).slideDown("fast");
2991 }
2992 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2993 node.expanded = true;
2994
2995 // perform api level toggling because new nodes are new to the DOM
2996 var selectedLevel = $("#apiLevelSelector option:selected").val();
2997 toggleVisisbleApis(selectedLevel, "#side-nav");
2998 }
2999}
3000
3001function get_node(me, mom)
3002{
3003 mom.children_visited = true;
3004 for (var i in mom.children_data) {
3005 var node_data = mom.children_data[i];
3006 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
3007 node_data[2], node_data[3]);
3008 }
3009}
3010
3011function this_page_relative(toroot)
3012{
3013 var full = document.location.pathname;
3014 var file = "";
3015 if (toroot.substr(0, 1) == "/") {
3016 if (full.substr(0, toroot.length) == toroot) {
3017 return full.substr(toroot.length);
3018 } else {
3019 // the file isn't under toroot. Fail.
3020 return null;
3021 }
3022 } else {
3023 if (toroot != "./") {
3024 toroot = "./" + toroot;
3025 }
3026 do {
3027 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
3028 var pos = full.lastIndexOf("/");
3029 file = full.substr(pos) + file;
3030 full = full.substr(0, pos);
3031 toroot = toroot.substr(0, toroot.length-3);
3032 }
3033 } while (toroot != "" && toroot != "/");
3034 return file.substr(1);
3035 }
3036}
3037
3038function find_page(url, data)
3039{
3040 var nodes = data;
3041 var result = null;
3042 for (var i in nodes) {
3043 var d = nodes[i];
3044 if (d[1] == url) {
3045 return new Array(i);
3046 }
3047 else if (d[2] != null) {
3048 result = find_page(url, d[2]);
3049 if (result != null) {
3050 return (new Array(i).concat(result));
3051 }
3052 }
3053 }
3054 return null;
3055}
3056
Scott Mainf5089842012-08-14 16:31:07 -07003057function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07003058 // load json file for navtree data
3059 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
3060 // when the file is loaded, initialize the tree
3061 if(jqxhr.status === 200) {
3062 init_navtree("tree-list", toroot, NAVTREE_DATA);
3063 }
3064 });
Scott Main3b90aff2013-08-01 18:09:35 -07003065
Scott Mainf5089842012-08-14 16:31:07 -07003066 // perform api level toggling because because the whole tree is new to the DOM
3067 var selectedLevel = $("#apiLevelSelector option:selected").val();
3068 toggleVisisbleApis(selectedLevel, "#side-nav");
3069}
3070
3071function init_navtree(navtree_id, toroot, root_nodes)
3072{
3073 var me = new Object();
3074 me.toroot = toroot;
3075 me.node = new Object();
3076
3077 me.node.li = document.getElementById(navtree_id);
3078 me.node.children_data = root_nodes;
3079 me.node.children = new Array();
3080 me.node.children_ul = document.createElement("ul");
3081 me.node.get_children_ul = function() { return me.node.children_ul; };
3082 //me.node.children_ul.className = "children_ul";
3083 me.node.li.appendChild(me.node.children_ul);
3084 me.node.depth = 0;
3085
3086 get_node(me, me.node);
3087
3088 me.this_page = this_page_relative(toroot);
3089 me.breadcrumbs = find_page(me.this_page, root_nodes);
3090 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
3091 var mom = me.node;
3092 for (var i in me.breadcrumbs) {
3093 var j = me.breadcrumbs[i];
3094 mom = mom.children[j];
3095 expand_node(me, mom);
3096 }
3097 mom.label_div.className = mom.label_div.className + " selected";
3098 addLoadEvent(function() {
3099 scrollIntoView("nav-tree");
3100 });
3101 }
3102}
3103
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003104
3105
3106
3107
3108
3109
3110
Robert Lyd2dd6e52012-11-29 21:28:48 -08003111/* TODO: eliminate redundancy with non-google functions */
3112function init_google_navtree(navtree_id, toroot, root_nodes)
3113{
3114 var me = new Object();
3115 me.toroot = toroot;
3116 me.node = new Object();
3117
3118 me.node.li = document.getElementById(navtree_id);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07003119 if (!me.node.li) {
3120 return;
3121 }
3122
Robert Lyd2dd6e52012-11-29 21:28:48 -08003123 me.node.children_data = root_nodes;
3124 me.node.children = new Array();
3125 me.node.children_ul = document.createElement("ul");
3126 me.node.get_children_ul = function() { return me.node.children_ul; };
3127 //me.node.children_ul.className = "children_ul";
3128 me.node.li.appendChild(me.node.children_ul);
3129 me.node.depth = 0;
3130
3131 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003132}
3133
3134function new_google_node(me, mom, text, link, children_data, api_level)
3135{
3136 var node = new Object();
3137 var child;
3138 node.children = Array();
3139 node.children_data = children_data;
3140 node.depth = mom.depth + 1;
3141 node.get_children_ul = function() {
3142 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003143 node.children_ul = document.createElement("ul");
3144 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003145 node.li.appendChild(node.children_ul);
3146 }
3147 return node.children_ul;
3148 };
3149 node.li = document.createElement("li");
3150
3151 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003152
3153
Robert Lyd2dd6e52012-11-29 21:28:48 -08003154 if(link) {
3155 child = document.createElement("a");
3156
3157 }
3158 else {
3159 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003160 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003161
3162 }
3163 if (children_data != null) {
3164 node.li.className="nav-section";
3165 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003166 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003167 node.li.appendChild(node.label_div);
3168 get_google_node(me, node);
3169 node.label_div.appendChild(child);
3170 }
3171 else {
3172 node.li.appendChild(child);
3173 }
3174 if(link) {
3175 child.href = me.toroot + link;
3176 }
3177 node.label = document.createTextNode(text);
3178 child.appendChild(node.label);
3179
3180 node.children_ul = null;
3181
3182 return node;
3183}
3184
3185function get_google_node(me, mom)
3186{
3187 mom.children_visited = true;
3188 var linkText;
3189 for (var i in mom.children_data) {
3190 var node_data = mom.children_data[i];
3191 linkText = node_data[0];
3192
3193 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3194 linkText = linkText.substr(19, linkText.length);
3195 }
3196 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3197 node_data[2], node_data[3]);
3198 }
3199}
Scott Mainad08f072013-08-20 16:49:57 -07003200
3201
3202
3203
3204
3205
3206/****** NEW version of script to build google and sample navs dynamically ******/
3207// TODO: update Google reference docs to tolerate this new implementation
3208
Scott Maine624b3f2013-09-12 12:56:41 -07003209var NODE_NAME = 0;
3210var NODE_HREF = 1;
3211var NODE_GROUP = 2;
3212var NODE_TAGS = 3;
3213var NODE_CHILDREN = 4;
3214
Scott Mainad08f072013-08-20 16:49:57 -07003215function init_google_navtree2(navtree_id, data)
3216{
3217 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003218 for (var i in data) {
3219 var node_data = data[i];
3220 $containerUl.append(new_google_node2(node_data));
3221 }
3222
Scott Main70557ee2013-10-30 14:47:40 -07003223 // Make all third-generation list items 'sticky' to prevent them from collapsing
3224 $containerUl.find('li li li.nav-section').addClass('sticky');
3225
Scott Mainad08f072013-08-20 16:49:57 -07003226 initExpandableNavItems("#"+navtree_id);
3227}
3228
3229function new_google_node2(node_data)
3230{
Scott Maine624b3f2013-09-12 12:56:41 -07003231 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003232 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3233 linkText = linkText.substr(19, linkText.length);
3234 }
3235 var $li = $('<li>');
3236 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003237 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003238 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3239 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003240 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003241 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3242 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003243 }
3244 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003245 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003246 $li.addClass("nav-section");
3247 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003248 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003249
Scott Maine624b3f2013-09-12 12:56:41 -07003250 for (var i in node_data[NODE_CHILDREN]) {
3251 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003252 $childUl.append(new_google_node2(child_node_data));
3253 }
3254 $li.append($childUl);
3255 }
3256 $li.prepend($a);
3257
3258 return $li;
3259}
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
Robert Lyd2dd6e52012-11-29 21:28:48 -08003271function showGoogleRefTree() {
3272 init_default_google_navtree(toRoot);
3273 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003274}
3275
3276function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003277 // load json file for navtree data
3278 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3279 // when the file is loaded, initialize the tree
3280 if(jqxhr.status === 200) {
3281 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3282 highlightSidenav();
3283 resizeNav();
3284 }
3285 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003286}
3287
3288function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003289 // load json file for navtree data
3290 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3291 // when the file is loaded, initialize the tree
3292 if(jqxhr.status === 200) {
3293 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3294 highlightSidenav();
3295 resizeNav();
3296 }
3297 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003298}
3299
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003300function showSamplesRefTree() {
3301 init_default_samples_navtree(toRoot);
3302}
3303
3304function init_default_samples_navtree(toroot) {
3305 // load json file for navtree data
3306 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3307 // when the file is loaded, initialize the tree
3308 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003309 // hack to remove the "about the samples" link then put it back in
3310 // after we nuke the list to remove the dummy static list of samples
3311 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3312 $("#nav.samples-nav").empty();
3313 $("#nav.samples-nav").append($firstLi);
3314
Scott Mainad08f072013-08-20 16:49:57 -07003315 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003316 highlightSidenav();
3317 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003318 if ($("#jd-content #samples").length) {
3319 showSamples();
3320 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003321 }
3322 });
3323}
3324
Scott Mainf5089842012-08-14 16:31:07 -07003325/* TOGGLE INHERITED MEMBERS */
3326
3327/* Toggle an inherited class (arrow toggle)
3328 * @param linkObj The link that was clicked.
3329 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3330 * 'null' to simply toggle.
3331 */
3332function toggleInherited(linkObj, expand) {
3333 var base = linkObj.getAttribute("id");
3334 var list = document.getElementById(base + "-list");
3335 var summary = document.getElementById(base + "-summary");
3336 var trigger = document.getElementById(base + "-trigger");
3337 var a = $(linkObj);
3338 if ( (expand == null && a.hasClass("closed")) || expand ) {
3339 list.style.display = "none";
3340 summary.style.display = "block";
3341 trigger.src = toRoot + "assets/images/triangle-opened.png";
3342 a.removeClass("closed");
3343 a.addClass("opened");
3344 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3345 list.style.display = "block";
3346 summary.style.display = "none";
3347 trigger.src = toRoot + "assets/images/triangle-closed.png";
3348 a.removeClass("opened");
3349 a.addClass("closed");
3350 }
3351 return false;
3352}
3353
3354/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3355 * @param linkObj The link that was clicked.
3356 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3357 * 'null' to simply toggle.
3358 */
3359function toggleAllInherited(linkObj, expand) {
3360 var a = $(linkObj);
3361 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3362 var expandos = $(".jd-expando-trigger", table);
3363 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3364 expandos.each(function(i) {
3365 toggleInherited(this, true);
3366 });
3367 a.text("[Collapse]");
3368 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3369 expandos.each(function(i) {
3370 toggleInherited(this, false);
3371 });
3372 a.text("[Expand]");
3373 }
3374 return false;
3375}
3376
3377/* Toggle all inherited members in the class (link in the class title)
3378 */
3379function toggleAllClassInherited() {
3380 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3381 var toggles = $(".toggle-all", $("#body-content"));
3382 if (a.text() == "[Expand All]") {
3383 toggles.each(function(i) {
3384 toggleAllInherited(this, true);
3385 });
3386 a.text("[Collapse All]");
3387 } else {
3388 toggles.each(function(i) {
3389 toggleAllInherited(this, false);
3390 });
3391 a.text("[Expand All]");
3392 }
3393 return false;
3394}
3395
3396/* Expand all inherited members in the class. Used when initiating page search */
3397function ensureAllInheritedExpanded() {
3398 var toggles = $(".toggle-all", $("#body-content"));
3399 toggles.each(function(i) {
3400 toggleAllInherited(this, true);
3401 });
3402 $("#toggleAllClassInherited").text("[Collapse All]");
3403}
3404
3405
3406/* HANDLE KEY EVENTS
3407 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3408 */
3409var agent = navigator['userAgent'].toLowerCase();
3410var mac = agent.indexOf("macintosh") != -1;
3411
3412$(document).keydown( function(e) {
3413var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3414 if (control && e.which == 70) { // 70 is "F"
3415 ensureAllInheritedExpanded();
3416 }
3417});
Scott Main498d7102013-08-21 15:47:38 -07003418
3419
3420
3421
3422
3423
3424/* On-demand functions */
3425
3426/** Move sample code line numbers out of PRE block and into non-copyable column */
3427function initCodeLineNumbers() {
3428 var numbers = $("#codesample-block a.number");
3429 if (numbers.length) {
3430 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3431 }
3432
3433 $(document).ready(function() {
3434 // select entire line when clicked
3435 $("span.code-line").click(function() {
3436 if (!shifted) {
3437 selectText(this);
3438 }
3439 });
3440 // invoke line link on double click
3441 $(".code-line").dblclick(function() {
3442 document.location.hash = $(this).attr('id');
3443 });
3444 // highlight the line when hovering on the number
3445 $("#codesample-line-numbers a.number").mouseover(function() {
3446 var id = $(this).attr('href');
3447 $(id).css('background','#e7e7e7');
3448 });
3449 $("#codesample-line-numbers a.number").mouseout(function() {
3450 var id = $(this).attr('href');
3451 $(id).css('background','none');
3452 });
3453 });
3454}
3455
3456// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3457var shifted = false;
3458$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3459
3460// courtesy of jasonedelman.com
3461function selectText(element) {
3462 var doc = document
3463 , range, selection
3464 ;
3465 if (doc.body.createTextRange) { //ms
3466 range = doc.body.createTextRange();
3467 range.moveToElementText(element);
3468 range.select();
3469 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003470 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003471 range = doc.createRange();
3472 range.selectNodeContents(element);
3473 selection.removeAllRanges();
3474 selection.addRange(range);
3475 }
Scott Main285f0772013-08-22 23:22:09 +00003476}
Scott Main03aca9a2013-10-31 07:20:55 -07003477
3478
3479
3480
3481/** Display links and other information about samples that match the
3482 group specified by the URL */
3483function showSamples() {
3484 var group = $("#samples").attr('class');
3485 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3486
3487 var $ul = $("<ul>");
3488 $selectedLi = $("#nav li.selected");
3489
3490 $selectedLi.children("ul").children("li").each(function() {
3491 var $li = $("<li>").append($(this).find("a").first().clone());
3492 $ul.append($li);
3493 });
3494
3495 $("#samples").append($ul);
3496
3497}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003498
3499
3500
3501/* ########################################################## */
3502/* ################### RESOURCE CARDS ##################### */
3503/* ########################################################## */
3504
3505/** Handle resource queries, collections, and grids (sections). Requires
3506 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3507
3508(function() {
3509 // Prevent the same resource from being loaded more than once per page.
3510 var addedPageResources = {};
3511
3512 $(document).ready(function() {
Dirk Dougherty29e93432015-05-05 18:17:13 -07003513 // Need to initialize hero carousel before other sections for dedupe
3514 // to work correctly.
3515 $('[data-carousel-query]').dacCarouselQuery();
3516
Dirk Doughertyc3921652014-05-13 16:55:26 -07003517 $('.resource-widget').each(function() {
3518 initResourceWidget(this);
3519 });
3520
3521 /* Pass the line height to ellipsisfade() to adjust the height of the
3522 text container to show the max number of lines possible, without
3523 showing lines that are cut off. This works with the css ellipsis
3524 classes to fade last text line and apply an ellipsis char. */
3525
Dirk Dougherty29e93432015-05-05 18:17:13 -07003526 //card text currently uses 20px line height.
3527 var lineHeight = 20;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003528 $('.card-info .text').ellipsisfade(lineHeight);
3529 });
3530
3531 /*
3532 Three types of resource layouts:
3533 Flow - Uses a fixed row-height flow using float left style.
3534 Carousel - Single card slideshow all same dimension absolute.
3535 Stack - Uses fixed columns and flexible element height.
3536 */
3537 function initResourceWidget(widget) {
3538 var $widget = $(widget);
3539 var isFlow = $widget.hasClass('resource-flow-layout'),
3540 isCarousel = $widget.hasClass('resource-carousel-layout'),
3541 isStack = $widget.hasClass('resource-stack-layout');
3542
Dirk Dougherty29e93432015-05-05 18:17:13 -07003543 // remove illegal col-x class which is not relevant anymore thanks to responsive styles.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003544 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
Dirk Dougherty29e93432015-05-05 18:17:13 -07003545 if (m && !$widget.is('.cols > *')) {
3546 $widget.removeClass('col-' + m[1]);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003547 }
3548
3549 var opts = {
3550 cardSizes: ($widget.data('cardsizes') || '').split(','),
3551 maxResults: parseInt($widget.data('maxresults') || '100', 10),
Dirk Doughertycbe032f2015-05-22 11:41:40 -07003552 initialResults: $widget.data('initialResults'),
Dirk Doughertyc3921652014-05-13 16:55:26 -07003553 itemsPerPage: $widget.data('itemsperpage'),
3554 sortOrder: $widget.data('sortorder'),
3555 query: $widget.data('query'),
3556 section: $widget.data('section'),
Robert Lye7eeb402014-06-03 19:35:24 -07003557 /* Added by LFL 6/6/14 */
3558 resourceStyle: $widget.data('resourcestyle') || 'card',
3559 stackSort: $widget.data('stacksort') || 'true'
Dirk Doughertyc3921652014-05-13 16:55:26 -07003560 };
3561
3562 // run the search for the set of resources to show
3563
3564 var resources = buildResourceList(opts);
3565
3566 if (isFlow) {
3567 drawResourcesFlowWidget($widget, opts, resources);
3568 } else if (isCarousel) {
3569 drawResourcesCarouselWidget($widget, opts, resources);
3570 } else if (isStack) {
smain@google.com95948b82014-06-16 19:24:25 -07003571 /* Looks like this got removed and is not used, so repurposing for the
3572 homepage style layout.
Robert Lye7eeb402014-06-03 19:35:24 -07003573 Modified by LFL 6/6/14
3574 */
3575 //var sections = buildSectionList(opts);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003576 opts['numStacks'] = $widget.data('numstacks');
Robert Lye7eeb402014-06-03 19:35:24 -07003577 drawResourcesStackWidget($widget, opts, resources/*, sections*/);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003578 }
3579 }
3580
3581 /* Initializes a Resource Carousel Widget */
3582 function drawResourcesCarouselWidget($widget, opts, resources) {
3583 $widget.empty();
Dirk Dougherty29e93432015-05-05 18:17:13 -07003584 var plusone = false; // stop showing plusone buttons on cards
Dirk Doughertyc3921652014-05-13 16:55:26 -07003585
3586 $widget.addClass('resource-card slideshow-container')
3587 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3588 .append($('<a>').addClass('slideshow-next').text('Next'));
3589
3590 var css = { 'width': $widget.width() + 'px',
3591 'height': $widget.height() + 'px' };
3592
3593 var $ul = $('<ul>');
3594
3595 for (var i = 0; i < resources.length; ++i) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003596 var $card = $('<a>')
Robert Lye7eeb402014-06-03 19:35:24 -07003597 .attr('href', cleanUrl(resources[i].url))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003598 .decorateResourceCard(resources[i],plusone);
3599
3600 $('<li>').css(css)
3601 .append($card)
3602 .appendTo($ul);
3603 }
3604
3605 $('<div>').addClass('frame')
3606 .append($ul)
3607 .appendTo($widget);
3608
3609 $widget.dacSlideshow({
3610 auto: true,
3611 btnPrev: '.slideshow-prev',
3612 btnNext: '.slideshow-next'
3613 });
3614 };
3615
Robert Lye7eeb402014-06-03 19:35:24 -07003616 /* Initializes a Resource Card Stack Widget (column-based layout)
3617 Modified by LFL 6/6/14
3618 */
Dirk Doughertyc3921652014-05-13 16:55:26 -07003619 function drawResourcesStackWidget($widget, opts, resources, sections) {
3620 // Don't empty widget, grab all items inside since they will be the first
3621 // items stacked, followed by the resource query
Dirk Dougherty29e93432015-05-05 18:17:13 -07003622 var plusone = false; // stop showing plusone buttons on cards
Dirk Doughertyc3921652014-05-13 16:55:26 -07003623 var cards = $widget.find('.resource-card').detach().toArray();
3624 var numStacks = opts.numStacks || 1;
3625 var $stacks = [];
3626 var urlString;
3627
3628 for (var i = 0; i < numStacks; ++i) {
3629 $stacks[i] = $('<div>').addClass('resource-card-stack')
3630 .appendTo($widget);
3631 }
3632
3633 var sectionResources = [];
3634
3635 // Extract any subsections that are actually resource cards
Robert Lye7eeb402014-06-03 19:35:24 -07003636 if (sections) {
3637 for (var i = 0; i < sections.length; ++i) {
3638 if (!sections[i].sections || !sections[i].sections.length) {
3639 // Render it as a resource card
3640 sectionResources.push(
3641 $('<a>')
3642 .addClass('resource-card section-card')
3643 .attr('href', cleanUrl(sections[i].resource.url))
3644 .decorateResourceCard(sections[i].resource,plusone)[0]
3645 );
Dirk Doughertyc3921652014-05-13 16:55:26 -07003646
Robert Lye7eeb402014-06-03 19:35:24 -07003647 } else {
3648 cards.push(
3649 $('<div>')
3650 .addClass('resource-card section-card-menu')
3651 .decorateResourceSection(sections[i],plusone)[0]
3652 );
3653 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003654 }
3655 }
3656
3657 cards = cards.concat(sectionResources);
3658
3659 for (var i = 0; i < resources.length; ++i) {
Robert Lye7eeb402014-06-03 19:35:24 -07003660 var $card = createResourceElement(resources[i], opts);
smain@google.com95948b82014-06-16 19:24:25 -07003661
Robert Lye7eeb402014-06-03 19:35:24 -07003662 if (opts.resourceStyle.indexOf('related') > -1) {
3663 $card.addClass('related-card');
3664 }
smain@google.com95948b82014-06-16 19:24:25 -07003665
Dirk Doughertyc3921652014-05-13 16:55:26 -07003666 cards.push($card[0]);
3667 }
3668
Robert Lye7eeb402014-06-03 19:35:24 -07003669 if (opts.stackSort != 'false') {
3670 for (var i = 0; i < cards.length; ++i) {
3671 // Find the stack with the shortest height, but give preference to
3672 // left to right order.
3673 var minHeight = $stacks[0].height();
3674 var minIndex = 0;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003675
Robert Lye7eeb402014-06-03 19:35:24 -07003676 for (var j = 1; j < numStacks; ++j) {
3677 var height = $stacks[j].height();
3678 if (height < minHeight - 45) {
3679 minHeight = height;
3680 minIndex = j;
3681 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003682 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003683
Robert Lye7eeb402014-06-03 19:35:24 -07003684 $stacks[minIndex].append($(cards[i]));
3685 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003686 }
3687
3688 };
smain@google.com95948b82014-06-16 19:24:25 -07003689
3690 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003691 Create a resource card using the given resource object and a list of html
3692 configured options. Returns a jquery object containing the element.
3693 */
smain@google.com95948b82014-06-16 19:24:25 -07003694 function createResourceElement(resource, opts, plusone) {
Robert Lye7eeb402014-06-03 19:35:24 -07003695 var $el;
smain@google.com95948b82014-06-16 19:24:25 -07003696
Robert Lye7eeb402014-06-03 19:35:24 -07003697 // The difference here is that generic cards are not entirely clickable
3698 // so its a div instead of an a tag, also the generic one is not given
3699 // the resource-card class so it appears with a transparent background
3700 // and can be styled in whatever way the css setup.
3701 if (opts.resourceStyle == 'generic') {
3702 $el = $('<div>')
3703 .addClass('resource')
3704 .attr('href', cleanUrl(resource.url))
3705 .decorateResource(resource, opts);
3706 } else {
3707 var cls = 'resource resource-card';
smain@google.com95948b82014-06-16 19:24:25 -07003708
Robert Lye7eeb402014-06-03 19:35:24 -07003709 $el = $('<a>')
3710 .addClass(cls)
3711 .attr('href', cleanUrl(resource.url))
3712 .decorateResourceCard(resource, plusone);
3713 }
smain@google.com95948b82014-06-16 19:24:25 -07003714
Robert Lye7eeb402014-06-03 19:35:24 -07003715 return $el;
3716 }
Quddus Chong2cb2f682015-09-04 14:45:46 -07003717
Dirk Dougherty29e93432015-05-05 18:17:13 -07003718 function createResponsiveFlowColumn(cardSize) {
3719 var cardWidth = parseInt(cardSize.match(/(\d+)/)[1], 10);
3720 var column = $('<div>').addClass('col-' + (cardWidth / 3) + 'of6');
3721 if (cardWidth < 9) {
3722 column.addClass('col-tablet-1of2');
3723 } else if (cardWidth > 9 && cardWidth < 18) {
3724 column.addClass('col-tablet-1of1');
3725 }
3726 if (cardWidth < 18) {
3727 column.addClass('col-mobile-1of1')
3728 }
3729 return column;
3730 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003731
3732 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3733 function drawResourcesFlowWidget($widget, opts, resources) {
Dirk Dougherty29e93432015-05-05 18:17:13 -07003734 $widget.empty().addClass('cols');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003735 var cardSizes = opts.cardSizes || ['6x6'];
Dirk Doughertycbe032f2015-05-22 11:41:40 -07003736 var initialResults = opts.initialResults || resources.length;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003737 var i = 0, j = 0;
Dirk Dougherty29e93432015-05-05 18:17:13 -07003738 var plusone = false; // stop showing plusone buttons on cards
Dirk Doughertycbe032f2015-05-22 11:41:40 -07003739 var cardParent = $widget;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003740
3741 while (i < resources.length) {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07003742
3743 if (i === initialResults && initialResults < resources.length) {
3744 // Toggle remaining cards
3745 cardParent = $('<div class="dac-toggle-content clearfix">').appendTo($widget);
3746 $widget.addClass('dac-toggle');
3747 $('<div class="col-1of1 dac-section-links dac-text-center">')
3748 .append(
3749 $('<div class="dac-section-link" data-toggle="section">')
3750 .append('<span class="dac-toggle-expand">More<i class="dac-sprite dac-auto-unfold-more"></i></span>')
3751 .append('<span class="dac-toggle-collapse">Less<i class="dac-sprite dac-auto-unfold-less"></i></span>')
3752 )
3753 .appendTo($widget)
3754 }
3755
Dirk Doughertyc3921652014-05-13 16:55:26 -07003756 var cardSize = cardSizes[j++ % cardSizes.length];
3757 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Quddus Chong2cb2f682015-09-04 14:45:46 -07003758
Dirk Doughertycbe032f2015-05-22 11:41:40 -07003759 var column = createResponsiveFlowColumn(cardSize).appendTo(cardParent);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003760
3761 // A stack has a third dimension which is the number of stacked items
3762 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3763 var stackCount = 0;
3764 var $stackDiv = null;
3765
3766 if (isStack) {
3767 // Create a stack container which should have the dimensions defined
3768 // by the product of the items inside.
3769 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
Dirk Dougherty29e93432015-05-05 18:17:13 -07003770 + 'x' + isStack[2] * isStack[3]) .appendTo(column);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003771 }
3772
3773 // Build each stack item or just a single item
3774 do {
3775 var resource = resources[i];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003776
Robert Lye7eeb402014-06-03 19:35:24 -07003777 var $card = createResourceElement(resources[i], opts, plusone);
smain@google.com95948b82014-06-16 19:24:25 -07003778
3779 $card.addClass('resource-card-' + cardSize +
Robert Lye7eeb402014-06-03 19:35:24 -07003780 ' resource-card-' + resource.type);
smain@google.com95948b82014-06-16 19:24:25 -07003781
Dirk Doughertyc3921652014-05-13 16:55:26 -07003782 if (isStack) {
3783 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3784 if (++stackCount == parseInt(isStack[3])) {
3785 $card.addClass('resource-card-row-stack-last');
3786 stackCount = 0;
3787 }
3788 } else {
3789 stackCount = 0;
3790 }
3791
Dirk Dougherty29e93432015-05-05 18:17:13 -07003792 $card.appendTo($stackDiv || column);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003793
3794 } while (++i < resources.length && stackCount > 0);
3795 }
3796 }
3797
3798 /* Build a site map of resources using a section as a root. */
3799 function buildSectionList(opts) {
3800 if (opts.section && SECTION_BY_ID[opts.section]) {
3801 return SECTION_BY_ID[opts.section].sections || [];
3802 }
3803 return [];
3804 }
3805
3806 function buildResourceList(opts) {
Dirk Dougherty29e93432015-05-05 18:17:13 -07003807 return $.queryResources(opts);
3808 }
3809
3810 $.queryResources = function(opts) {
Dirk Doughertyc3921652014-05-13 16:55:26 -07003811 var maxResults = opts.maxResults || 100;
3812
3813 var query = opts.query || '';
3814 var expressions = parseResourceQuery(query);
3815 var addedResourceIndices = {};
3816 var results = [];
3817
3818 for (var i = 0; i < expressions.length; i++) {
3819 var clauses = expressions[i];
3820
3821 // build initial set of resources from first clause
3822 var firstClause = clauses[0];
3823 var resources = [];
3824 switch (firstClause.attr) {
3825 case 'type':
3826 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3827 break;
3828 case 'lang':
3829 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3830 break;
3831 case 'tag':
3832 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3833 break;
3834 case 'collection':
3835 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3836 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3837 break;
3838 case 'section':
3839 var urls = SITE_MAP[firstClause.value].sections || [];
3840 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3841 break;
3842 }
3843 // console.log(firstClause.attr + ':' + firstClause.value);
3844 resources = resources || [];
3845
3846 // use additional clauses to filter corpus
3847 if (clauses.length > 1) {
3848 var otherClauses = clauses.slice(1);
3849 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3850 }
3851
3852 // filter out resources already added
3853 if (i > 1) {
3854 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3855 }
3856
3857 // add to list of already added indices
3858 for (var j = 0; j < resources.length; j++) {
Dirk Dougherty29e93432015-05-05 18:17:13 -07003859 if (resources[j]) {
3860 addedResourceIndices[resources[j].index] = 1;
3861 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003862 }
3863
3864 // concat to final results list
3865 results = results.concat(resources);
3866 }
3867
3868 if (opts.sortOrder && results.length) {
3869 var attr = opts.sortOrder;
3870
3871 if (opts.sortOrder == 'random') {
3872 var i = results.length, j, temp;
3873 while (--i) {
3874 j = Math.floor(Math.random() * (i + 1));
3875 temp = results[i];
3876 results[i] = results[j];
3877 results[j] = temp;
3878 }
3879 } else {
3880 var desc = attr.charAt(0) == '-';
3881 if (desc) {
3882 attr = attr.substring(1);
3883 }
3884 results = results.sort(function(x,y) {
3885 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3886 });
3887 }
3888 }
3889
3890 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3891 results = results.slice(0, maxResults);
3892
3893 for (var j = 0; j < results.length; ++j) {
3894 addedPageResources[results[j].index] = 1;
3895 }
3896
3897 return results;
3898 }
3899
3900
3901 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3902 return function(resource) {
Dirk Dougherty29e93432015-05-05 18:17:13 -07003903 return resource && !addedResourceIndices[resource.index];
Dirk Doughertyc3921652014-05-13 16:55:26 -07003904 };
3905 }
3906
3907
3908 function getResourceMatchesClausesFilter(clauses) {
3909 return function(resource) {
3910 return doesResourceMatchClauses(resource, clauses);
3911 };
3912 }
3913
3914
3915 function doesResourceMatchClauses(resource, clauses) {
3916 for (var i = 0; i < clauses.length; i++) {
3917 var map;
3918 switch (clauses[i].attr) {
3919 case 'type':
3920 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3921 break;
3922 case 'lang':
3923 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3924 break;
3925 case 'tag':
3926 map = IS_RESOURCE_TAGGED[clauses[i].value];
3927 break;
3928 }
3929
3930 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3931 return clauses[i].negative;
3932 }
3933 }
3934 return true;
3935 }
smain@google.com95948b82014-06-16 19:24:25 -07003936
Robert Lye7eeb402014-06-03 19:35:24 -07003937 function cleanUrl(url)
3938 {
3939 if (url && url.indexOf('//') === -1) {
3940 url = toRoot + url;
3941 }
smain@google.com95948b82014-06-16 19:24:25 -07003942
Robert Lye7eeb402014-06-03 19:35:24 -07003943 return url;
3944 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07003945
3946
3947 function parseResourceQuery(query) {
3948 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3949 var expressions = [];
3950 var expressionStrs = query.split(',') || [];
3951 for (var i = 0; i < expressionStrs.length; i++) {
3952 var expr = expressionStrs[i] || '';
3953
3954 // Break expression into clauses (clause e.g. 'tag:foo')
3955 var clauses = [];
3956 var clauseStrs = expr.split(/(?=[\+\-])/);
3957 for (var j = 0; j < clauseStrs.length; j++) {
3958 var clauseStr = clauseStrs[j] || '';
3959
3960 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3961 var parts = clauseStr.split(':');
3962 var clause = {};
3963
3964 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3965 if (clause.attr) {
3966 if (clause.attr.charAt(0) == '+') {
3967 clause.attr = clause.attr.substring(1);
3968 } else if (clause.attr.charAt(0) == '-') {
3969 clause.negative = true;
3970 clause.attr = clause.attr.substring(1);
3971 }
3972 }
3973
3974 if (parts.length > 1) {
3975 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3976 }
3977
3978 clauses.push(clause);
3979 }
3980
3981 if (!clauses.length) {
3982 continue;
3983 }
3984
3985 expressions.push(clauses);
3986 }
3987
3988 return expressions;
3989 }
3990})();
3991
3992(function($) {
Robert Lye7eeb402014-06-03 19:35:24 -07003993
smain@google.com95948b82014-06-16 19:24:25 -07003994 /*
Robert Lye7eeb402014-06-03 19:35:24 -07003995 Utility method for creating dom for the description area of a card.
3996 Used in decorateResourceCard and decorateResource.
3997 */
3998 function buildResourceCardDescription(resource, plusone) {
3999 var $description = $('<div>').addClass('description ellipsis');
smain@google.com95948b82014-06-16 19:24:25 -07004000
Robert Lye7eeb402014-06-03 19:35:24 -07004001 $description.append($('<div>').addClass('text').html(resource.summary));
smain@google.com95948b82014-06-16 19:24:25 -07004002
Robert Lye7eeb402014-06-03 19:35:24 -07004003 if (resource.cta) {
4004 $description.append($('<a>').addClass('cta').html(resource.cta));
4005 }
smain@google.com95948b82014-06-16 19:24:25 -07004006
Robert Lye7eeb402014-06-03 19:35:24 -07004007 if (plusone) {
smain@google.com95948b82014-06-16 19:24:25 -07004008 var plusurl = resource.url.indexOf("//") > -1 ? resource.url :
Robert Lye7eeb402014-06-03 19:35:24 -07004009 "//developer.android.com/" + resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07004010
Robert Lye7eeb402014-06-03 19:35:24 -07004011 $description.append($('<div>').addClass('util')
4012 .append($('<div>').addClass('g-plusone')
4013 .attr('data-size', 'small')
4014 .attr('data-align', 'right')
4015 .attr('data-href', plusurl)));
4016 }
smain@google.com95948b82014-06-16 19:24:25 -07004017
Robert Lye7eeb402014-06-03 19:35:24 -07004018 return $description;
4019 }
smain@google.com95948b82014-06-16 19:24:25 -07004020
4021
Dirk Doughertyc3921652014-05-13 16:55:26 -07004022 /* Simple jquery function to create dom for a standard resource card */
4023 $.fn.decorateResourceCard = function(resource,plusone) {
4024 var section = resource.group || resource.type;
smain@google.com95948b82014-06-16 19:24:25 -07004025 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004026 'assets/images/resource-card-default-android.jpg';
smain@google.com95948b82014-06-16 19:24:25 -07004027
Robert Lye7eeb402014-06-03 19:35:24 -07004028 if (imgUrl.indexOf('//') === -1) {
4029 imgUrl = toRoot + imgUrl;
Dirk Doughertyc3921652014-05-13 16:55:26 -07004030 }
Robert Lye7eeb402014-06-03 19:35:24 -07004031
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004032 if (resource.type === 'youtube') {
4033 $('<div>').addClass('play-button')
4034 .append($('<i class="dac-sprite dac-play-white">'))
4035 .appendTo(this);
4036 }
4037
Robert Lye7eeb402014-06-03 19:35:24 -07004038 $('<div>').addClass('card-bg')
smain@google.com95948b82014-06-16 19:24:25 -07004039 .css('background-image', 'url(' + (imgUrl || toRoot +
Robert Lye7eeb402014-06-03 19:35:24 -07004040 'assets/images/resource-card-default-android.jpg') + ')')
Dirk Doughertyc3921652014-05-13 16:55:26 -07004041 .appendTo(this);
smain@google.com95948b82014-06-16 19:24:25 -07004042
Robert Lye7eeb402014-06-03 19:35:24 -07004043 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
4044 .append($('<div>').addClass('section').text(section))
4045 .append($('<div>').addClass('title').html(resource.title))
4046 .append(buildResourceCardDescription(resource, plusone))
4047 .appendTo(this);
Dirk Doughertyc3921652014-05-13 16:55:26 -07004048
4049 return this;
4050 };
4051
4052 /* Simple jquery function to create dom for a resource section card (menu) */
4053 $.fn.decorateResourceSection = function(section,plusone) {
4054 var resource = section.resource;
4055 //keep url clean for matching and offline mode handling
4056 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
4057 var $base = $('<a>')
4058 .addClass('card-bg')
4059 .attr('href', resource.url)
4060 .append($('<div>').addClass('card-section-icon')
4061 .append($('<div>').addClass('icon'))
4062 .append($('<div>').addClass('section').html(resource.title)))
4063 .appendTo(this);
4064
4065 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
4066
4067 if (section.sections && section.sections.length) {
4068 // Recurse the section sub-tree to find a resource image.
4069 var stack = [section];
4070
4071 while (stack.length) {
4072 if (stack[0].resource.image) {
4073 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
4074 break;
4075 }
4076
4077 if (stack[0].sections) {
4078 stack = stack.concat(stack[0].sections);
4079 }
4080
4081 stack.shift();
4082 }
4083
4084 var $ul = $('<ul>')
4085 .appendTo($cardInfo);
4086
4087 var max = section.sections.length > 3 ? 3 : section.sections.length;
4088
4089 for (var i = 0; i < max; ++i) {
4090
4091 var subResource = section.sections[i];
4092 if (!plusone) {
4093 $('<li>')
4094 .append($('<a>').attr('href', subResource.url)
4095 .append($('<div>').addClass('title').html(subResource.title))
4096 .append($('<div>').addClass('description ellipsis')
4097 .append($('<div>').addClass('text').html(subResource.summary))
4098 .append($('<div>').addClass('util'))))
4099 .appendTo($ul);
4100 } else {
4101 $('<li>')
4102 .append($('<a>').attr('href', subResource.url)
4103 .append($('<div>').addClass('title').html(subResource.title))
4104 .append($('<div>').addClass('description ellipsis')
4105 .append($('<div>').addClass('text').html(subResource.summary))
4106 .append($('<div>').addClass('util')
4107 .append($('<div>').addClass('g-plusone')
4108 .attr('data-size', 'small')
4109 .attr('data-align', 'right')
4110 .attr('data-href', resource.url)))))
4111 .appendTo($ul);
4112 }
4113 }
4114
4115 // Add a more row
4116 if (max < section.sections.length) {
4117 $('<li>')
4118 .append($('<a>').attr('href', resource.url)
4119 .append($('<div>')
4120 .addClass('title')
4121 .text('More')))
4122 .appendTo($ul);
4123 }
4124 } else {
4125 // No sub-resources, just render description?
4126 }
4127
4128 return this;
4129 };
smain@google.com95948b82014-06-16 19:24:25 -07004130
4131
4132
4133
Robert Lye7eeb402014-06-03 19:35:24 -07004134 /* Render other types of resource styles that are not cards. */
4135 $.fn.decorateResource = function(resource, opts) {
smain@google.com95948b82014-06-16 19:24:25 -07004136 var imgUrl = resource.image ||
Robert Lye7eeb402014-06-03 19:35:24 -07004137 'assets/images/resource-card-default-android.jpg';
4138 var linkUrl = resource.url;
smain@google.com95948b82014-06-16 19:24:25 -07004139
Robert Lye7eeb402014-06-03 19:35:24 -07004140 if (imgUrl.indexOf('//') === -1) {
4141 imgUrl = toRoot + imgUrl;
4142 }
smain@google.com95948b82014-06-16 19:24:25 -07004143
Robert Lye7eeb402014-06-03 19:35:24 -07004144 if (linkUrl && linkUrl.indexOf('//') === -1) {
4145 linkUrl = toRoot + linkUrl;
4146 }
4147
4148 $(this).append(
4149 $('<div>').addClass('image')
4150 .css('background-image', 'url(' + imgUrl + ')'),
4151 $('<div>').addClass('info').append(
4152 $('<h4>').addClass('title').html(resource.title),
4153 $('<p>').addClass('summary').html(resource.summary),
4154 $('<a>').attr('href', linkUrl).addClass('cta').html('Learn More')
4155 )
4156 );
4157
4158 return this;
4159 };
Dirk Doughertyc3921652014-05-13 16:55:26 -07004160})(jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004161
4162
Dirk Doughertyc3921652014-05-13 16:55:26 -07004163/* Calculate the vertical area remaining */
4164(function($) {
4165 $.fn.ellipsisfade= function(lineHeight) {
4166 this.each(function() {
4167 // get element text
4168 var $this = $(this);
4169 var remainingHeight = $this.parent().parent().height();
4170 $this.parent().siblings().each(function ()
smain@google.comc91ecb72014-06-23 10:22:23 -07004171 {
smain@google.comcda1a9a2014-06-19 17:07:46 -07004172 if ($(this).is(":visible")) {
Dirk Dougherty29e93432015-05-05 18:17:13 -07004173 var h = $(this).outerHeight(true);
smain@google.comcda1a9a2014-06-19 17:07:46 -07004174 remainingHeight = remainingHeight - h;
4175 }
Dirk Doughertyc3921652014-05-13 16:55:26 -07004176 });
4177
4178 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
4179 $this.parent().css({'height': adjustedRemainingHeight});
4180 $this.css({'height': "auto"});
4181 });
4182
4183 return this;
4184 };
4185}) (jQuery);
Robert Lye7eeb402014-06-03 19:35:24 -07004186
4187/*
4188 Fullscreen Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004189
Robert Lye7eeb402014-06-03 19:35:24 -07004190 The following allows for an area at the top of the page that takes over the
smain@google.com95948b82014-06-16 19:24:25 -07004191 entire browser height except for its top offset and an optional bottom
Robert Lye7eeb402014-06-03 19:35:24 -07004192 padding specified as a data attribute.
smain@google.com95948b82014-06-16 19:24:25 -07004193
Robert Lye7eeb402014-06-03 19:35:24 -07004194 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004195
Robert Lye7eeb402014-06-03 19:35:24 -07004196 <div class="fullscreen-carousel">
4197 <div class="fullscreen-carousel-content">
4198 <!-- content here -->
4199 </div>
4200 <div class="fullscreen-carousel-content">
4201 <!-- content here -->
4202 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004203
Robert Lye7eeb402014-06-03 19:35:24 -07004204 etc ...
smain@google.com95948b82014-06-16 19:24:25 -07004205
Robert Lye7eeb402014-06-03 19:35:24 -07004206 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004207
Robert Lye7eeb402014-06-03 19:35:24 -07004208 Control over how the carousel takes over the screen can mostly be defined in
4209 a css file. Setting min-height on the .fullscreen-carousel-content elements
smain@google.com95948b82014-06-16 19:24:25 -07004210 will prevent them from shrinking to far vertically when the browser is very
Robert Lye7eeb402014-06-03 19:35:24 -07004211 short, and setting max-height on the .fullscreen-carousel itself will prevent
smain@google.com95948b82014-06-16 19:24:25 -07004212 the area from becoming to long in the case that the browser is stretched very
Robert Lye7eeb402014-06-03 19:35:24 -07004213 tall.
smain@google.com95948b82014-06-16 19:24:25 -07004214
Robert Lye7eeb402014-06-03 19:35:24 -07004215 There is limited functionality for having multiple sections since that request
4216 was removed, but it is possible to add .next-arrow and .prev-arrow elements to
4217 scroll between multiple content areas.
4218*/
4219
4220(function() {
4221 $(document).ready(function() {
4222 $('.fullscreen-carousel').each(function() {
4223 initWidget(this);
4224 });
4225 });
4226
4227 function initWidget(widget) {
4228 var $widget = $(widget);
smain@google.com95948b82014-06-16 19:24:25 -07004229
Robert Lye7eeb402014-06-03 19:35:24 -07004230 var topOffset = $widget.offset().top;
4231 var padBottom = parseInt($widget.data('paddingbottom')) || 0;
4232 var maxHeight = 0;
4233 var minHeight = 0;
4234 var $content = $widget.find('.fullscreen-carousel-content');
4235 var $nextArrow = $widget.find('.next-arrow');
4236 var $prevArrow = $widget.find('.prev-arrow');
4237 var $curSection = $($content[0]);
smain@google.com95948b82014-06-16 19:24:25 -07004238
Robert Lye7eeb402014-06-03 19:35:24 -07004239 if ($content.length <= 1) {
4240 $nextArrow.hide();
4241 $prevArrow.hide();
4242 } else {
4243 $nextArrow.click(function() {
4244 var index = ($content.index($curSection) + 1);
4245 $curSection.hide();
4246 $curSection = $($content[index >= $content.length ? 0 : index]);
4247 $curSection.show();
4248 });
smain@google.com95948b82014-06-16 19:24:25 -07004249
Robert Lye7eeb402014-06-03 19:35:24 -07004250 $prevArrow.click(function() {
4251 var index = ($content.index($curSection) - 1);
4252 $curSection.hide();
4253 $curSection = $($content[index < 0 ? $content.length - 1 : 0]);
4254 $curSection.show();
4255 });
4256 }
4257
4258 // Just hide all content sections except first.
4259 $content.each(function(index) {
4260 if ($(this).height() > minHeight) minHeight = $(this).height();
4261 $(this).css({position: 'absolute', display: index > 0 ? 'none' : ''});
4262 });
4263
4264 // Register for changes to window size, and trigger.
4265 $(window).resize(resizeWidget);
4266 resizeWidget();
4267
4268 function resizeWidget() {
4269 var height = $(window).height() - topOffset - padBottom;
4270 $widget.width($(window).width());
smain@google.com95948b82014-06-16 19:24:25 -07004271 $widget.height(height < minHeight ? minHeight :
Robert Lye7eeb402014-06-03 19:35:24 -07004272 (maxHeight && height > maxHeight ? maxHeight : height));
4273 }
smain@google.com95948b82014-06-16 19:24:25 -07004274 }
Robert Lye7eeb402014-06-03 19:35:24 -07004275})();
4276
4277
4278
4279
4280
4281/*
4282 Tab Carousel
smain@google.com95948b82014-06-16 19:24:25 -07004283
Robert Lye7eeb402014-06-03 19:35:24 -07004284 The following allows tab widgets to be installed via the html below. Each
4285 tab content section should have a data-tab attribute matching one of the
4286 nav items'. Also each tab content section should have a width matching the
4287 tab carousel.
smain@google.com95948b82014-06-16 19:24:25 -07004288
Robert Lye7eeb402014-06-03 19:35:24 -07004289 HTML:
smain@google.com95948b82014-06-16 19:24:25 -07004290
Robert Lye7eeb402014-06-03 19:35:24 -07004291 <div class="tab-carousel">
4292 <ul class="tab-nav">
4293 <li><a href="#" data-tab="handsets">Handsets</a>
4294 <li><a href="#" data-tab="wearable">Wearable</a>
4295 <li><a href="#" data-tab="tv">TV</a>
4296 </ul>
smain@google.com95948b82014-06-16 19:24:25 -07004297
Robert Lye7eeb402014-06-03 19:35:24 -07004298 <div class="tab-carousel-content">
4299 <div data-tab="handsets">
4300 <!--Full width content here-->
4301 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004302
Robert Lye7eeb402014-06-03 19:35:24 -07004303 <div data-tab="wearable">
4304 <!--Full width content here-->
4305 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004306
Robert Lye7eeb402014-06-03 19:35:24 -07004307 <div data-tab="tv">
4308 <!--Full width content here-->
4309 </div>
4310 </div>
4311 </div>
smain@google.com95948b82014-06-16 19:24:25 -07004312
Robert Lye7eeb402014-06-03 19:35:24 -07004313*/
4314(function() {
4315 $(document).ready(function() {
4316 $('.tab-carousel').each(function() {
4317 initWidget(this);
4318 });
4319 });
4320
4321 function initWidget(widget) {
4322 var $widget = $(widget);
4323 var $nav = $widget.find('.tab-nav');
4324 var $anchors = $nav.find('[data-tab]');
4325 var $li = $nav.find('li');
4326 var $contentContainer = $widget.find('.tab-carousel-content');
4327 var $tabs = $contentContainer.find('[data-tab]');
4328 var $curTab = $($tabs[0]); // Current tab is first tab.
4329 var width = $widget.width();
4330
4331 // Setup nav interactivity.
4332 $anchors.click(function(evt) {
4333 evt.preventDefault();
4334 var query = '[data-tab=' + $(this).data('tab') + ']';
smain@google.com95948b82014-06-16 19:24:25 -07004335 transitionWidget($tabs.filter(query));
Robert Lye7eeb402014-06-03 19:35:24 -07004336 });
smain@google.com95948b82014-06-16 19:24:25 -07004337
Robert Lye7eeb402014-06-03 19:35:24 -07004338 // Add highlight for navigation on first item.
4339 var $highlight = $('<div>').addClass('highlight')
4340 .css({left:$li.position().left + 'px', width:$li.outerWidth() + 'px'})
4341 .appendTo($nav);
smain@google.com95948b82014-06-16 19:24:25 -07004342
Robert Lye7eeb402014-06-03 19:35:24 -07004343 // Store height since we will change contents to absolute.
4344 $contentContainer.height($contentContainer.height());
smain@google.com95948b82014-06-16 19:24:25 -07004345
Robert Lye7eeb402014-06-03 19:35:24 -07004346 // Absolutely position tabs so they're ready for transition.
4347 $tabs.each(function(index) {
4348 $(this).css({position: 'absolute', left: index > 0 ? width + 'px' : '0'});
4349 });
smain@google.com95948b82014-06-16 19:24:25 -07004350
Robert Lye7eeb402014-06-03 19:35:24 -07004351 function transitionWidget($toTab) {
4352 if (!$curTab.is($toTab)) {
4353 var curIndex = $tabs.index($curTab[0]);
4354 var toIndex = $tabs.index($toTab[0]);
4355 var dir = toIndex > curIndex ? 1 : -1;
smain@google.com95948b82014-06-16 19:24:25 -07004356
Robert Lye7eeb402014-06-03 19:35:24 -07004357 // Animate content sections.
4358 $toTab.css({left:(width * dir) + 'px'});
4359 $curTab.animate({left:(width * -dir) + 'px'});
4360 $toTab.animate({left:'0'});
smain@google.com95948b82014-06-16 19:24:25 -07004361
Robert Lye7eeb402014-06-03 19:35:24 -07004362 // Animate navigation highlight.
smain@google.com95948b82014-06-16 19:24:25 -07004363 $highlight.animate({left:$($li[toIndex]).position().left + 'px',
Robert Lye7eeb402014-06-03 19:35:24 -07004364 width:$($li[toIndex]).outerWidth() + 'px'})
smain@google.com95948b82014-06-16 19:24:25 -07004365
Robert Lye7eeb402014-06-03 19:35:24 -07004366 // Store new current section.
4367 $curTab = $toTab;
4368 }
4369 }
smain@google.com95948b82014-06-16 19:24:25 -07004370 }
Dirk Doughertyb87e3002014-11-18 19:34:34 -08004371})();
Dirk Dougherty29e93432015-05-05 18:17:13 -07004372
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004373/**
4374 * Auto TOC
4375 *
4376 * Upgrades h2s on the page to have a rule and be toggle-able on mobile.
4377 */
4378(function($) {
4379 var upgraded = false;
4380 var h2Titles;
4381
4382 function initWidget() {
4383 // add HRs below all H2s (except for a few other h2 variants)
4384 // Consider doing this with css instead.
4385 h2Titles = $('h2').not('#qv h2, #tb h2, .sidebox h2, #devdoc-nav h2, h2.norule');
4386 h2Titles.css({marginBottom:0}).after('<hr/>');
4387
4388 // Exit early if on older browser.
4389 if (!window.matchMedia) {
4390 return;
4391 }
4392
4393 // Only run logic in mobile layout.
4394 var query = window.matchMedia('(max-width: 719px)');
4395 if (query.matches) {
4396 makeTogglable();
4397 } else {
4398 query.addListener(makeTogglable);
4399 }
4400 }
4401
4402 function makeTogglable() {
4403 // Only run this logic once.
4404 if (upgraded) { return; }
4405 upgraded = true;
4406
4407 // Only make content h2s togglable.
4408 var contentTitles = h2Titles.filter('#jd-content *');
4409
4410 // If there are more than 1
4411 if (contentTitles.size() < 2) {
4412 return;
4413 }
4414
4415 contentTitles.each(function() {
4416 // Find all the relevant nodes.
4417 var $title = $(this);
4418 var $hr = $title.next();
4419 var $contents = $hr.nextUntil('h2, .next-docs');
4420 var $section = $($title)
4421 .add($hr)
4422 .add($title.prev('a[name]'))
4423 .add($contents);
4424 var $anchor = $section.first().prev();
4425 var anchorMethod = 'after';
4426 if ($anchor.length === 0) {
4427 $anchor = $title.parent();
4428 anchorMethod = 'prepend';
4429 }
4430
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004431 // Some h2s are in their own container making it pretty hard to find the end, so skip.
4432 if ($contents.length === 0) {
4433 return;
4434 }
4435
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004436 // Remove from DOM before messing with it. DOM is slow!
4437 $section.detach();
4438
4439 // Add mobile-only expand arrows.
4440 $title.prepend('<span class="dac-visible-mobile-inline-block">' +
4441 '<i class="dac-toggle-expand dac-sprite dac-expand-more-black"></i>' +
4442 '<i class="dac-toggle-collapse dac-sprite dac-expand-less-black"></i>' +
4443 '</span>')
4444 .attr('data-toggle', 'section');
4445
4446 // Wrap in magic markup.
4447 $section = $section.wrapAll('<div class="dac-toggle dac-mobile">').parent();
4448 $contents.wrapAll('<div class="dac-toggle-content"><div>'); // extra div used for max-height calculation.
4449
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004450 // Pre-expand section if requested.
4451 if ($title.hasClass('is-expanded')) {
4452 $section.addClass('is-expanded');
4453 }
4454
4455 // Pre-expand section if targetted by hash.
4456 if (location.hash && $section.find(location.hash).length) {
4457 $section.addClass('is-expanded');
4458 }
4459
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004460 // Add it back to the dom.
4461 $anchor[anchorMethod].call($anchor, $section);
4462 });
4463 }
4464
4465 $(function() {
4466 initWidget();
4467 });
4468})(jQuery);
4469
Dirk Dougherty29e93432015-05-05 18:17:13 -07004470(function($) {
4471 'use strict';
4472
4473 /**
4474 * Toggle Floating Label state.
4475 * @param {HTMLElement} el - The DOM element.
4476 * @param options
4477 * @constructor
4478 */
4479 function FloatingLabel(el, options) {
4480 this.el = $(el);
4481 this.options = $.extend({}, FloatingLabel.DEFAULTS_, options);
4482 this.group = this.el.closest('.dac-form-input-group');
4483 this.input = this.group.find('.dac-form-input');
4484
4485 this.checkValue_ = this.checkValue_.bind(this);
4486 this.checkValue_();
4487
4488 this.input.on('focus', function() {
4489 this.group.addClass('dac-focused');
4490 }.bind(this));
4491 this.input.on('blur', function() {
4492 this.group.removeClass('dac-focused');
4493 this.checkValue_();
4494 }.bind(this));
4495 this.input.on('keyup', this.checkValue_);
4496 }
4497
4498 /**
4499 * The label is moved out of the textbox when it has a value.
4500 */
4501 FloatingLabel.prototype.checkValue_ = function() {
4502 if (this.input.val().length) {
4503 this.group.addClass('dac-has-value');
4504 } else {
4505 this.group.removeClass('dac-has-value');
4506 }
4507 };
4508
4509 /**
4510 * jQuery plugin
4511 * @param {object} options - Override default options.
4512 */
4513 $.fn.dacFloatingLabel = function(options) {
4514 return this.each(function() {
4515 new FloatingLabel(this, options);
4516 });
4517 };
4518
4519 $(document).on('ready.aranja', function() {
4520 $('.dac-form-floatlabel').each(function() {
4521 $(this).dacFloatingLabel($(this).data());
4522 });
4523 });
4524})(jQuery);
4525
4526/* global toRoot, CAROUSEL_OVERRIDE */
4527(function($) {
4528 // Ordering matters
4529 var TAG_MAP = [
4530 {from: 'developerstory', to: 'Android Developer Story'},
4531 {from: 'googleplay', to: 'Google Play'}
4532 ];
4533
4534 function DacCarouselQuery(el) {
4535 this.el = $(el);
4536
4537 var opts = this.el.data();
4538 opts.maxResults = parseInt(opts.maxResults || '100', 10);
4539 opts.query = opts.carouselQuery;
4540 var resources = $.queryResources(opts);
4541
4542 this.el.empty();
4543 $(resources).map(function() {
4544 var resource = $.extend({}, this, CAROUSEL_OVERRIDE[this.url]);
4545 var slide = $('<article class="dac-expand dac-hero">');
4546 var image = cleanUrl(resource.heroImage || resource.image);
4547 var fullBleed = image && !resource.heroColor;
4548
4549 // Configure background
4550 slide.css({
4551 backgroundImage: fullBleed ? 'url(' + image + ')' : '',
4552 backgroundColor: resource.heroColor || ''
4553 });
4554
4555 // Should copy be inverted
4556 slide.toggleClass('dac-invert', resource.heroInvert || fullBleed);
4557 slide.toggleClass('dac-darken', fullBleed);
4558
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004559 // Should be clickable
4560 slide.append($('<a class="dac-hero-carousel-action">').attr('href', cleanUrl(resource.url)));
4561
Dirk Dougherty29e93432015-05-05 18:17:13 -07004562 var cols = $('<div class="cols dac-hero-content">');
4563
4564 // inline image column
4565 var rightCol = $('<div class="col-1of2 col-push-1of2 dac-hero-figure">')
4566 .appendTo(cols);
4567
4568 if (!fullBleed && image) {
4569 rightCol.append($('<img>').attr('src', image));
4570 }
4571
4572 // info column
4573 $('<div class="col-1of2 col-pull-1of2">')
4574 .append($('<div class="dac-hero-tag">').text(formatTag(resource)))
4575 .append($('<h1 class="dac-hero-title">').text(formatTitle(resource)))
4576 .append($('<p class="dac-hero-description">').text(resource.summary))
4577 .append($('<a class="dac-hero-cta">')
4578 .text(formatCTA(resource))
4579 .attr('href', cleanUrl(resource.url))
4580 .prepend($('<span class="dac-sprite dac-auto-chevron">'))
4581 )
4582 .appendTo(cols);
4583
4584 slide.append(cols.wrap('<div class="wrap">').parent());
4585 return slide[0];
4586 }).prependTo(this.el);
4587
4588 // Pagination element.
4589 this.el.append('<div class="dac-hero-carousel-pagination"><div class="wrap" data-carousel-pagination>');
4590
4591 this.el.dacCarousel();
4592 }
4593
4594 function cleanUrl(url) {
4595 if (url && url.indexOf('//') === -1) {
4596 url = toRoot + url;
4597 }
4598 return url;
4599 }
4600
4601 function formatTag(resource) {
4602 // Hmm, need a better more scalable solution for this.
4603 for (var i = 0, mapping; mapping = TAG_MAP[i]; i++) {
4604 if (resource.tags.indexOf(mapping.from) > -1) {
4605 return mapping.to;
4606 }
4607 }
4608 return resource.type;
4609 }
4610
4611 function formatTitle(resource) {
4612 return resource.title.replace(/android developer story: /i, '');
4613 }
4614
4615 function formatCTA(resource) {
4616 return resource.type === 'youtube' ? 'Watch the video' : 'Learn more';
4617 }
4618
4619 // jQuery plugin
4620 $.fn.dacCarouselQuery = function() {
4621 return this.each(function() {
4622 var el = $(this);
4623 var data = el.data('dac.carouselQuery');
4624
4625 if (!data) { el.data('dac.carouselQuery', (data = new DacCarouselQuery(el))); }
4626 });
4627 };
4628
4629 // Data API
4630 $(function() {
4631 $('[data-carousel-query]').dacCarouselQuery();
4632 });
4633})(jQuery);
4634
4635(function($) {
4636 /**
4637 * A CSS based carousel, inspired by SequenceJS.
4638 * @param {jQuery} el
4639 * @param {object} options
4640 * @constructor
4641 */
4642 function DacCarousel(el, options) {
4643 this.el = $(el);
4644 this.options = options = $.extend({}, DacCarousel.OPTIONS, this.el.data(), options || {});
4645 this.frames = this.el.find(options.frameSelector);
4646 this.count = this.frames.size();
4647 this.current = options.start;
4648
4649 this.initPagination();
4650 this.initEvents();
4651 this.initFrame();
4652 }
4653
4654 DacCarousel.OPTIONS = {
4655 auto: true,
4656 autoTime: 10000,
4657 autoMinTime: 5000,
4658 btnPrev: '[data-carousel-prev]',
4659 btnNext: '[data-carousel-next]',
4660 frameSelector: 'article',
4661 loop: true,
4662 start: 0,
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004663 swipeThreshold: 160,
Dirk Dougherty29e93432015-05-05 18:17:13 -07004664 pagination: '[data-carousel-pagination]'
4665 };
4666
4667 DacCarousel.prototype.initPagination = function() {
4668 this.pagination = $([]);
4669 if (!this.options.pagination) { return; }
4670
4671 var pagination = $('<ul class="dac-pagination">');
4672 var parent = this.el;
4673 if (typeof this.options.pagination === 'string') { parent = this.el.find(this.options.pagination); }
4674
4675 if (this.count > 1) {
4676 for (var i = 0; i < this.count; i++) {
4677 var li = $('<li class="dac-pagination-item">').text(i);
4678 if (i === this.options.start) { li.addClass('active'); }
4679 li.click(this.go.bind(this, i));
4680
4681 pagination.append(li);
4682 }
4683 this.pagination = pagination.children();
4684 parent.append(pagination);
4685 }
4686 };
4687
4688 DacCarousel.prototype.initEvents = function() {
4689 var that = this;
4690
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004691 this.touch = {
4692 start: {x: 0, y: 0},
4693 end: {x: 0, y: 0}
4694 };
4695
4696 this.el.on('touchstart', this.touchstart_.bind(this));
4697 this.el.on('touchend', this.touchend_.bind(this));
4698 this.el.on('touchmove', this.touchmove_.bind(this));
4699
Dirk Dougherty29e93432015-05-05 18:17:13 -07004700 this.el.hover(function() {
4701 that.pauseRotateTimer();
4702 }, function() {
4703 that.startRotateTimer();
4704 });
4705
4706 $(this.options.btnPrev).click(function(e) {
4707 e.preventDefault();
4708 that.prev();
4709 });
4710
4711 $(this.options.btnNext).click(function(e) {
4712 e.preventDefault();
4713 that.next();
4714 });
4715 };
4716
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004717 DacCarousel.prototype.touchstart_ = function(event) {
4718 var t = event.originalEvent.touches[0];
4719 this.touch.start = {x: t.screenX, y: t.screenY};
4720 };
4721
4722 DacCarousel.prototype.touchend_ = function() {
4723 var deltaX = this.touch.end.x - this.touch.start.x;
4724 var deltaY = Math.abs(this.touch.end.y - this.touch.start.y);
4725 var shouldSwipe = (deltaY < Math.abs(deltaX)) && (Math.abs(deltaX) >= this.options.swipeThreshold);
4726
4727 if (shouldSwipe) {
4728 if (deltaX > 0) {
4729 this.prev();
4730 } else {
4731 this.next();
4732 }
4733 }
4734 };
4735
4736 DacCarousel.prototype.touchmove_ = function(event) {
4737 var t = event.originalEvent.touches[0];
4738 this.touch.end = {x: t.screenX, y: t.screenY};
4739 };
4740
Dirk Dougherty29e93432015-05-05 18:17:13 -07004741 DacCarousel.prototype.initFrame = function() {
4742 this.frames.removeClass('active').eq(this.options.start).addClass('active');
4743 };
4744
4745 DacCarousel.prototype.startRotateTimer = function() {
4746 if (!this.options.auto || this.rotateTimer) { return; }
4747 this.rotateTimer = setTimeout(this.next.bind(this), this.options.autoTime);
4748 };
4749
4750 DacCarousel.prototype.pauseRotateTimer = function() {
4751 clearTimeout(this.rotateTimer);
4752 this.rotateTimer = null;
4753 };
4754
4755 DacCarousel.prototype.prev = function() {
4756 this.go(this.current - 1);
4757 };
4758
4759 DacCarousel.prototype.next = function() {
4760 this.go(this.current + 1);
4761 };
4762
4763 DacCarousel.prototype.go = function(next) {
4764 // Figure out what the next slide is.
4765 while (this.count > 0 && next >= this.count) { next -= this.count; }
4766 while (next < 0) { next += this.count; }
4767
4768 // Cancel if we're already on that slide.
4769 if (next === this.current) { return; }
4770
4771 // Prepare next slide.
4772 this.frames.eq(next).removeClass('out');
4773
4774 // Recalculate styles before starting slide transition.
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004775 this.el.resolveStyles();
4776 // Update pagination
4777 this.pagination.removeClass('active').eq(next).addClass('active');
Dirk Dougherty29e93432015-05-05 18:17:13 -07004778
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004779 // Transition out current frame
4780 this.frames.eq(this.current).toggleClass('active out');
Dirk Dougherty29e93432015-05-05 18:17:13 -07004781
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004782 // Transition in a new frame
4783 this.frames.eq(next).toggleClass('active');
Dirk Dougherty29e93432015-05-05 18:17:13 -07004784
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004785 this.current = next;
Dirk Dougherty29e93432015-05-05 18:17:13 -07004786 };
4787
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004788 // Helper which resolves new styles for an element, so it can start transitioning
4789 // from the new values.
4790 $.fn.resolveStyles = function() {
Dirk Dougherty29e93432015-05-05 18:17:13 -07004791 /*jshint expr:true*/
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004792 this[0] && this[0].offsetTop;
4793 return this;
4794 };
Dirk Dougherty29e93432015-05-05 18:17:13 -07004795
4796 // jQuery plugin
4797 $.fn.dacCarousel = function() {
4798 this.each(function() {
4799 var $el = $(this);
4800 $el.data('dac-carousel', new DacCarousel(this));
4801 });
4802 return this;
4803 };
4804
4805 // Data API
4806 $(function() {
4807 $('[data-carousel]').dacCarousel();
4808 });
4809})(jQuery);
4810
4811(function($) {
4812 'use strict';
4813
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004814 function Modal(el, options) {
4815 this.el = $(el);
4816 this.options = $.extend({}, ToggleModal.DEFAULTS_, options);
4817 this.isOpen = false;
4818
4819 this.el.on('click', function(event) {
4820 if (!$.contains($('.dac-modal-window')[0], event.target)) {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004821 return this.el.trigger('modal-close');
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004822 }
4823 }.bind(this));
4824
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004825 this.el.on('modal-open', this.open_.bind(this));
4826 this.el.on('modal-close', this.close_.bind(this));
4827 this.el.on('modal-toggle', this.toggle_.bind(this));
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004828 }
4829
4830 Modal.prototype.toggle_ = function() {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004831 this.el.trigger('modal-' + (this.isOpen ? 'close' : 'open'));
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004832 };
4833
4834 Modal.prototype.close_ = function() {
4835 this.el.removeClass('dac-active');
4836 $('body').removeClass('dac-modal-open');
4837 this.isOpen = false;
4838 };
4839
4840 Modal.prototype.open_ = function() {
4841 this.el.addClass('dac-active');
4842 $('body').addClass('dac-modal-open');
4843 this.isOpen = true;
4844 };
4845
Dirk Dougherty29e93432015-05-05 18:17:13 -07004846 function ToggleModal(el, options) {
4847 this.el = $(el);
4848 this.options = $.extend({}, ToggleModal.DEFAULTS_, options);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004849 this.modal = this.options.modalToggle ? $('[data-modal="' + this.options.modalToggle + '"]') :
4850 this.el.closest('[data-modal]');
4851
Dirk Dougherty29e93432015-05-05 18:17:13 -07004852 this.el.on('click', this.clickHandler_.bind(this));
4853 }
4854
Dirk Dougherty29e93432015-05-05 18:17:13 -07004855 ToggleModal.prototype.clickHandler_ = function(event) {
4856 event.preventDefault();
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004857 this.modal.trigger('modal-toggle');
Dirk Dougherty29e93432015-05-05 18:17:13 -07004858 };
4859
4860 /**
4861 * jQuery plugin
4862 * @param {object} options - Override default options.
4863 */
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004864 $.fn.dacModal = function(options) {
4865 return this.each(function() {
4866 new Modal(this, options);
4867 });
4868 };
4869
Dirk Dougherty29e93432015-05-05 18:17:13 -07004870 $.fn.dacToggleModal = function(options) {
4871 return this.each(function() {
4872 new ToggleModal(this, options);
4873 });
4874 };
4875
4876 /**
4877 * Data Attribute API
4878 */
4879 $(document).on('ready.aranja', function() {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004880 $('[data-modal]').each(function() {
4881 $(this).dacModal($(this).data());
4882 });
4883
4884 $('[data-modal-toggle]').each(function() {
Dirk Dougherty29e93432015-05-05 18:17:13 -07004885 $(this).dacToggleModal($(this).data());
4886 });
4887 });
4888})(jQuery);
4889
4890(function($) {
4891 'use strict';
4892
4893 /**
4894 * Toggle the visabilty of the mobile navigation.
4895 * @param {HTMLElement} el - The DOM element.
4896 * @param options
4897 * @constructor
4898 */
4899 function ToggleNav(el, options) {
4900 this.el = $(el);
4901 this.options = $.extend({}, ToggleNav.DEFAULTS_, options);
4902 this.options.target = [this.options.navigation];
4903
4904 if (this.options.body) {this.options.target.push('body')}
4905 if (this.options.dimmer) {this.options.target.push(this.options.dimmer)}
4906
4907 this.el.on('click', this.clickHandler_.bind(this));
4908 }
4909
4910 /**
4911 * ToggleNav Default Settings
4912 * @type {{body: boolean, dimmer: string, navigation: string, toggleClass: string}}
4913 * @private
4914 */
4915 ToggleNav.DEFAULTS_ = {
4916 body: true,
4917 dimmer: '.dac-nav-dimmer',
4918 navigation: '[data-dac-nav]',
4919 toggleClass: 'dac-nav-open'
4920 };
4921
4922 /**
4923 * The actual toggle logic.
4924 * @param event
4925 * @private
4926 */
4927 ToggleNav.prototype.clickHandler_ = function(event) {
4928 event.preventDefault();
4929 $(this.options.target.join(', ')).toggleClass(this.options.toggleClass);
4930 };
4931
4932 /**
4933 * jQuery plugin
4934 * @param {object} options - Override default options.
4935 */
4936 $.fn.dacToggleMobileNav = function(options) {
4937 return this.each(function() {
4938 new ToggleNav(this, options);
4939 });
4940 };
4941
4942 /**
4943 * Data Attribute API
4944 */
4945 $(window).on('load.aranja', function() {
4946 $('[data-dac-toggle-nav]').each(function() {
4947 $(this).dacToggleMobileNav($(this).data());
4948 });
4949 });
4950})(jQuery);
4951
4952(function($) {
4953 'use strict';
4954
4955 /**
4956 * Submit the newsletter form to a Google Form.
4957 * @param {HTMLElement} el - The Form DOM element.
4958 * @constructor
4959 */
4960 function NewsletterForm(el) {
4961 this.el = $(el);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07004962 this.form = this.el.find('form');
4963 $('<iframe/>').hide()
4964 .attr('name', 'dac-newsletter-iframe')
4965 .attr('src', '')
4966 .insertBefore(this.form);
4967 this.form.on('submit', this.submitHandler_.bind(this));
Dirk Dougherty29e93432015-05-05 18:17:13 -07004968 }
4969
4970 /**
Dirk Doughertycbe032f2015-05-22 11:41:40 -07004971 * Milliseconds until modal has vanished after modal-close is triggered.
4972 * @type {number}
4973 * @private
4974 */
4975 NewsletterForm.CLOSE_DELAY_ = 300;
4976
4977 /**
4978 * Switch view to display form after close.
4979 * @private
4980 */
4981 NewsletterForm.prototype.closeHandler_ = function() {
4982 setTimeout(function() {
4983 this.el.trigger('swap-reset');
4984 }.bind(this), NewsletterForm.CLOSE_DELAY_);
4985 };
4986
4987 /**
4988 * Reset the modal to initial state.
4989 * @private
4990 */
4991 NewsletterForm.prototype.reset_ = function() {
4992 this.form.trigger('reset');
4993 this.el.one('modal-close', this.closeHandler_.bind(this));
4994 };
4995
4996 /**
4997 * Display a success view on submit.
Dirk Dougherty29e93432015-05-05 18:17:13 -07004998 * @private
4999 */
5000 NewsletterForm.prototype.submitHandler_ = function() {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07005001 this.el.one('swap-complete', this.reset_.bind(this));
5002 this.el.trigger('swap-content');
Dirk Dougherty29e93432015-05-05 18:17:13 -07005003 };
5004
5005 /**
5006 * jQuery plugin
5007 * @param {object} options - Override default options.
5008 */
5009 $.fn.dacNewsletterForm = function(options) {
5010 return this.each(function() {
5011 new NewsletterForm(this, options);
5012 });
5013 };
5014
5015 /**
5016 * Data Attribute API
5017 */
5018 $(document).on('ready.aranja', function() {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07005019 $('[data-newsletter]').each(function() {
Dirk Dougherty29e93432015-05-05 18:17:13 -07005020 $(this).dacNewsletterForm();
5021 });
5022 });
5023})(jQuery);
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07005024
5025(function($) {
5026 'use strict';
5027
5028 /**
5029 * Smoothly scroll to location on current page.
5030 * @param el
5031 * @param options
5032 * @constructor
5033 */
5034 function ScrollButton(el, options) {
5035 this.el = $(el);
5036 this.target = $(this.el.attr('href'));
5037 this.options = $.extend({}, ScrollButton.DEFAULTS_, options);
5038
5039 if (typeof this.options.offset === 'string') {
5040 this.options.offset = $(this.options.offset).height();
5041 }
5042
5043 this.el.on('click', this.clickHandler_.bind(this));
5044 }
5045
5046 /**
5047 * Default options
5048 * @type {{duration: number, easing: string, offset: number, scrollContainer: string}}
5049 * @private
5050 */
5051 ScrollButton.DEFAULTS_ = {
5052 duration: 300,
5053 easing: 'swing',
5054 offset: 0,
5055 scrollContainer: 'html, body'
5056 };
5057
5058 /**
5059 * Scroll logic
5060 * @param event
5061 * @private
5062 */
5063 ScrollButton.prototype.clickHandler_ = function(event) {
5064 if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
5065 return;
5066 }
5067
5068 event.preventDefault();
5069
5070 $(this.options.scrollContainer).animate({
5071 scrollTop: this.target.offset().top - this.options.offset
5072 }, this.options);
5073 };
5074
5075 /**
5076 * jQuery plugin
5077 * @param {object} options - Override default options.
5078 */
5079 $.fn.dacScrollButton = function(options) {
5080 return this.each(function() {
5081 new ScrollButton(this, options);
5082 });
5083 };
5084
5085 /**
5086 * Data Attribute API
5087 */
5088 $(document).on('ready.aranja', function() {
5089 $('[data-scroll-button]').each(function() {
5090 $(this).dacScrollButton($(this).data());
5091 });
5092 });
5093})(jQuery);
5094
5095(function($) {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07005096 'use strict';
5097
5098 /**
5099 * A component that swaps two dynamic height views with an animation.
5100 * Listens for the following events:
5101 * * swap-content: triggers SwapContent.swap_()
5102 * * swap-reset: triggers SwapContent.reset()
5103 * @param el
5104 * @param options
5105 * @constructor
5106 */
5107 function SwapContent(el, options) {
5108 this.el = $(el);
5109 this.options = $.extend({}, SwapContent.DEFAULTS_, options);
5110 this.containers = this.el.find(this.options.container);
5111 this.initiallyActive = this.containers.children('.' + this.options.activeClass).eq(0);
5112 this.el.on('swap-content', this.swap.bind(this));
5113 this.el.on('swap-reset', this.reset.bind(this));
5114 }
5115
5116 /**
5117 * SwapContent's default settings.
5118 * @type {{activeClass: string, container: string, transitionSpeed: number}}
5119 * @private
5120 */
5121 SwapContent.DEFAULTS_ = {
5122 activeClass: 'dac-active',
5123 container: '[data-swap-container]',
5124 transitionSpeed: 500
5125 };
5126
5127 /**
5128 * Returns container's visible height.
5129 * @param container
5130 * @returns {number}
5131 */
5132 SwapContent.prototype.currentHeight = function(container) {
5133 return container.children('.' + this.options.activeClass).outerHeight();
5134 };
5135
5136 /**
5137 * Reset to show initial content
5138 */
5139 SwapContent.prototype.reset = function() {
5140 if (!this.initiallyActive.hasClass(this.initiallyActive)) {
5141 this.containers.children().toggleClass(this.options.activeClass);
5142 }
5143 };
5144
5145 /**
5146 * Complete the swap.
5147 */
5148 SwapContent.prototype.complete = function() {
5149 this.containers.height('auto');
5150 this.containers.trigger('swap-complete');
5151 };
5152
5153 /**
5154 * Perform the swap of content.
5155 */
5156 SwapContent.prototype.swap = function() {
5157 console.log(this.containers);
5158 this.containers.each(function(index, container) {
5159 container = $(container);
5160 container.height(this.currentHeight(container)).children().toggleClass(this.options.activeClass);
5161 container.animate({height: this.currentHeight(container)}, this.options.transitionSpeed,
5162 this.complete.bind(this));
5163 }.bind(this));
5164 };
5165
5166 /**
5167 * jQuery plugin
5168 * @param {object} options - Override default options.
5169 */
5170 $.fn.dacSwapContent = function(options) {
5171 return this.each(function() {
5172 new SwapContent(this, options);
5173 });
5174 };
5175
5176 /**
5177 * Data Attribute API
5178 */
5179 $(document).on('ready.aranja', function() {
5180 $('[data-swap]').each(function() {
5181 $(this).dacSwapContent($(this).data());
5182 });
5183 });
5184})(jQuery);
5185
5186(function($) {
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07005187 function Toggle(el) {
5188 $(el).on('click.dac.togglesection', this.toggle);
5189 }
5190
5191 Toggle.prototype.toggle = function() {
5192 var $this = $(this);
5193
5194 var $parent = getParent($this);
5195 var isExpanded = $parent.hasClass('is-expanded');
5196
5197 transitionMaxHeight($parent.find('.dac-toggle-content'), !isExpanded);
5198 $parent.toggleClass('is-expanded');
5199
5200 return false;
5201 };
5202
5203 function getParent($this) {
5204 var selector = $this.attr('data-target');
5205
5206 if (!selector) {
5207 selector = $this.attr('href');
5208 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '');
5209 }
5210
5211 var $parent = selector && $(selector);
5212
Dirk Doughertycbe032f2015-05-22 11:41:40 -07005213 $parent = $parent && $parent.length ? $parent : $this.closest('.dac-toggle');
5214
5215 return $parent.length ? $parent : $this.parent();
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07005216 }
5217
5218 /**
5219 * Runs a transition of max-height along with responsive styles which hide or expand the element.
5220 * @param $el
5221 * @param visible
5222 */
5223 function transitionMaxHeight($el, visible) {
Dirk Doughertycbe032f2015-05-22 11:41:40 -07005224 var contentHeight = $el.prop('scrollHeight');
Dirk Doughertyf97b2ef2015-05-12 21:23:05 -07005225 var targetHeight = visible ? contentHeight : 0;
5226 var duration = $el.transitionDuration();
5227
5228 // If we're hiding, first set the maxHeight we're transitioning from.
5229 if (!visible) {
5230 $el.css('maxHeight', contentHeight + 'px')
5231 .resolveStyles();
5232 }
5233
5234 // Transition to new state
5235 $el.css('maxHeight', targetHeight);
5236
5237 // Reset maxHeight to css value after transition.
5238 setTimeout(function() {
5239 $el.css('maxHeight', '');
5240 }, duration);
5241 }
5242
5243 // Utility to get the transition duration for the element.
5244 $.fn.transitionDuration = function() {
5245 var d = $(this).css('transitionDuration') || '0s';
5246
5247 return +(parseFloat(d) * (/ms/.test(d) ? 1 : 1000)).toFixed(0);
5248 };
5249
5250 // jQuery plugin
5251 $.fn.toggleSection = function(option) {
5252 return this.each(function() {
5253 var $this = $(this);
5254 var data = $this.data('dac.togglesection');
5255 if (!data) {$this.data('dac.togglesection', (data = new Toggle(this)));}
5256 if (typeof option === 'string') {data[option].call($this);}
5257 });
5258 };
5259
5260 // Data api
5261 $(document)
5262 .on('click.toggle', '[data-toggle="section"]', Toggle.prototype.toggle);
5263})(jQuery);