blob: e626f5a399940b04a936a7260f130aa017937351 [file] [log] [blame]
Scott Maine4d8f1b2012-06-21 18:03:05 -07001var classesNav;
2var devdocNav;
3var sidenav;
4var cookie_namespace = 'android_developer';
5var NAV_PREF_TREE = "tree";
6var NAV_PREF_PANELS = "panels";
7var nav_pref;
Scott Maine4d8f1b2012-06-21 18:03:05 -07008var isMobile = false; // true if mobile, so we can adjust some layout
Scott Mainf6145542013-04-01 16:38:11 -07009var mPagePath; // initialized in ready() function
Scott Maine4d8f1b2012-06-21 18:03:05 -070010
Scott Main1b3db112012-07-03 14:06:22 -070011var basePath = getBaseUri(location.pathname);
12var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
Scott Main7e447ed2013-02-19 17:22:37 -080013var GOOGLE_DATA; // combined data for google service apis, used for search suggest
Scott Main3b90aff2013-08-01 18:09:35 -070014
Scott Main25e73002013-03-27 15:24:06 -070015// Ensure that all ajax getScript() requests allow caching
16$.ajaxSetup({
17 cache: true
18});
Scott Maine4d8f1b2012-06-21 18:03:05 -070019
20/****** ON LOAD SET UP STUFF *********/
21
Scott Maine4d8f1b2012-06-21 18:03:05 -070022$(document).ready(function() {
Scott Main7e447ed2013-02-19 17:22:37 -080023
Scott Main0e76e7e2013-03-12 10:24:07 -070024 // load json file for JD doc search suggestions
Scott Main719acb42013-12-05 16:05:09 -080025 $.getScript(toRoot + 'jd_lists_unified.js');
Scott Main7e447ed2013-02-19 17:22:37 -080026 // load json file for Android API search suggestions
27 $.getScript(toRoot + 'reference/lists.js');
28 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080029 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080030 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
31 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080032 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080033 if(jqxhr.status === 200) {
34 // combine GCM and GMS data
35 GOOGLE_DATA = GMS_DATA;
36 var start = GOOGLE_DATA.length;
37 for (var i=0; i<GCM_DATA.length; i++) {
38 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
39 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
40 }
41 }
42 });
43 }
44 });
45
Scott Main0e76e7e2013-03-12 10:24:07 -070046 // setup keyboard listener for search shortcut
47 $('body').keyup(function(event) {
48 if (event.which == 191) {
49 $('#search_autocomplete').focus();
50 }
51 });
Scott Main015d6162013-01-29 09:01:52 -080052
Scott Maine4d8f1b2012-06-21 18:03:05 -070053 // init the fullscreen toggle click event
54 $('#nav-swap .fullscreen').click(function(){
55 if ($(this).hasClass('disabled')) {
56 toggleFullscreen(true);
57 } else {
58 toggleFullscreen(false);
59 }
60 });
Scott Main3b90aff2013-08-01 18:09:35 -070061
Scott Maine4d8f1b2012-06-21 18:03:05 -070062 // initialize the divs with custom scrollbars
63 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070064
Scott Maine4d8f1b2012-06-21 18:03:05 -070065 // add HRs below all H2s (except for a few other h2 variants)
Scott Maindb3678b2012-10-23 14:13:41 -070066 $('h2').not('#qv h2').not('#tb h2').not('.sidebox h2').not('#devdoc-nav h2').not('h2.norule').css({marginBottom:0}).after('<hr/>');
Scott Maine4d8f1b2012-06-21 18:03:05 -070067
68 // set up the search close button
69 $('.search .close').click(function() {
70 $searchInput = $('#search_autocomplete');
71 $searchInput.attr('value', '');
72 $(this).addClass("hide");
73 $("#search-container").removeClass('active');
74 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070075 search_focus_changed($searchInput.get(), false);
76 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070077 });
78
79 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070080 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070081 $("#btn-quicknav").click(function() {
82 if (quicknav_open) {
83 $(this).removeClass('active');
84 quicknav_open = false;
85 collapse();
86 } else {
87 $(this).addClass('active');
88 quicknav_open = true;
89 expand();
90 }
91 })
Scott Main3b90aff2013-08-01 18:09:35 -070092
Scott Maine4d8f1b2012-06-21 18:03:05 -070093 var expand = function() {
94 $('#header-wrap').addClass('quicknav');
95 $('#quicknav').stop().show().animate({opacity:'1'});
96 }
Scott Main3b90aff2013-08-01 18:09:35 -070097
Scott Maine4d8f1b2012-06-21 18:03:05 -070098 var collapse = function() {
99 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
100 $(this).hide();
101 $('#header-wrap').removeClass('quicknav');
102 });
103 }
Scott Main3b90aff2013-08-01 18:09:35 -0700104
105
Scott Maine4d8f1b2012-06-21 18:03:05 -0700106 //Set up search
107 $("#search_autocomplete").focus(function() {
108 $("#search-container").addClass('active');
109 })
110 $("#search-container").mouseover(function() {
111 $("#search-container").addClass('active');
112 $("#search_autocomplete").focus();
113 })
114 $("#search-container").mouseout(function() {
115 if ($("#search_autocomplete").is(":focus")) return;
116 if ($("#search_autocomplete").val() == '') {
117 setTimeout(function(){
118 $("#search-container").removeClass('active');
119 $("#search_autocomplete").blur();
120 },250);
121 }
122 })
123 $("#search_autocomplete").blur(function() {
124 if ($("#search_autocomplete").val() == '') {
125 $("#search-container").removeClass('active');
126 }
127 })
128
Scott Main3b90aff2013-08-01 18:09:35 -0700129
Scott Maine4d8f1b2012-06-21 18:03:05 -0700130 // prep nav expandos
131 var pagePath = document.location.pathname;
132 // account for intl docs by removing the intl/*/ path
133 if (pagePath.indexOf("/intl/") == 0) {
134 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
135 }
Scott Mainac2aef52013-02-12 14:15:23 -0800136
Scott Maine4d8f1b2012-06-21 18:03:05 -0700137 if (pagePath.indexOf(SITE_ROOT) == 0) {
138 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
139 pagePath += 'index.html';
140 }
141 }
142
Scott Main01a25452013-02-12 17:32:27 -0800143 // Need a copy of the pagePath before it gets changed in the next block;
144 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
145 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700146 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
147 // If running locally, SITE_ROOT will be a relative path, so account for that by
148 // finding the relative URL to this page. This will allow us to find links on the page
149 // leading back to this page.
150 var pathParts = pagePath.split('/');
151 var relativePagePathParts = [];
152 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
153 for (var i = 0; i < upDirs; i++) {
154 relativePagePathParts.push('..');
155 }
156 for (var i = 0; i < upDirs; i++) {
157 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
158 }
159 relativePagePathParts.push(pathParts[pathParts.length - 1]);
160 pagePath = relativePagePathParts.join('/');
161 } else {
162 // Otherwise the page path is already an absolute URL
163 }
164
Scott Mainac2aef52013-02-12 14:15:23 -0800165 // Highlight the header tabs...
166 // highlight Design tab
167 if ($("body").hasClass("design")) {
168 $("#header li.design a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700169 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800170
171 // highlight Develop tab
172 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
173 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700174 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800175 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800176 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800177 if (rootDir == "training") {
178 $("#nav-x li.training a").addClass("selected");
179 } else if (rootDir == "guide") {
180 $("#nav-x li.guide a").addClass("selected");
181 } else if (rootDir == "reference") {
182 // If the root is reference, but page is also part of Google Services, select Google
183 if ($("body").hasClass("google")) {
184 $("#nav-x li.google a").addClass("selected");
185 } else {
186 $("#nav-x li.reference a").addClass("selected");
187 }
188 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
189 $("#nav-x li.tools a").addClass("selected");
190 } else if ($("body").hasClass("google")) {
191 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700192 } else if ($("body").hasClass("samples")) {
193 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800194 }
195
196 // highlight Distribute tab
197 } else if ($("body").hasClass("distribute")) {
198 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700199 $("#sticky-header").addClass("distribute");
200
201 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
202 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
203 if (secondFrag == "users") {
204 $("#nav-x li.users a").addClass("selected");
205 } else if (secondFrag == "engage") {
206 $("#nav-x li.engage a").addClass("selected");
207 } else if (secondFrag == "monetize") {
208 $("#nav-x li.monetize a").addClass("selected");
209 } else if (secondFrag == "tools") {
210 $("#nav-x li.disttools a").addClass("selected");
211 } else if (secondFrag == "stories") {
212 $("#nav-x li.stories a").addClass("selected");
213 } else if (secondFrag == "essentials") {
214 $("#nav-x li.essentials a").addClass("selected");
215 } else if (secondFrag == "googleplay") {
216 $("#nav-x li.googleplay a").addClass("selected");
217 }
218 } else if ($("body").hasClass("about")) {
219 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700220 }
Scott Mainac2aef52013-02-12 14:15:23 -0800221
Scott Mainf6145542013-04-01 16:38:11 -0700222 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
223 // and highlight the sidenav
224 mPagePath = pagePath;
225 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700226 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800227
Scott Mainf6145542013-04-01 16:38:11 -0700228 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700229 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700230 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700231 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800232 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700233
234 // set up prev links
235 var $prevLink = [];
236 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700237
Scott Maine4d8f1b2012-06-21 18:03:05 -0700238 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
239false; // navigate across topic boundaries only in design docs
240 if ($prevListItem.length) {
241 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700242 // jump to last topic of previous section
243 $prevLink = $prevListItem.find('a:last');
244 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700245 // jump to previous topic in this section
246 $prevLink = $prevListItem.find('a:eq(0)');
247 }
248 } else {
249 // jump to this section's index page (if it exists)
250 var $parentListItem = $selListItem.parents('li');
251 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700252
Scott Maine4d8f1b2012-06-21 18:03:05 -0700253 // except if cross boundaries aren't allowed, and we're at the top of a section already
254 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700255 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700256 && $selListItem.hasClass('nav-section')) {
257 $prevLink = [];
258 }
259 }
260
Scott Maine4d8f1b2012-06-21 18:03:05 -0700261 // set up next links
262 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700263 var startClass = false;
264 var training = $(".next-class-link").length; // decides whether to provide "next class" link
265 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700266
Scott Main1a00f7f2013-10-29 11:11:19 -0700267 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700268 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700269 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700270
271 // if there aren't any children, go to the next section (required for About pages)
272 if($nextLink.length == 0) {
273 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700274 } else if ($('.topic-start-link').length) {
275 // as long as there's a child link and there is a "topic start link" (we're on a landing)
276 // then set the landing page "start link" text to be the first doc title
277 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700278 }
Scott Main3b90aff2013-08-01 18:09:35 -0700279
Scott Main5a1123e2012-09-26 12:51:28 -0700280 // If the selected page has a description, then it's a class or article homepage
281 if ($selListItem.find('a[description]').length) {
282 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700283 startClass = true;
284 }
285 } else {
286 // jump to the next topic in this section (if it exists)
287 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700288 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700289 isCrossingBoundary = true;
290 // no more topics in this section, jump to the first topic in the next section
291 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
292 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
293 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700294 if ($nextLink.length == 0) {
295 // if that doesn't work, we're at the end of the list, so disable NEXT link
296 $('.next-page-link').attr('href','').addClass("disabled")
297 .click(function() { return false; });
298 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700299 }
300 }
301 }
Scott Main5a1123e2012-09-26 12:51:28 -0700302
303 if (startClass) {
304 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
305
Scott Main3b90aff2013-08-01 18:09:35 -0700306 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700307 // then we need to add a bottom border to button
308 if (!$("#tb").length) {
309 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700310 }
Scott Main5a1123e2012-09-26 12:51:28 -0700311 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
312 $('.content-footer.next-class').show();
313 $('.next-page-link').attr('href','')
314 .removeClass("hide").addClass("disabled")
315 .click(function() { return false; });
Scott Main1a00f7f2013-10-29 11:11:19 -0700316 if ($nextLink.length) {
317 $('.next-class-link').attr('href',$nextLink.attr('href'))
318 .removeClass("hide").append($nextLink.html());
319 $('.next-class-link').find('.new').empty();
320 }
Scott Main5a1123e2012-09-26 12:51:28 -0700321 } else {
322 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
323 }
324
325 if (!startClass && $prevLink.length) {
326 var prevHref = $prevLink.attr('href');
327 if (prevHref == SITE_ROOT + 'index.html') {
328 // Don't show Previous when it leads to the homepage
329 } else {
330 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
331 }
Scott Main3b90aff2013-08-01 18:09:35 -0700332 }
Scott Main5a1123e2012-09-26 12:51:28 -0700333
334 // If this is a training 'article', there should be no prev/next nav
335 // ... if the grandparent is the "nav" ... and it has no child list items...
336 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
337 !$selListItem.find('li').length) {
338 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
339 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700340 }
Scott Main3b90aff2013-08-01 18:09:35 -0700341
Scott Maine4d8f1b2012-06-21 18:03:05 -0700342 }
Scott Main3b90aff2013-08-01 18:09:35 -0700343
344
345
Scott Main5a1123e2012-09-26 12:51:28 -0700346 // Set up the course landing pages for Training with class names and descriptions
347 if ($('body.trainingcourse').length) {
348 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
Scott Maine7d75352014-05-22 15:50:56 -0700349
350 // create an array for all the class descriptions
351 var $classDescriptions = new Array($classLinks.length);
352 var lang = getLangPref();
353 $classLinks.each(function(index) {
354 var langDescr = $(this).attr(lang + "-description");
355 if (typeof langDescr !== 'undefined' && langDescr !== false) {
356 // if there's a class description in the selected language, use that
357 $classDescriptions[index] = langDescr;
358 } else {
359 // otherwise, use the default english description
360 $classDescriptions[index] = $(this).attr("description");
361 }
362 });
Scott Main3b90aff2013-08-01 18:09:35 -0700363
Scott Main5a1123e2012-09-26 12:51:28 -0700364 var $olClasses = $('<ol class="class-list"></ol>');
365 var $liClass;
366 var $imgIcon;
367 var $h2Title;
368 var $pSummary;
369 var $olLessons;
370 var $liLesson;
371 $classLinks.each(function(index) {
372 $liClass = $('<li></li>');
373 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
Scott Maine7d75352014-05-22 15:50:56 -0700374 $pSummary = $('<p class="description">' + $classDescriptions[index] + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700375
Scott Main5a1123e2012-09-26 12:51:28 -0700376 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700377
Scott Main5a1123e2012-09-26 12:51:28 -0700378 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700379
Scott Main5a1123e2012-09-26 12:51:28 -0700380 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700381 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
382 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700383 $lessons.each(function(index) {
384 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
385 });
386 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700387 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
388 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700389 $pSummary.addClass('article');
390 }
391
392 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
393 $olClasses.append($liClass);
394 });
395 $('.jd-descr').append($olClasses);
396 }
397
Scott Maine4d8f1b2012-06-21 18:03:05 -0700398 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700399 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700400
Scott Main3b90aff2013-08-01 18:09:35 -0700401
Scott Maine4d8f1b2012-06-21 18:03:05 -0700402 $(".scroll-pane").scroll(function(event) {
403 event.preventDefault();
404 return false;
405 });
406
407 /* Resize nav height when window height changes */
408 $(window).resize(function() {
409 if ($('#side-nav').length == 0) return;
410 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
411 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
412 // make sidenav behave when resizing the window and side-scolling is a concern
Scott Mainf5257812014-05-22 17:26:38 -0700413 if (sticky) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700414 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
415 updateSideNavPosition();
416 } else {
417 updateSidenavFullscreenWidth();
418 }
419 }
420 resizeNav();
421 });
422
423
Scott Maine4d8f1b2012-06-21 18:03:05 -0700424 var navBarLeftPos;
425 if ($('#devdoc-nav').length) {
426 setNavBarLeftPos();
427 }
428
429
Scott Maine4d8f1b2012-06-21 18:03:05 -0700430 // Set up play-on-hover <video> tags.
431 $('video.play-on-hover').bind('click', function(){
432 $(this).get(0).load(); // in case the video isn't seekable
433 $(this).get(0).play();
434 });
435
436 // Set up tooltips
437 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700438 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700439 var $target = $(this);
440 var $tooltip = $('<div>')
441 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700442 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700443 .hide()
444 .appendTo('body');
445 $target.removeAttr('title');
446
447 $target.hover(function() {
448 // in
449 var targetRect = $target.offset();
450 targetRect.width = $target.width();
451 targetRect.height = $target.height();
452
453 $tooltip.css({
454 left: targetRect.left,
455 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
456 });
457 $tooltip.addClass('below');
458 $tooltip.show();
459 }, function() {
460 // out
461 $tooltip.hide();
462 });
463 });
464
465 // Set up <h2> deeplinks
466 $('h2').click(function() {
467 var id = $(this).attr('id');
468 if (id) {
469 document.location.hash = id;
470 }
471 });
472
473 //Loads the +1 button
474 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
475 po.src = 'https://apis.google.com/js/plusone.js';
476 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
477
478
Scott Main3b90aff2013-08-01 18:09:35 -0700479 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700480 // which avoids the visible width from changing each time the bar appears
481 var $sidenav = $("#side-nav");
482 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700483
Scott Maine4d8f1b2012-06-21 18:03:05 -0700484 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
485
486
487 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700488
Scott Maine4d8f1b2012-06-21 18:03:05 -0700489 if ($(".scroll-pane").length > 1) {
490 // Check if there's a user preference for the panel heights
491 var cookieHeight = readCookie("reference_height");
492 if (cookieHeight) {
493 restoreHeight(cookieHeight);
494 }
495 }
Scott Main3b90aff2013-08-01 18:09:35 -0700496
Scott Main06f3f2c2014-05-30 11:23:00 -0700497 // Resize once loading is finished
Scott Maine4d8f1b2012-06-21 18:03:05 -0700498 resizeNav();
Scott Main06f3f2c2014-05-30 11:23:00 -0700499 // Check if there's an anchor that we need to scroll into view.
500 // A delay is needed, because some browsers do not immediately scroll down to the anchor
501 window.setTimeout(offsetScrollForSticky, 100);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700502
Scott Main015d6162013-01-29 09:01:52 -0800503 /* init the language selector based on user cookie for lang */
504 loadLangPref();
505 changeNavLang(getLangPref());
506
507 /* setup event handlers to ensure the overflow menu is visible while picking lang */
508 $("#language select")
509 .mousedown(function() {
510 $("div.morehover").addClass("hover"); })
511 .blur(function() {
512 $("div.morehover").removeClass("hover"); });
513
514 /* some global variable setup */
515 resizePackagesNav = $("#resize-packages-nav");
516 classesNav = $("#classes-nav");
517 devdocNav = $("#devdoc-nav");
518
519 var cookiePath = "";
520 if (location.href.indexOf("/reference/") != -1) {
521 cookiePath = "reference_";
522 } else if (location.href.indexOf("/guide/") != -1) {
523 cookiePath = "guide_";
524 } else if (location.href.indexOf("/tools/") != -1) {
525 cookiePath = "tools_";
526 } else if (location.href.indexOf("/training/") != -1) {
527 cookiePath = "training_";
528 } else if (location.href.indexOf("/design/") != -1) {
529 cookiePath = "design_";
530 } else if (location.href.indexOf("/distribute/") != -1) {
531 cookiePath = "distribute_";
532 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700533
534});
Scott Main7e447ed2013-02-19 17:22:37 -0800535// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700536
537
Scott Mainad08f072013-08-20 16:49:57 -0700538function initExpandableNavItems(rootTag) {
539 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
540 var section = $(this).closest('li.nav-section');
541 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700542 /* hide me and descendants */
543 section.find('ul').slideUp(250, function() {
544 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700545 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700546 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700547 resizeNav();
548 });
549 } else {
550 /* show me */
551 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700552 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700553 $others.removeClass('expanded').children('ul').slideUp(250);
554
555 // now expand me
556 section.closest('li').addClass('expanded');
557 section.children('ul').slideDown(250, function() {
558 resizeNav();
559 });
560 }
561 });
Scott Mainf0093852013-08-22 11:37:11 -0700562
563 // Stop expand/collapse behavior when clicking on nav section links
564 // (since we're navigating away from the page)
565 // This selector captures the first instance of <a>, but not those with "#" as the href.
566 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
567 window.location.href = $(this).attr('href');
568 return false;
569 });
Scott Mainad08f072013-08-20 16:49:57 -0700570}
571
Dirk Doughertyc3921652014-05-13 16:55:26 -0700572
573/** Create the list of breadcrumb links in the sticky header */
574function buildBreadcrumbs() {
575 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
576 // Add the secondary horizontal nav item, if provided
577 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
578 if ($selectedSecondNav.length) {
579 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
580 }
581 // Add the primary horizontal nav
582 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
583 // If there's no header nav item, use the logo link and title from alt text
584 if ($selectedFirstNav.length < 1) {
585 $selectedFirstNav = $("<a>")
586 .attr('href', $("div#header .logo a").attr('href'))
587 .text($("div#header .logo img").attr('alt'));
588 }
589 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
590}
591
592
593
Scott Maine624b3f2013-09-12 12:56:41 -0700594/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700595function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700596 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
597 if ($("ul#nav li.selected").length) {
598 unHighlightSidenav();
599 }
600 // look for URL in sidenav, including the hash
601 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
602
603 // If the selNavLink is still empty, look for it without the hash
604 if ($selNavLink.length == 0) {
605 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
606 }
607
Scott Mainf6145542013-04-01 16:38:11 -0700608 var $selListItem;
609 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700610 // Find this page's <li> in sidenav and set selected
611 $selListItem = $selNavLink.closest('li');
612 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700613
Scott Mainf6145542013-04-01 16:38:11 -0700614 // Traverse up the tree and expand all parent nav-sections
615 $selNavLink.parents('li.nav-section').each(function() {
616 $(this).addClass('expanded');
617 $(this).children('ul').show();
618 });
619 }
620}
621
Scott Maine624b3f2013-09-12 12:56:41 -0700622function unHighlightSidenav() {
623 $("ul#nav li.selected").removeClass("selected");
624 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
625}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700626
627function toggleFullscreen(enable) {
628 var delay = 20;
629 var enabled = true;
630 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
631 if (enable) {
632 // Currently NOT USING fullscreen; enable fullscreen
633 stylesheet.removeAttr('disabled');
634 $('#nav-swap .fullscreen').removeClass('disabled');
635 $('#devdoc-nav').css({left:''});
636 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
637 enabled = true;
638 } else {
639 // Currently USING fullscreen; disable fullscreen
640 stylesheet.attr('disabled', 'disabled');
641 $('#nav-swap .fullscreen').addClass('disabled');
642 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
643 enabled = false;
644 }
645 writeCookie("fullscreen", enabled, null, null);
646 setNavBarLeftPos();
647 resizeNav(delay);
648 updateSideNavPosition();
649 setTimeout(initSidenavHeightResize,delay);
650}
651
652
653function setNavBarLeftPos() {
654 navBarLeftPos = $('#body-content').offset().left;
655}
656
657
658function updateSideNavPosition() {
659 var newLeft = $(window).scrollLeft() - navBarLeftPos;
660 $('#devdoc-nav').css({left: -newLeft});
661 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
662}
Scott Main3b90aff2013-08-01 18:09:35 -0700663
Scott Maine4d8f1b2012-06-21 18:03:05 -0700664// TODO: use $(document).ready instead
665function addLoadEvent(newfun) {
666 var current = window.onload;
667 if (typeof window.onload != 'function') {
668 window.onload = newfun;
669 } else {
670 window.onload = function() {
671 current();
672 newfun();
673 }
674 }
675}
676
677var agent = navigator['userAgent'].toLowerCase();
678// If a mobile phone, set flag and do mobile setup
679if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
680 (agent.indexOf("blackberry") != -1) ||
681 (agent.indexOf("webos") != -1) ||
682 (agent.indexOf("mini") != -1)) { // opera mini browsers
683 isMobile = true;
684}
685
686
Scott Main498d7102013-08-21 15:47:38 -0700687$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700688 $("pre:not(.no-pretty-print)").addClass("prettyprint");
689 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700690});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700691
Scott Maine4d8f1b2012-06-21 18:03:05 -0700692
693
694
695/* ######### RESIZE THE SIDENAV HEIGHT ########## */
696
697function resizeNav(delay) {
698 var $nav = $("#devdoc-nav");
699 var $window = $(window);
700 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700701
Scott Maine4d8f1b2012-06-21 18:03:05 -0700702 // Get the height of entire window and the total header height.
703 // Then figure out based on scroll position whether the header is visible
704 var windowHeight = $window.height();
705 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700706 var headerHeight = $('#header-wrapper').outerHeight();
707 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700708
709 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700710 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700711 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700712 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700713
Scott Maine4d8f1b2012-06-21 18:03:05 -0700714 // Depending on whether the header is visible, set the side nav's height.
715 if (headerVisible) {
716 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700717 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700718 } else {
719 // Once header is off screen, the nav height is almost full window height
720 navHeight = windowHeight - topMargin;
721 }
Scott Main3b90aff2013-08-01 18:09:35 -0700722
723
724
Scott Maine4d8f1b2012-06-21 18:03:05 -0700725 $scrollPanes = $(".scroll-pane");
726 if ($scrollPanes.length > 1) {
727 // subtract the height of the api level widget and nav swapper from the available nav height
728 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700729
Scott Maine4d8f1b2012-06-21 18:03:05 -0700730 $("#swapper").css({height:navHeight + "px"});
731 if ($("#nav-tree").is(":visible")) {
732 $("#nav-tree").css({height:navHeight});
733 }
Scott Main3b90aff2013-08-01 18:09:35 -0700734
735 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700736 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700737
738 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700739 // then the package panel should begin to shrink
740 if (parseInt(classesHeight) <= 0) {
741 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
742 $("#packages-nav").css({height:navHeight - 10});
743 }
Scott Main3b90aff2013-08-01 18:09:35 -0700744
Scott Maine4d8f1b2012-06-21 18:03:05 -0700745 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
746 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700747
748
Scott Maine4d8f1b2012-06-21 18:03:05 -0700749 } else {
750 $nav.height(navHeight);
751 }
Scott Main3b90aff2013-08-01 18:09:35 -0700752
Scott Maine4d8f1b2012-06-21 18:03:05 -0700753 if (delay) {
754 updateFromResize = true;
755 delayedReInitScrollbars(delay);
756 } else {
757 reInitScrollbars();
758 }
Scott Main3b90aff2013-08-01 18:09:35 -0700759
Scott Maine4d8f1b2012-06-21 18:03:05 -0700760}
761
762var updateScrollbars = false;
763var updateFromResize = false;
764
765/* Re-initialize the scrollbars to account for changed nav size.
766 * This method postpones the actual update by a 1/4 second in order to optimize the
767 * scroll performance while the header is still visible, because re-initializing the
768 * scroll panes is an intensive process.
769 */
770function delayedReInitScrollbars(delay) {
771 // If we're scheduled for an update, but have received another resize request
772 // before the scheduled resize has occured, just ignore the new request
773 // (and wait for the scheduled one).
774 if (updateScrollbars && updateFromResize) {
775 updateFromResize = false;
776 return;
777 }
Scott Main3b90aff2013-08-01 18:09:35 -0700778
Scott Maine4d8f1b2012-06-21 18:03:05 -0700779 // We're scheduled for an update and the update request came from this method's setTimeout
780 if (updateScrollbars && !updateFromResize) {
781 reInitScrollbars();
782 updateScrollbars = false;
783 } else {
784 updateScrollbars = true;
785 updateFromResize = false;
786 setTimeout('delayedReInitScrollbars()',delay);
787 }
788}
789
790/* Re-initialize the scrollbars to account for changed nav size. */
791function reInitScrollbars() {
792 var pane = $(".scroll-pane").each(function(){
793 var api = $(this).data('jsp');
794 if (!api) { setTimeout(reInitScrollbars,300); return;}
795 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700796 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700797 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
798}
799
800
801/* Resize the height of the nav panels in the reference,
802 * and save the new size to a cookie */
803function saveNavPanels() {
804 var basePath = getBaseUri(location.pathname);
805 var section = basePath.substring(1,basePath.indexOf("/",1));
806 writeCookie("height", resizePackagesNav.css("height"), section, null);
807}
808
809
810
811function restoreHeight(packageHeight) {
812 $("#resize-packages-nav").height(packageHeight);
813 $("#packages-nav").height(packageHeight);
814 // var classesHeight = navHeight - packageHeight;
815 // $("#classes-nav").css({height:classesHeight});
816 // $("#classes-nav .jspContainer").css({height:classesHeight});
817}
818
819
820
821/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
822
823
824
825
826
Scott Main3b90aff2013-08-01 18:09:35 -0700827/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700828 This is called when the page finished loading. */
829function scrollIntoView(nav) {
830 var $nav = $("#"+nav);
831 var element = $nav.jScrollPane({/* ...settings... */});
832 var api = element.data('jsp');
833
834 if ($nav.is(':visible')) {
835 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700836 if ($selected.length == 0) {
837 // If no selected item found, exit
838 return;
839 }
Scott Main52dd2062013-08-15 12:22:28 -0700840 // get the selected item's offset from its container nav by measuring the item's offset
841 // relative to the document then subtract the container nav's offset relative to the document
842 var selectedOffset = $selected.offset().top - $nav.offset().top;
843 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
844 // if it's more than 80% down the nav
845 // scroll the item up by an amount equal to 80% the container nav's height
846 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700847 }
848 }
849}
850
851
852
853
854
855
856/* Show popup dialogs */
857function showDialog(id) {
858 $dialog = $("#"+id);
859 $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>');
860 $dialog.wrapInner('<div/>');
861 $dialog.removeClass("hide");
862}
863
864
865
866
867
868/* ######### COOKIES! ########## */
869
870function readCookie(cookie) {
871 var myCookie = cookie_namespace+"_"+cookie+"=";
872 if (document.cookie) {
873 var index = document.cookie.indexOf(myCookie);
874 if (index != -1) {
875 var valStart = index + myCookie.length;
876 var valEnd = document.cookie.indexOf(";", valStart);
877 if (valEnd == -1) {
878 valEnd = document.cookie.length;
879 }
880 var val = document.cookie.substring(valStart, valEnd);
881 return val;
882 }
883 }
884 return 0;
885}
886
887function writeCookie(cookie, val, section, expiration) {
888 if (val==undefined) return;
889 section = section == null ? "_" : "_"+section+"_";
890 if (expiration == null) {
891 var date = new Date();
892 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
893 expiration = date.toGMTString();
894 }
Scott Main3b90aff2013-08-01 18:09:35 -0700895 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700896 + "; expires=" + expiration+"; path=/";
897 document.cookie = cookieValue;
898}
899
900/* ######### END COOKIES! ########## */
901
902
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700903var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -0700904var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700905var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -0700906/* Sets the vertical scoll position at which the sticky bar should appear.
907 This method is called to reset the position when search results appear or hide */
908function setStickyTop() {
909 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
910}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700911
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700912/*
Scott Mainb16376f2014-05-21 20:35:47 -0700913 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -0700914 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700915$(window).scroll(function(event) {
916
917 setStickyTop();
918 var hiding = false;
919 var $stickyEl = $('#sticky-header');
920 var $menuEl = $('.menu-container');
921 // Exit if there's no sidenav
922 if ($('#side-nav').length == 0) return;
923 // Exit if the mouse target is a DIV, because that means the event is coming
924 // from a scrollable div and so there's no need to make adjustments to our layout
925 if ($(event.target).nodeName == "DIV") {
926 return;
927 }
928
929 var top = $(window).scrollTop();
930 // we set the navbar fixed when the scroll position is beyond the height of the site header...
931 var shouldBeSticky = top >= stickyTop;
932 // ... except if the document content is shorter than the sidenav height.
933 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
934 if ($("#doc-col").height() < $("#side-nav").height()) {
935 shouldBeSticky = false;
936 }
Scott Mainf5257812014-05-22 17:26:38 -0700937 // Account for horizontal scroll
938 var scrollLeft = $(window).scrollLeft();
939 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
940 if (sticky && (scrollLeft != prevScrollLeft)) {
941 updateSideNavPosition();
942 prevScrollLeft = scrollLeft;
943 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700944
945 // Don't continue if the header is sufficently far away
946 // (to avoid intensive resizing that slows scrolling)
947 if (sticky == shouldBeSticky) {
948 return;
949 }
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700950
951 // If sticky header visible and position is now near top, hide sticky
952 if (sticky && !shouldBeSticky) {
953 sticky = false;
954 hiding = true;
955 // make the sidenav static again
956 $('#devdoc-nav')
957 .removeClass('fixed')
958 .css({'width':'auto','margin':''})
959 .prependTo('#side-nav');
960 // delay hide the sticky
961 $menuEl.removeClass('sticky-menu');
962 $stickyEl.fadeOut(250);
963 hiding = false;
964
965 // update the sidenaav position for side scrolling
966 updateSideNavPosition();
967 } else if (!sticky && shouldBeSticky) {
968 sticky = true;
969 $stickyEl.fadeIn(10);
970 $menuEl.addClass('sticky-menu');
971
972 // make the sidenav fixed
973 var width = $('#devdoc-nav').width();
974 $('#devdoc-nav')
975 .addClass('fixed')
976 .css({'width':width+'px'})
977 .prependTo('#body-content');
978
979 // update the sidenaav position for side scrolling
980 updateSideNavPosition();
981
982 } else if (hiding && top < 15) {
983 $menuEl.removeClass('sticky-menu');
984 $stickyEl.hide();
985 hiding = false;
986 }
987 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
988});
989
990/*
991 * Manages secion card states and nav resize to conclude loading
992 */
Dirk Doughertyc3921652014-05-13 16:55:26 -0700993(function() {
994 $(document).ready(function() {
995
Dirk Doughertyc3921652014-05-13 16:55:26 -0700996 // Stack hover states
997 $('.section-card-menu').each(function(index, el) {
998 var height = $(el).height();
999 $(el).css({height:height+'px', position:'relative'});
1000 var $cardInfo = $(el).find('.card-info');
1001
1002 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
1003 });
1004
Dirk Doughertyc3921652014-05-13 16:55:26 -07001005 });
1006
1007})();
1008
Scott Maine4d8f1b2012-06-21 18:03:05 -07001009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
Scott Maind7026f72013-06-17 15:08:49 -07001022/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001023
1024
1025
1026
1027
1028function toggle(obj, slide) {
1029 var ul = $("ul:first", obj);
1030 var li = ul.parent();
1031 if (li.hasClass("closed")) {
1032 if (slide) {
1033 ul.slideDown("fast");
1034 } else {
1035 ul.show();
1036 }
1037 li.removeClass("closed");
1038 li.addClass("open");
1039 $(".toggle-img", li).attr("title", "hide pages");
1040 } else {
1041 ul.slideUp("fast");
1042 li.removeClass("open");
1043 li.addClass("closed");
1044 $(".toggle-img", li).attr("title", "show pages");
1045 }
1046}
1047
1048
Scott Maine4d8f1b2012-06-21 18:03:05 -07001049function buildToggleLists() {
1050 $(".toggle-list").each(
1051 function(i) {
1052 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1053 $(this).addClass("closed");
1054 });
1055}
1056
1057
1058
Scott Maind7026f72013-06-17 15:08:49 -07001059function hideNestedItems(list, toggle) {
1060 $list = $(list);
1061 // hide nested lists
1062 if($list.hasClass('showing')) {
1063 $("li ol", $list).hide('fast');
1064 $list.removeClass('showing');
1065 // show nested lists
1066 } else {
1067 $("li ol", $list).show('fast');
1068 $list.addClass('showing');
1069 }
1070 $(".more,.less",$(toggle)).toggle();
1071}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100/* REFERENCE NAV SWAP */
1101
1102
1103function getNavPref() {
1104 var v = readCookie('reference_nav');
1105 if (v != NAV_PREF_TREE) {
1106 v = NAV_PREF_PANELS;
1107 }
1108 return v;
1109}
1110
1111function chooseDefaultNav() {
1112 nav_pref = getNavPref();
1113 if (nav_pref == NAV_PREF_TREE) {
1114 $("#nav-panels").toggle();
1115 $("#panel-link").toggle();
1116 $("#nav-tree").toggle();
1117 $("#tree-link").toggle();
1118 }
1119}
1120
1121function swapNav() {
1122 if (nav_pref == NAV_PREF_TREE) {
1123 nav_pref = NAV_PREF_PANELS;
1124 } else {
1125 nav_pref = NAV_PREF_TREE;
1126 init_default_navtree(toRoot);
1127 }
1128 var date = new Date();
1129 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1130 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1131
1132 $("#nav-panels").toggle();
1133 $("#panel-link").toggle();
1134 $("#nav-tree").toggle();
1135 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001136
Scott Maine4d8f1b2012-06-21 18:03:05 -07001137 resizeNav();
1138
1139 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1140 $("#nav-tree .jspContainer:visible")
1141 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1142 // Another nasty hack to make the scrollbar appear now that we have height
1143 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001144
Scott Maine4d8f1b2012-06-21 18:03:05 -07001145 if ($("#nav-tree").is(':visible')) {
1146 scrollIntoView("nav-tree");
1147 } else {
1148 scrollIntoView("packages-nav");
1149 scrollIntoView("classes-nav");
1150 }
1151}
1152
1153
1154
Scott Mainf5089842012-08-14 16:31:07 -07001155/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001156/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001157/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001158
1159function getBaseUri(uri) {
1160 var intlUrl = (uri.substring(0,6) == "/intl/");
1161 if (intlUrl) {
1162 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1163 base = base.substring(base.indexOf('/')+1, base.length);
1164 //alert("intl, returning base url: /" + base);
1165 return ("/" + base);
1166 } else {
1167 //alert("not intl, returning uri as found.");
1168 return uri;
1169 }
1170}
1171
1172function requestAppendHL(uri) {
1173//append "?hl=<lang> to an outgoing request (such as to blog)
1174 var lang = getLangPref();
1175 if (lang) {
1176 var q = 'hl=' + lang;
1177 uri += '?' + q;
1178 window.location = uri;
1179 return false;
1180 } else {
1181 return true;
1182 }
1183}
1184
1185
Scott Maine4d8f1b2012-06-21 18:03:05 -07001186function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001187 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1188 $links.each(function(i){ // for each link with a translation
1189 var $link = $(this);
1190 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1191 // put the desired language from the attribute as the text
1192 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001193 }
Scott Main6eb95f12012-10-02 17:12:23 -07001194 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001195}
1196
Scott Main015d6162013-01-29 09:01:52 -08001197function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001198 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001199 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001200 // keep this for 50 years
1201 //alert("expires: " + expires)
1202 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001203
1204 // ####### TODO: Remove this condition once we're stable on devsite #######
1205 // This condition is only needed if we still need to support legacy GAE server
1206 if (devsite) {
1207 // Switch language when on Devsite server
1208 if (submit) {
1209 $("#setlang").submit();
1210 }
1211 } else {
1212 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001213 if (submit) {
1214 window.location = getBaseUri(location.pathname);
1215 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001216 }
1217}
1218
1219function loadLangPref() {
1220 var lang = readCookie("pref_lang");
1221 if (lang != 0) {
1222 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1223 }
1224}
1225
1226function getLangPref() {
1227 var lang = $("#language").find(":selected").attr("value");
1228 if (!lang) {
1229 lang = readCookie("pref_lang");
1230 }
1231 return (lang != 0) ? lang : 'en';
1232}
1233
1234/* ########## END LOCALIZATION ############ */
1235
1236
1237
1238
1239
1240
1241/* Used to hide and reveal supplemental content, such as long code samples.
1242 See the companion CSS in android-developer-docs.css */
1243function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001244 var div = $(obj).closest(".toggle-content");
1245 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001246 if (div.hasClass("closed")) { // if it's closed, open it
1247 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001248 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001249 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001250 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001251 + "assets/images/triangle-opened.png");
1252 } else { // if it's open, close it
1253 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001254 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001255 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001256 div.find(".toggle-content").removeClass("open").addClass("closed")
1257 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001258 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001259 + "assets/images/triangle-closed.png");
1260 });
1261 }
1262 return false;
1263}
Scott Mainf5089842012-08-14 16:31:07 -07001264
1265
Scott Maindb3678b2012-10-23 14:13:41 -07001266/* New version of expandable content */
1267function toggleExpandable(link,id) {
1268 if($(id).is(':visible')) {
1269 $(id).slideUp();
1270 $(link).removeClass('expanded');
1271 } else {
1272 $(id).slideDown();
1273 $(link).addClass('expanded');
1274 }
1275}
1276
1277function hideExpandable(ids) {
1278 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001279 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001280}
1281
Scott Mainf5089842012-08-14 16:31:07 -07001282
1283
1284
1285
Scott Main3b90aff2013-08-01 18:09:35 -07001286/*
Scott Mainf5089842012-08-14 16:31:07 -07001287 * Slideshow 1.0
1288 * Used on /index.html and /develop/index.html for carousel
1289 *
1290 * Sample usage:
1291 * HTML -
1292 * <div class="slideshow-container">
1293 * <a href="" class="slideshow-prev">Prev</a>
1294 * <a href="" class="slideshow-next">Next</a>
1295 * <ul>
1296 * <li class="item"><img src="images/marquee1.jpg"></li>
1297 * <li class="item"><img src="images/marquee2.jpg"></li>
1298 * <li class="item"><img src="images/marquee3.jpg"></li>
1299 * <li class="item"><img src="images/marquee4.jpg"></li>
1300 * </ul>
1301 * </div>
1302 *
1303 * <script type="text/javascript">
1304 * $('.slideshow-container').dacSlideshow({
1305 * auto: true,
1306 * btnPrev: '.slideshow-prev',
1307 * btnNext: '.slideshow-next'
1308 * });
1309 * </script>
1310 *
1311 * Options:
1312 * btnPrev: optional identifier for previous button
1313 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001314 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001315 * auto: whether or not to auto-proceed
1316 * speed: animation speed
1317 * autoTime: time between auto-rotation
1318 * easing: easing function for transition
1319 * start: item to select by default
1320 * scroll: direction to scroll in
1321 * pagination: whether or not to include dotted pagination
1322 *
1323 */
1324
1325 (function($) {
1326 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001327
Scott Mainf5089842012-08-14 16:31:07 -07001328 //Options - see above
1329 o = $.extend({
1330 btnPrev: null,
1331 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001332 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001333 auto: true,
1334 speed: 500,
1335 autoTime: 12000,
1336 easing: null,
1337 start: 0,
1338 scroll: 1,
1339 pagination: true
1340
1341 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001342
1343 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001344 return this.each(function() {
1345
1346 var running = false;
1347 var animCss = o.vertical ? "top" : "left";
1348 var sizeCss = o.vertical ? "height" : "width";
1349 var div = $(this);
1350 var ul = $("ul", div);
1351 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001352 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001353 var timer = null;
1354
1355 var li = $("li", ul);
1356 var itemLength = li.size();
1357 var curr = o.start;
1358
1359 li.css({float: o.vertical ? "none" : "left"});
1360 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1361 div.css({position: "relative", "z-index": "2", left: "0px"});
1362
1363 var liSize = o.vertical ? height(li) : width(li);
1364 var ulSize = liSize * itemLength;
1365 var divSize = liSize;
1366
1367 li.css({width: li.width(), height: li.height()});
1368 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1369
1370 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001371
Scott Mainf5089842012-08-14 16:31:07 -07001372 //Pagination
1373 if (o.pagination) {
1374 var pagination = $("<div class='pagination'></div>");
1375 var pag_ul = $("<ul></ul>");
1376 if (tl > 1) {
1377 for (var i=0;i<tl;i++) {
1378 var li = $("<li>"+i+"</li>");
1379 pag_ul.append(li);
1380 if (i==o.start) li.addClass('active');
1381 li.click(function() {
1382 go(parseInt($(this).text()));
1383 })
1384 }
1385 pagination.append(pag_ul);
1386 div.append(pagination);
1387 }
1388 }
Scott Main3b90aff2013-08-01 18:09:35 -07001389
Scott Mainf5089842012-08-14 16:31:07 -07001390 //Previous button
1391 if(o.btnPrev)
1392 $(o.btnPrev).click(function(e) {
1393 e.preventDefault();
1394 return go(curr-o.scroll);
1395 });
1396
1397 //Next button
1398 if(o.btnNext)
1399 $(o.btnNext).click(function(e) {
1400 e.preventDefault();
1401 return go(curr+o.scroll);
1402 });
Scott Maineb410352013-01-14 19:03:40 -08001403
1404 //Pause button
1405 if(o.btnPause)
1406 $(o.btnPause).click(function(e) {
1407 e.preventDefault();
1408 if ($(this).hasClass('paused')) {
1409 startRotateTimer();
1410 } else {
1411 pauseRotateTimer();
1412 }
1413 });
Scott Main3b90aff2013-08-01 18:09:35 -07001414
Scott Mainf5089842012-08-14 16:31:07 -07001415 //Auto rotation
1416 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001417
Scott Mainf5089842012-08-14 16:31:07 -07001418 function startRotateTimer() {
1419 clearInterval(timer);
1420 timer = setInterval(function() {
1421 if (curr == tl-1) {
1422 go(0);
1423 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001424 go(curr+o.scroll);
1425 }
Scott Mainf5089842012-08-14 16:31:07 -07001426 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001427 $(o.btnPause).removeClass('paused');
1428 }
1429
1430 function pauseRotateTimer() {
1431 clearInterval(timer);
1432 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001433 }
1434
1435 //Go to an item
1436 function go(to) {
1437 if(!running) {
1438
1439 if(to<0) {
1440 to = itemLength-1;
1441 } else if (to>itemLength-1) {
1442 to = 0;
1443 }
1444 curr = to;
1445
1446 running = true;
1447
1448 ul.animate(
1449 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1450 function() {
1451 running = false;
1452 }
1453 );
1454
1455 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1456 $( (curr-o.scroll<0 && o.btnPrev)
1457 ||
1458 (curr+o.scroll > itemLength && o.btnNext)
1459 ||
1460 []
1461 ).addClass("disabled");
1462
Scott Main3b90aff2013-08-01 18:09:35 -07001463
Scott Mainf5089842012-08-14 16:31:07 -07001464 var nav_items = $('li', pagination);
1465 nav_items.removeClass('active');
1466 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001467
Scott Mainf5089842012-08-14 16:31:07 -07001468
1469 }
1470 if(o.auto) startRotateTimer();
1471 return false;
1472 };
1473 });
1474 };
1475
1476 function css(el, prop) {
1477 return parseInt($.css(el[0], prop)) || 0;
1478 };
1479 function width(el) {
1480 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1481 };
1482 function height(el) {
1483 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1484 };
1485
1486 })(jQuery);
1487
1488
Scott Main3b90aff2013-08-01 18:09:35 -07001489/*
Scott Mainf5089842012-08-14 16:31:07 -07001490 * dacSlideshow 1.0
1491 * Used on develop/index.html for side-sliding tabs
1492 *
1493 * Sample usage:
1494 * HTML -
1495 * <div class="slideshow-container">
1496 * <a href="" class="slideshow-prev">Prev</a>
1497 * <a href="" class="slideshow-next">Next</a>
1498 * <ul>
1499 * <li class="item"><img src="images/marquee1.jpg"></li>
1500 * <li class="item"><img src="images/marquee2.jpg"></li>
1501 * <li class="item"><img src="images/marquee3.jpg"></li>
1502 * <li class="item"><img src="images/marquee4.jpg"></li>
1503 * </ul>
1504 * </div>
1505 *
1506 * <script type="text/javascript">
1507 * $('.slideshow-container').dacSlideshow({
1508 * auto: true,
1509 * btnPrev: '.slideshow-prev',
1510 * btnNext: '.slideshow-next'
1511 * });
1512 * </script>
1513 *
1514 * Options:
1515 * btnPrev: optional identifier for previous button
1516 * btnNext: optional identifier for next button
1517 * auto: whether or not to auto-proceed
1518 * speed: animation speed
1519 * autoTime: time between auto-rotation
1520 * easing: easing function for transition
1521 * start: item to select by default
1522 * scroll: direction to scroll in
1523 * pagination: whether or not to include dotted pagination
1524 *
1525 */
1526 (function($) {
1527 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001528
Scott Mainf5089842012-08-14 16:31:07 -07001529 //Options - see above
1530 o = $.extend({
1531 speed : 250,
1532 easing: null,
1533 nav_id: null,
1534 frame_id: null
1535 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001536
1537 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001538 return this.each(function() {
1539
1540 var curr = 0;
1541 var running = false;
1542 var animCss = "margin-left";
1543 var sizeCss = "width";
1544 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001545
Scott Mainf5089842012-08-14 16:31:07 -07001546 var nav = $(o.nav_id, div);
1547 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001548 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001549 var frame = div.find(o.frame_id);
1550 var content_width = $(frame).find('ul').width();
1551 //Buttons
1552 $(nav_li).click(function(e) {
1553 go($(nav_li).index($(this)));
1554 })
Scott Main3b90aff2013-08-01 18:09:35 -07001555
Scott Mainf5089842012-08-14 16:31:07 -07001556 //Go to an item
1557 function go(to) {
1558 if(!running) {
1559 curr = to;
1560 running = true;
1561
1562 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1563 function() {
1564 running = false;
1565 }
1566 );
1567
Scott Main3b90aff2013-08-01 18:09:35 -07001568
Scott Mainf5089842012-08-14 16:31:07 -07001569 nav_li.removeClass('active');
1570 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001571
Scott Mainf5089842012-08-14 16:31:07 -07001572
1573 }
1574 return false;
1575 };
1576 });
1577 };
1578
1579 function css(el, prop) {
1580 return parseInt($.css(el[0], prop)) || 0;
1581 };
1582 function width(el) {
1583 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1584 };
1585 function height(el) {
1586 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1587 };
1588
1589 })(jQuery);
1590
1591
1592
1593
1594
1595/* ######################################################## */
1596/* ################ SEARCH SUGGESTIONS ################## */
1597/* ######################################################## */
1598
1599
Scott Main7e447ed2013-02-19 17:22:37 -08001600
Scott Main0e76e7e2013-03-12 10:24:07 -07001601var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1602var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1603
Scott Mainf5089842012-08-14 16:31:07 -07001604var gMatches = new Array();
1605var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001606var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001607var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1608var gListLength = 0;
1609
1610
1611var gGoogleMatches = new Array();
1612var ROW_COUNT_GOOGLE = 15; // max number of results in list
1613var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001614
Scott Main0e76e7e2013-03-12 10:24:07 -07001615var gDocsMatches = new Array();
1616var ROW_COUNT_DOCS = 100; // max number of results in list
1617var gDocsListLength = 0;
1618
Scott Mainde295272013-03-25 15:48:35 -07001619function onSuggestionClick(link) {
1620 // When user clicks a suggested document, track it
1621 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1622 'from: ' + $("#search_autocomplete").val()]);
1623}
1624
Scott Mainf5089842012-08-14 16:31:07 -07001625function set_item_selected($li, selected)
1626{
1627 if (selected) {
1628 $li.attr('class','jd-autocomplete jd-selected');
1629 } else {
1630 $li.attr('class','jd-autocomplete');
1631 }
1632}
1633
1634function set_item_values(toroot, $li, match)
1635{
1636 var $link = $('a',$li);
1637 $link.html(match.__hilabel || match.label);
1638 $link.attr('href',toroot + match.link);
1639}
1640
Scott Main719acb42013-12-05 16:05:09 -08001641function set_item_values_jd(toroot, $li, match)
1642{
1643 var $link = $('a',$li);
1644 $link.html(match.title);
1645 $link.attr('href',toroot + match.url);
1646}
1647
Scott Main0e76e7e2013-03-12 10:24:07 -07001648function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001649 var $li = $("<li class='jd-autocomplete'></li>");
1650 $list.append($li);
1651
1652 $li.mousedown(function() {
1653 window.location = this.firstChild.getAttribute("href");
1654 });
1655 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001656 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001657 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001658 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1659 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001660 });
Scott Mainde295272013-03-25 15:48:35 -07001661 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001662 $li.attr('class','show-item');
1663 return $li;
1664}
1665
Scott Mainf5089842012-08-14 16:31:07 -07001666function sync_selection_table(toroot)
1667{
Scott Mainf5089842012-08-14 16:31:07 -07001668 var $li; //list item jquery object
1669 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001670
Scott Main0e76e7e2013-03-12 10:24:07 -07001671 // if there are NO results at all, hide all columns
1672 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1673 $('.suggest-card').hide(300);
1674 return;
1675 }
1676
1677 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001678 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001679 // reveal suggestion list
1680 $('.suggest-card.dummy').show();
1681 $('.suggest-card.reference').show();
1682 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001683
Scott Main0e76e7e2013-03-12 10:24:07 -07001684 // reset the lists
1685 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001686
Scott Main0e76e7e2013-03-12 10:24:07 -07001687 // ########### ANDROID RESULTS #############
1688 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001689
Scott Main0e76e7e2013-03-12 10:24:07 -07001690 // determine android results to show
1691 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1692 gMatches.length : ROW_COUNT_FRAMEWORK;
1693 for (i=0; i<gListLength; i++) {
1694 var $li = new_suggestion($(".suggest-card.reference ul"));
1695 set_item_values(toroot, $li, gMatches[i]);
1696 set_item_selected($li, i == gSelectedIndex);
1697 }
1698 }
Scott Main7e447ed2013-02-19 17:22:37 -08001699
Scott Main0e76e7e2013-03-12 10:24:07 -07001700 // ########### GOOGLE RESULTS #############
1701 if (gGoogleMatches.length > 0) {
1702 // show header for list
1703 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001704
Scott Main0e76e7e2013-03-12 10:24:07 -07001705 // determine google results to show
1706 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1707 for (i=0; i<gGoogleListLength; i++) {
1708 var $li = new_suggestion($(".suggest-card.reference ul"));
1709 set_item_values(toroot, $li, gGoogleMatches[i]);
1710 set_item_selected($li, i == gSelectedIndex);
1711 }
1712 }
Scott Mainf5089842012-08-14 16:31:07 -07001713 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001714 $('.suggest-card.reference').hide();
1715 $('.suggest-card.dummy').hide();
1716 }
1717
1718 // ########### JD DOC RESULTS #############
1719 if (gDocsMatches.length > 0) {
1720 // reset the lists
1721 $(".search_filtered_wrapper.docs li").remove();
1722
1723 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001724 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1725 // The order must match the reverse order that each section appears as a card in
1726 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001727 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1728 for (i=0; i<gDocsListLength; i++) {
1729 var sugg = gDocsMatches[i];
1730 var $li;
1731 if (sugg.type == "design") {
1732 $li = new_suggestion($(".suggest-card.design ul"));
1733 } else
1734 if (sugg.type == "distribute") {
1735 $li = new_suggestion($(".suggest-card.distribute ul"));
1736 } else
Scott Main719acb42013-12-05 16:05:09 -08001737 if (sugg.type == "samples") {
1738 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1739 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001740 if (sugg.type == "training") {
1741 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1742 } else
Scott Main719acb42013-12-05 16:05:09 -08001743 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001744 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1745 } else {
1746 continue;
1747 }
1748
Scott Main719acb42013-12-05 16:05:09 -08001749 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001750 set_item_selected($li, i == gSelectedIndex);
1751 }
1752
1753 // add heading and show or hide card
1754 if ($(".suggest-card.design li").length > 0) {
1755 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1756 $(".suggest-card.design").show(300);
1757 } else {
1758 $('.suggest-card.design').hide(300);
1759 }
1760 if ($(".suggest-card.distribute li").length > 0) {
1761 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1762 $(".suggest-card.distribute").show(300);
1763 } else {
1764 $('.suggest-card.distribute').hide(300);
1765 }
1766 if ($(".child-card.guides li").length > 0) {
1767 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1768 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1769 }
1770 if ($(".child-card.training li").length > 0) {
1771 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1772 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1773 }
Scott Main719acb42013-12-05 16:05:09 -08001774 if ($(".child-card.samples li").length > 0) {
1775 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1776 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1777 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001778
1779 if ($(".suggest-card.develop li").length > 0) {
1780 $(".suggest-card.develop").show(300);
1781 } else {
1782 $('.suggest-card.develop').hide(300);
1783 }
1784
1785 } else {
1786 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001787 }
1788}
1789
Scott Main0e76e7e2013-03-12 10:24:07 -07001790/** Called by the search input's onkeydown and onkeyup events.
1791 * Handles navigation with keyboard arrows, Enter key to invoke search,
1792 * otherwise invokes search suggestions on key-up event.
1793 * @param e The JS event
1794 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001795 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001796 * @returns True if the event should bubble up
1797 */
Scott Mainf5089842012-08-14 16:31:07 -07001798function search_changed(e, kd, toroot)
1799{
Scott Main719acb42013-12-05 16:05:09 -08001800 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001801 var search = document.getElementById("search_autocomplete");
1802 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001803 // get the ul hosting the currently selected item
1804 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1805 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1806 var $selectedUl = $columns[gSelectedColumn];
1807
Scott Mainf5089842012-08-14 16:31:07 -07001808 // show/hide the close button
1809 if (text != '') {
1810 $(".search .close").removeClass("hide");
1811 } else {
1812 $(".search .close").addClass("hide");
1813 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001814 // 27 = esc
1815 if (e.keyCode == 27) {
1816 // close all search results
1817 if (kd) $('.search .close').trigger('click');
1818 return true;
1819 }
Scott Mainf5089842012-08-14 16:31:07 -07001820 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001821 else if (e.keyCode == 13) {
1822 if (gSelectedIndex < 0) {
1823 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001824 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1825 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001826 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001827 return true;
1828 } else {
1829 // otherwise, results are already showing, so allow ajax to auto refresh the results
1830 // and ignore this Enter press to avoid the reload.
1831 return false;
1832 }
1833 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001834 // click the link corresponding to selected item
1835 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001836 return false;
1837 }
1838 }
Scott Mainb16376f2014-05-21 20:35:47 -07001839 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001840 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001841 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001842 if ((sticky ) && (search.value != "")) {
1843 $('body,html').animate({scrollTop:0}, '500', 'swing');
1844 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001845 return true;
1846 }
1847 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001848 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001849 // if the next item is a header, skip it
1850 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001851 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001852 }
1853 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001854 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001855 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001856 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1857 // If user reaches top, reset selected column
1858 if (gSelectedIndex < 0) {
1859 gSelectedColumn = -1;
1860 }
Scott Mainf5089842012-08-14 16:31:07 -07001861 }
1862 return false;
1863 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001864 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001865 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001866 // if the next item is a header, skip it
1867 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001868 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001869 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001870 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1871 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1872 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001873 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001874 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001875 }
1876 return false;
1877 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001878 // Consider left/right arrow navigation
1879 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1880 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1881 // 37 LEFT ARROW
1882 // go left only if current column is not left-most column (last column)
1883 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1884 $('li', $selectedUl).removeClass('jd-selected');
1885 gSelectedColumn++;
1886 $selectedUl = $columns[gSelectedColumn];
1887 // keep or reset the selected item to last item as appropriate
1888 gSelectedIndex = gSelectedIndex >
1889 $("li", $selectedUl).length-1 ?
1890 $("li", $selectedUl).length-1 : gSelectedIndex;
1891 // if the corresponding item is a header, move down
1892 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1893 gSelectedIndex++;
1894 }
Scott Main3b90aff2013-08-01 18:09:35 -07001895 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001896 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1897 return false;
1898 }
1899 // 39 RIGHT ARROW
1900 // go right only if current column is not the right-most column (first column)
1901 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1902 $('li', $selectedUl).removeClass('jd-selected');
1903 gSelectedColumn--;
1904 $selectedUl = $columns[gSelectedColumn];
1905 // keep or reset the selected item to last item as appropriate
1906 gSelectedIndex = gSelectedIndex >
1907 $("li", $selectedUl).length-1 ?
1908 $("li", $selectedUl).length-1 : gSelectedIndex;
1909 // if the corresponding item is a header, move down
1910 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1911 gSelectedIndex++;
1912 }
Scott Main3b90aff2013-08-01 18:09:35 -07001913 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001914 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1915 return false;
1916 }
1917 }
1918
Scott Main719acb42013-12-05 16:05:09 -08001919 // if key-up event and not arrow down/up/left/right,
1920 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001921 else if (!kd && (e.keyCode != 40)
1922 && (e.keyCode != 38)
1923 && (e.keyCode != 37)
1924 && (e.keyCode != 39)) {
1925 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001926 gMatches = new Array();
1927 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001928 gGoogleMatches = new Array();
1929 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001930 gDocsMatches = new Array();
1931 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001932
1933 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001934 for (var i=0; i<DATA.length; i++) {
1935 var s = DATA[i];
1936 if (text.length != 0 &&
1937 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1938 gMatches[matchedCount] = s;
1939 matchedCount++;
1940 }
1941 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001942 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001943 for (var i=0; i<gMatches.length; i++) {
1944 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001945 }
1946
1947
1948 // Search for Google matches
1949 for (var i=0; i<GOOGLE_DATA.length; i++) {
1950 var s = GOOGLE_DATA[i];
1951 if (text.length != 0 &&
1952 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1953 gGoogleMatches[matchedCountGoogle] = s;
1954 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001955 }
1956 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001957 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001958 for (var i=0; i<gGoogleMatches.length; i++) {
1959 var s = gGoogleMatches[i];
1960 }
1961
Scott Mainf5089842012-08-14 16:31:07 -07001962 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001963
1964
1965
Scott Main719acb42013-12-05 16:05:09 -08001966 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07001967 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08001968 // Regex to match only the beginning of a word
1969 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1970
1971
1972 // Search for Training classes
1973 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001974 // current search comparison, with counters for tag and title,
1975 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08001976 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07001977 s.matched_tag = 0;
1978 s.matched_title = 0;
1979 var matched = false;
1980
1981 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08001982 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001983 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08001984 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001985 matched = true;
1986 s.matched_tag = j + 1; // add 1 to index position
1987 }
1988 }
Scott Main719acb42013-12-05 16:05:09 -08001989 // Don't consider doc title for lessons (only for class landing pages),
1990 // unless the lesson has a tag that already matches
1991 if ((s.lang == currentLang) &&
1992 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001993 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08001994 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001995 matched = true;
1996 s.matched_title = 1;
1997 }
1998 }
1999 if (matched) {
2000 gDocsMatches[matchedCountDocs] = s;
2001 matchedCountDocs++;
2002 }
2003 }
Scott Main719acb42013-12-05 16:05:09 -08002004
2005
2006 // Search for API Guides
2007 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
2008 // current search comparison, with counters for tag and title,
2009 // used later to improve ranking
2010 var s = GUIDE_RESOURCES[i];
2011 s.matched_tag = 0;
2012 s.matched_title = 0;
2013 var matched = false;
2014
2015 // Check if query matches any tags; work backwards toward 1 to assist ranking
2016 for (var j = s.keywords.length - 1; j >= 0; j--) {
2017 // it matches a tag
2018 if (s.keywords[j].toLowerCase().match(textRegex)) {
2019 matched = true;
2020 s.matched_tag = j + 1; // add 1 to index position
2021 }
2022 }
2023 // Check if query matches the doc title, but only for current language
2024 if (s.lang == currentLang) {
2025 // if query matches the doc title
2026 if (s.title.toLowerCase().match(textRegex)) {
2027 matched = true;
2028 s.matched_title = 1;
2029 }
2030 }
2031 if (matched) {
2032 gDocsMatches[matchedCountDocs] = s;
2033 matchedCountDocs++;
2034 }
2035 }
2036
2037
2038 // Search for Tools Guides
2039 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2040 // current search comparison, with counters for tag and title,
2041 // used later to improve ranking
2042 var s = TOOLS_RESOURCES[i];
2043 s.matched_tag = 0;
2044 s.matched_title = 0;
2045 var matched = false;
2046
2047 // Check if query matches any tags; work backwards toward 1 to assist ranking
2048 for (var j = s.keywords.length - 1; j >= 0; j--) {
2049 // it matches a tag
2050 if (s.keywords[j].toLowerCase().match(textRegex)) {
2051 matched = true;
2052 s.matched_tag = j + 1; // add 1 to index position
2053 }
2054 }
2055 // Check if query matches the doc title, but only for current language
2056 if (s.lang == currentLang) {
2057 // if query matches the doc title
2058 if (s.title.toLowerCase().match(textRegex)) {
2059 matched = true;
2060 s.matched_title = 1;
2061 }
2062 }
2063 if (matched) {
2064 gDocsMatches[matchedCountDocs] = s;
2065 matchedCountDocs++;
2066 }
2067 }
2068
2069
2070 // Search for About docs
2071 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2072 // current search comparison, with counters for tag and title,
2073 // used later to improve ranking
2074 var s = ABOUT_RESOURCES[i];
2075 s.matched_tag = 0;
2076 s.matched_title = 0;
2077 var matched = false;
2078
2079 // Check if query matches any tags; work backwards toward 1 to assist ranking
2080 for (var j = s.keywords.length - 1; j >= 0; j--) {
2081 // it matches a tag
2082 if (s.keywords[j].toLowerCase().match(textRegex)) {
2083 matched = true;
2084 s.matched_tag = j + 1; // add 1 to index position
2085 }
2086 }
2087 // Check if query matches the doc title, but only for current language
2088 if (s.lang == currentLang) {
2089 // if query matches the doc title
2090 if (s.title.toLowerCase().match(textRegex)) {
2091 matched = true;
2092 s.matched_title = 1;
2093 }
2094 }
2095 if (matched) {
2096 gDocsMatches[matchedCountDocs] = s;
2097 matchedCountDocs++;
2098 }
2099 }
2100
2101
2102 // Search for Design guides
2103 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2104 // current search comparison, with counters for tag and title,
2105 // used later to improve ranking
2106 var s = DESIGN_RESOURCES[i];
2107 s.matched_tag = 0;
2108 s.matched_title = 0;
2109 var matched = false;
2110
2111 // Check if query matches any tags; work backwards toward 1 to assist ranking
2112 for (var j = s.keywords.length - 1; j >= 0; j--) {
2113 // it matches a tag
2114 if (s.keywords[j].toLowerCase().match(textRegex)) {
2115 matched = true;
2116 s.matched_tag = j + 1; // add 1 to index position
2117 }
2118 }
2119 // Check if query matches the doc title, but only for current language
2120 if (s.lang == currentLang) {
2121 // if query matches the doc title
2122 if (s.title.toLowerCase().match(textRegex)) {
2123 matched = true;
2124 s.matched_title = 1;
2125 }
2126 }
2127 if (matched) {
2128 gDocsMatches[matchedCountDocs] = s;
2129 matchedCountDocs++;
2130 }
2131 }
2132
2133
2134 // Search for Distribute guides
2135 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2136 // current search comparison, with counters for tag and title,
2137 // used later to improve ranking
2138 var s = DISTRIBUTE_RESOURCES[i];
2139 s.matched_tag = 0;
2140 s.matched_title = 0;
2141 var matched = false;
2142
2143 // Check if query matches any tags; work backwards toward 1 to assist ranking
2144 for (var j = s.keywords.length - 1; j >= 0; j--) {
2145 // it matches a tag
2146 if (s.keywords[j].toLowerCase().match(textRegex)) {
2147 matched = true;
2148 s.matched_tag = j + 1; // add 1 to index position
2149 }
2150 }
2151 // Check if query matches the doc title, but only for current language
2152 if (s.lang == currentLang) {
2153 // if query matches the doc title
2154 if (s.title.toLowerCase().match(textRegex)) {
2155 matched = true;
2156 s.matched_title = 1;
2157 }
2158 }
2159 if (matched) {
2160 gDocsMatches[matchedCountDocs] = s;
2161 matchedCountDocs++;
2162 }
2163 }
2164
2165
2166 // Search for Google guides
2167 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2168 // current search comparison, with counters for tag and title,
2169 // used later to improve ranking
2170 var s = GOOGLE_RESOURCES[i];
2171 s.matched_tag = 0;
2172 s.matched_title = 0;
2173 var matched = false;
2174
2175 // Check if query matches any tags; work backwards toward 1 to assist ranking
2176 for (var j = s.keywords.length - 1; j >= 0; j--) {
2177 // it matches a tag
2178 if (s.keywords[j].toLowerCase().match(textRegex)) {
2179 matched = true;
2180 s.matched_tag = j + 1; // add 1 to index position
2181 }
2182 }
2183 // Check if query matches the doc title, but only for current language
2184 if (s.lang == currentLang) {
2185 // if query matches the doc title
2186 if (s.title.toLowerCase().match(textRegex)) {
2187 matched = true;
2188 s.matched_title = 1;
2189 }
2190 }
2191 if (matched) {
2192 gDocsMatches[matchedCountDocs] = s;
2193 matchedCountDocs++;
2194 }
2195 }
2196
2197
2198 // Search for Samples
2199 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2200 // current search comparison, with counters for tag and title,
2201 // used later to improve ranking
2202 var s = SAMPLES_RESOURCES[i];
2203 s.matched_tag = 0;
2204 s.matched_title = 0;
2205 var matched = false;
2206 // Check if query matches any tags; work backwards toward 1 to assist ranking
2207 for (var j = s.keywords.length - 1; j >= 0; j--) {
2208 // it matches a tag
2209 if (s.keywords[j].toLowerCase().match(textRegex)) {
2210 matched = true;
2211 s.matched_tag = j + 1; // add 1 to index position
2212 }
2213 }
2214 // Check if query matches the doc title, but only for current language
2215 if (s.lang == currentLang) {
2216 // if query matches the doc title.t
2217 if (s.title.toLowerCase().match(textRegex)) {
2218 matched = true;
2219 s.matched_title = 1;
2220 }
2221 }
2222 if (matched) {
2223 gDocsMatches[matchedCountDocs] = s;
2224 matchedCountDocs++;
2225 }
2226 }
2227
2228 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002229 rank_autocomplete_doc_results(text, gDocsMatches);
2230 }
2231
2232 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002233 sync_selection_table(toroot);
2234 return true; // allow the event to bubble up to the search api
2235 }
2236}
2237
Scott Main0e76e7e2013-03-12 10:24:07 -07002238/* Order the jd doc result list based on match quality */
2239function rank_autocomplete_doc_results(query, matches) {
2240 query = query || '';
2241 if (!matches || !matches.length)
2242 return;
2243
2244 var _resultScoreFn = function(match) {
2245 var score = 1.0;
2246
2247 // if the query matched a tag
2248 if (match.matched_tag > 0) {
2249 // multiply score by factor relative to position in tags list (max of 3)
2250 score *= 3 / match.matched_tag;
2251
2252 // if it also matched the title
2253 if (match.matched_title > 0) {
2254 score *= 2;
2255 }
2256 } else if (match.matched_title > 0) {
2257 score *= 3;
2258 }
2259
2260 return score;
2261 };
2262
2263 for (var i=0; i<matches.length; i++) {
2264 matches[i].__resultScore = _resultScoreFn(matches[i]);
2265 }
2266
2267 matches.sort(function(a,b){
2268 var n = b.__resultScore - a.__resultScore;
2269 if (n == 0) // lexicographical sort if scores are the same
2270 n = (a.label < b.label) ? -1 : 1;
2271 return n;
2272 });
2273}
2274
Scott Main7e447ed2013-02-19 17:22:37 -08002275/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002276function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002277 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002278 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002279 return;
2280
2281 // helper function that gets the last occurence index of the given regex
2282 // in the given string, or -1 if not found
2283 var _lastSearch = function(s, re) {
2284 if (s == '')
2285 return -1;
2286 var l = -1;
2287 var tmp;
2288 while ((tmp = s.search(re)) >= 0) {
2289 if (l < 0) l = 0;
2290 l += tmp;
2291 s = s.substr(tmp + 1);
2292 }
2293 return l;
2294 };
2295
2296 // helper function that counts the occurrences of a given character in
2297 // a given string
2298 var _countChar = function(s, c) {
2299 var n = 0;
2300 for (var i=0; i<s.length; i++)
2301 if (s.charAt(i) == c) ++n;
2302 return n;
2303 };
2304
2305 var queryLower = query.toLowerCase();
2306 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2307 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2308 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2309
2310 var _resultScoreFn = function(result) {
2311 // scores are calculated based on exact and prefix matches,
2312 // and then number of path separators (dots) from the last
2313 // match (i.e. favoring classes and deep package names)
2314 var score = 1.0;
2315 var labelLower = result.label.toLowerCase();
2316 var t;
2317 t = _lastSearch(labelLower, partExactAlnumRE);
2318 if (t >= 0) {
2319 // exact part match
2320 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2321 score *= 200 / (partsAfter + 1);
2322 } else {
2323 t = _lastSearch(labelLower, partPrefixAlnumRE);
2324 if (t >= 0) {
2325 // part prefix match
2326 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2327 score *= 20 / (partsAfter + 1);
2328 }
2329 }
2330
2331 return score;
2332 };
2333
Scott Main7e447ed2013-02-19 17:22:37 -08002334 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002335 // if the API is deprecated, default score is 0; otherwise, perform scoring
2336 if (matches[i].deprecated == "true") {
2337 matches[i].__resultScore = 0;
2338 } else {
2339 matches[i].__resultScore = _resultScoreFn(matches[i]);
2340 }
Scott Mainf5089842012-08-14 16:31:07 -07002341 }
2342
Scott Main7e447ed2013-02-19 17:22:37 -08002343 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002344 var n = b.__resultScore - a.__resultScore;
2345 if (n == 0) // lexicographical sort if scores are the same
2346 n = (a.label < b.label) ? -1 : 1;
2347 return n;
2348 });
2349}
2350
Scott Main7e447ed2013-02-19 17:22:37 -08002351/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002352function highlight_autocomplete_result_labels(query) {
2353 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002354 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002355 return;
2356
2357 var queryLower = query.toLowerCase();
2358 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2359 var queryRE = new RegExp(
2360 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2361 for (var i=0; i<gMatches.length; i++) {
2362 gMatches[i].__hilabel = gMatches[i].label.replace(
2363 queryRE, '<b>$1</b>');
2364 }
Scott Main7e447ed2013-02-19 17:22:37 -08002365 for (var i=0; i<gGoogleMatches.length; i++) {
2366 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2367 queryRE, '<b>$1</b>');
2368 }
Scott Mainf5089842012-08-14 16:31:07 -07002369}
2370
2371function search_focus_changed(obj, focused)
2372{
Scott Main3b90aff2013-08-01 18:09:35 -07002373 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002374 if(obj.value == ""){
2375 $(".search .close").addClass("hide");
2376 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002377 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002378 }
2379}
2380
2381function submit_search() {
2382 var query = document.getElementById('search_autocomplete').value;
2383 location.hash = 'q=' + query;
2384 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002385 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002386 return false;
2387}
2388
2389
2390function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002391 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002392 $(".search .close").addClass("hide");
2393 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002394
Scott Mainf5089842012-08-14 16:31:07 -07002395 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002396
Scott Mainf5089842012-08-14 16:31:07 -07002397 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2398 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002399
2400 // forcefully regain key-up event control (previously jacked by search api)
2401 $("#search_autocomplete").keyup(function(event) {
2402 return search_changed(event, false, toRoot);
2403 });
2404
Scott Mainf5089842012-08-14 16:31:07 -07002405 return false;
2406}
2407
2408
2409
2410/* ########################################################## */
2411/* ################ CUSTOM SEARCH ENGINE ################## */
2412/* ########################################################## */
2413
Scott Mainf5089842012-08-14 16:31:07 -07002414var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002415google.load('search', '1', {"callback" : function() {
2416 searchControl = new google.search.SearchControl();
2417 } });
Scott Mainf5089842012-08-14 16:31:07 -07002418
2419function loadSearchResults() {
2420 document.getElementById("search_autocomplete").style.color = "#000";
2421
Scott Mainf5089842012-08-14 16:31:07 -07002422 searchControl = new google.search.SearchControl();
2423
2424 // use our existing search form and use tabs when multiple searchers are used
2425 drawOptions = new google.search.DrawOptions();
2426 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2427 drawOptions.setInput(document.getElementById("search_autocomplete"));
2428
2429 // configure search result options
2430 searchOptions = new google.search.SearcherOptions();
2431 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2432
2433 // configure each of the searchers, for each tab
2434 devSiteSearcher = new google.search.WebSearch();
2435 devSiteSearcher.setUserDefinedLabel("All");
2436 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2437
2438 designSearcher = new google.search.WebSearch();
2439 designSearcher.setUserDefinedLabel("Design");
2440 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2441
2442 trainingSearcher = new google.search.WebSearch();
2443 trainingSearcher.setUserDefinedLabel("Training");
2444 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2445
2446 guidesSearcher = new google.search.WebSearch();
2447 guidesSearcher.setUserDefinedLabel("Guides");
2448 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2449
2450 referenceSearcher = new google.search.WebSearch();
2451 referenceSearcher.setUserDefinedLabel("Reference");
2452 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2453
Scott Maindf08ada2012-12-03 08:54:37 -08002454 googleSearcher = new google.search.WebSearch();
2455 googleSearcher.setUserDefinedLabel("Google Services");
2456 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2457
Scott Mainf5089842012-08-14 16:31:07 -07002458 blogSearcher = new google.search.WebSearch();
2459 blogSearcher.setUserDefinedLabel("Blog");
2460 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2461
2462 // add each searcher to the search control
2463 searchControl.addSearcher(devSiteSearcher, searchOptions);
2464 searchControl.addSearcher(designSearcher, searchOptions);
2465 searchControl.addSearcher(trainingSearcher, searchOptions);
2466 searchControl.addSearcher(guidesSearcher, searchOptions);
2467 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002468 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002469 searchControl.addSearcher(blogSearcher, searchOptions);
2470
2471 // configure result options
2472 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2473 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2474 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2475 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2476
2477 // upon ajax search, refresh the url and search title
2478 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2479 updateResultTitle(query);
2480 var query = document.getElementById('search_autocomplete').value;
2481 location.hash = 'q=' + query;
2482 });
2483
Scott Mainde295272013-03-25 15:48:35 -07002484 // once search results load, set up click listeners
2485 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2486 addResultClickListeners();
2487 });
2488
Scott Mainf5089842012-08-14 16:31:07 -07002489 // draw the search results box
2490 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2491
2492 // get query and execute the search
2493 searchControl.execute(decodeURI(getQuery(location.hash)));
2494
2495 document.getElementById("search_autocomplete").focus();
2496 addTabListeners();
2497}
2498// End of loadSearchResults
2499
2500
2501google.setOnLoadCallback(function(){
2502 if (location.hash.indexOf("q=") == -1) {
2503 // if there's no query in the url, don't search and make sure results are hidden
2504 $('#searchResults').hide();
2505 return;
2506 } else {
2507 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002508 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002509 $(".search .close").removeClass("hide");
2510 loadSearchResults();
2511 }
2512}, true);
2513
Scott Mainb16376f2014-05-21 20:35:47 -07002514/* Adjust the scroll position to account for sticky header, only if the hash matches an id */
2515function offsetScrollForSticky() {
2516 var hash = location.hash;
2517 var $matchingElement = $(hash);
2518 // If there's no element with the hash as an ID, then look for an <a name=''> with it.
2519 if ($matchingElement.length < 1) {
2520 $matchingElement = $('a[name="' + hash.substr(1) + '"]');
2521 }
2522 // Sanity check that hash is a real hash and that there's an element with that ID on the page
2523 if ((hash.indexOf("#") == 0) && $matchingElement.length) {
2524 // If the position of the target element is near the top of the page (<20px, where we expect it
2525 // to be because we need to move it down 60px to become in view), then move it down 60px
2526 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2527 $(window).scrollTop($(window).scrollTop() - 60);
Scott Mainb16376f2014-05-21 20:35:47 -07002528 }
2529 }
2530}
2531
Scott Mainf5089842012-08-14 16:31:07 -07002532// when an event on the browser history occurs (back, forward, load) requery hash and do search
2533$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002534 // If the hash isn't a search query or there's an error in the query,
2535 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002536 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2537 // If the results pane is open, close it.
2538 if (!$("#searchResults").is(":hidden")) {
2539 hideResults();
2540 }
Scott Mainb16376f2014-05-21 20:35:47 -07002541 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002542 return;
2543 }
2544
2545 // Otherwise, we have a search to do
2546 var query = decodeURI(getQuery(location.hash));
2547 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002548 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002549 $("#search_autocomplete").focus();
2550 $(".search .close").removeClass("hide");
2551
2552 updateResultTitle(query);
2553});
2554
2555function updateResultTitle(query) {
2556 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2557}
2558
2559// forcefully regain key-up event control (previously jacked by search api)
2560$("#search_autocomplete").keyup(function(event) {
2561 return search_changed(event, false, toRoot);
2562});
2563
2564// add event listeners to each tab so we can track the browser history
2565function addTabListeners() {
2566 var tabHeaders = $(".gsc-tabHeader");
2567 for (var i = 0; i < tabHeaders.length; i++) {
2568 $(tabHeaders[i]).attr("id",i).click(function() {
2569 /*
2570 // make a copy of the page numbers for the search left pane
2571 setTimeout(function() {
2572 // remove any residual page numbers
2573 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002574 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002575 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002576 // and because we're going to remove it (previous line),
2577 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002578 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2579 .clone().appendTo('#searchResults .gsc-tabsArea');
2580 }, 200);
2581 */
2582 });
2583 }
2584 setTimeout(function(){$(tabHeaders[0]).click()},200);
2585}
2586
Scott Mainde295272013-03-25 15:48:35 -07002587// add analytics tracking events to each result link
2588function addResultClickListeners() {
2589 $("#searchResults a.gs-title").each(function(index, link) {
2590 // When user clicks enter for Google search results, track it
2591 $(link).click(function() {
2592 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2593 'from: ' + $("#search_autocomplete").val()]);
2594 });
2595 });
2596}
2597
Scott Mainf5089842012-08-14 16:31:07 -07002598
2599function getQuery(hash) {
2600 var queryParts = hash.split('=');
2601 return queryParts[1];
2602}
2603
2604/* returns the given string with all HTML brackets converted to entities
2605 TODO: move this to the site's JS library */
2606function escapeHTML(string) {
2607 return string.replace(/</g,"&lt;")
2608 .replace(/>/g,"&gt;");
2609}
2610
2611
2612
2613
2614
2615
2616
2617/* ######################################################## */
2618/* ################# JAVADOC REFERENCE ################### */
2619/* ######################################################## */
2620
Scott Main65511c02012-09-07 15:51:32 -07002621/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002622if (location.pathname.indexOf("/reference") == 0) {
2623 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2624 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2625 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002626 $(document).ready(function() {
2627 // init available apis based on user pref
2628 changeApiLevel();
2629 initSidenavHeightResize()
2630 });
2631 }
Scott Main65511c02012-09-07 15:51:32 -07002632}
Scott Mainf5089842012-08-14 16:31:07 -07002633
2634var API_LEVEL_COOKIE = "api_level";
2635var minLevel = 1;
2636var maxLevel = 1;
2637
2638/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002639
Scott Mainf5089842012-08-14 16:31:07 -07002640 function initSidenavHeightResize() {
2641 // Change the drag bar size to nicely fit the scrollbar positions
2642 var $dragBar = $(".ui-resizable-s");
2643 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002644
2645 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002646 containment: "#nav-panels",
2647 handles: "s",
2648 alsoResize: "#packages-nav",
2649 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2650 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2651 });
Scott Main3b90aff2013-08-01 18:09:35 -07002652
Scott Mainf5089842012-08-14 16:31:07 -07002653 }
Scott Main3b90aff2013-08-01 18:09:35 -07002654
Scott Mainf5089842012-08-14 16:31:07 -07002655function updateSidenavFixedWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002656 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002657 $('#devdoc-nav').css({
2658 'width' : $('#side-nav').css('width'),
2659 'margin' : $('#side-nav').css('margin')
2660 });
2661 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002662
Scott Mainf5089842012-08-14 16:31:07 -07002663 initSidenavHeightResize();
2664}
2665
2666function updateSidenavFullscreenWidth() {
Scott Mainf5257812014-05-22 17:26:38 -07002667 if (!sticky) return;
Scott Mainf5089842012-08-14 16:31:07 -07002668 $('#devdoc-nav').css({
2669 'width' : $('#side-nav').css('width'),
2670 'margin' : $('#side-nav').css('margin')
2671 });
2672 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002673
Scott Mainf5089842012-08-14 16:31:07 -07002674 initSidenavHeightResize();
2675}
2676
2677function buildApiLevelSelector() {
2678 maxLevel = SINCE_DATA.length;
2679 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2680 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2681
2682 minLevel = parseInt($("#doc-api-level").attr("class"));
2683 // Handle provisional api levels; the provisional level will always be the highest possible level
2684 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2685 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2686 if (isNaN(minLevel) && minLevel.length) {
2687 minLevel = maxLevel;
2688 }
2689 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2690 for (var i = maxLevel-1; i >= 0; i--) {
2691 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2692 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2693 select.append(option);
2694 }
2695
2696 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2697 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2698 selectedLevelItem.setAttribute('selected',true);
2699}
2700
2701function changeApiLevel() {
2702 maxLevel = SINCE_DATA.length;
2703 var selectedLevel = maxLevel;
2704
2705 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2706 toggleVisisbleApis(selectedLevel, "body");
2707
2708 var date = new Date();
2709 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2710 var expiration = date.toGMTString();
2711 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2712
2713 if (selectedLevel < minLevel) {
2714 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002715 $("#naMessage").show().html("<div><p><strong>This " + thing
2716 + " requires API level " + minLevel + " or higher.</strong></p>"
2717 + "<p>This document is hidden because your selected API level for the documentation is "
2718 + selectedLevel + ". You can change the documentation API level with the selector "
2719 + "above the left navigation.</p>"
2720 + "<p>For more information about specifying the API level your app requires, "
2721 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2722 + ">Supporting Different Platform Versions</a>.</p>"
2723 + "<input type='button' value='OK, make this page visible' "
2724 + "title='Change the API level to " + minLevel + "' "
2725 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2726 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002727 } else {
2728 $("#naMessage").hide();
2729 }
2730}
2731
2732function toggleVisisbleApis(selectedLevel, context) {
2733 var apis = $(".api",context);
2734 apis.each(function(i) {
2735 var obj = $(this);
2736 var className = obj.attr("class");
2737 var apiLevelIndex = className.lastIndexOf("-")+1;
2738 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2739 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2740 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2741 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2742 return;
2743 }
2744 apiLevel = parseInt(apiLevel);
2745
2746 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2747 var selectedLevelNum = parseInt(selectedLevel)
2748 var apiLevelNum = parseInt(apiLevel);
2749 if (isNaN(apiLevelNum)) {
2750 apiLevelNum = maxLevel;
2751 }
2752
2753 // Grey things out that aren't available and give a tooltip title
2754 if (apiLevelNum > selectedLevelNum) {
2755 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002756 + apiLevel + "\" or higher. To reveal, change the target API level "
2757 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002758 }
Scott Mainf5089842012-08-14 16:31:07 -07002759 else obj.removeClass("absent").removeAttr("title");
2760 });
2761}
2762
2763
2764
2765
2766/* ################# SIDENAV TREE VIEW ################### */
2767
2768function new_node(me, mom, text, link, children_data, api_level)
2769{
2770 var node = new Object();
2771 node.children = Array();
2772 node.children_data = children_data;
2773 node.depth = mom.depth + 1;
2774
2775 node.li = document.createElement("li");
2776 mom.get_children_ul().appendChild(node.li);
2777
2778 node.label_div = document.createElement("div");
2779 node.label_div.className = "label";
2780 if (api_level != null) {
2781 $(node.label_div).addClass("api");
2782 $(node.label_div).addClass("api-level-"+api_level);
2783 }
2784 node.li.appendChild(node.label_div);
2785
2786 if (children_data != null) {
2787 node.expand_toggle = document.createElement("a");
2788 node.expand_toggle.href = "javascript:void(0)";
2789 node.expand_toggle.onclick = function() {
2790 if (node.expanded) {
2791 $(node.get_children_ul()).slideUp("fast");
2792 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2793 node.expanded = false;
2794 } else {
2795 expand_node(me, node);
2796 }
2797 };
2798 node.label_div.appendChild(node.expand_toggle);
2799
2800 node.plus_img = document.createElement("img");
2801 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2802 node.plus_img.className = "plus";
2803 node.plus_img.width = "8";
2804 node.plus_img.border = "0";
2805 node.expand_toggle.appendChild(node.plus_img);
2806
2807 node.expanded = false;
2808 }
2809
2810 var a = document.createElement("a");
2811 node.label_div.appendChild(a);
2812 node.label = document.createTextNode(text);
2813 a.appendChild(node.label);
2814 if (link) {
2815 a.href = me.toroot + link;
2816 } else {
2817 if (children_data != null) {
2818 a.className = "nolink";
2819 a.href = "javascript:void(0)";
2820 a.onclick = node.expand_toggle.onclick;
2821 // This next line shouldn't be necessary. I'll buy a beer for the first
2822 // person who figures out how to remove this line and have the link
2823 // toggle shut on the first try. --joeo@android.com
2824 node.expanded = false;
2825 }
2826 }
Scott Main3b90aff2013-08-01 18:09:35 -07002827
Scott Mainf5089842012-08-14 16:31:07 -07002828
2829 node.children_ul = null;
2830 node.get_children_ul = function() {
2831 if (!node.children_ul) {
2832 node.children_ul = document.createElement("ul");
2833 node.children_ul.className = "children_ul";
2834 node.children_ul.style.display = "none";
2835 node.li.appendChild(node.children_ul);
2836 }
2837 return node.children_ul;
2838 };
2839
2840 return node;
2841}
2842
Robert Lyd2dd6e52012-11-29 21:28:48 -08002843
2844
2845
Scott Mainf5089842012-08-14 16:31:07 -07002846function expand_node(me, node)
2847{
2848 if (node.children_data && !node.expanded) {
2849 if (node.children_visited) {
2850 $(node.get_children_ul()).slideDown("fast");
2851 } else {
2852 get_node(me, node);
2853 if ($(node.label_div).hasClass("absent")) {
2854 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002855 }
Scott Mainf5089842012-08-14 16:31:07 -07002856 $(node.get_children_ul()).slideDown("fast");
2857 }
2858 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2859 node.expanded = true;
2860
2861 // perform api level toggling because new nodes are new to the DOM
2862 var selectedLevel = $("#apiLevelSelector option:selected").val();
2863 toggleVisisbleApis(selectedLevel, "#side-nav");
2864 }
2865}
2866
2867function get_node(me, mom)
2868{
2869 mom.children_visited = true;
2870 for (var i in mom.children_data) {
2871 var node_data = mom.children_data[i];
2872 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2873 node_data[2], node_data[3]);
2874 }
2875}
2876
2877function this_page_relative(toroot)
2878{
2879 var full = document.location.pathname;
2880 var file = "";
2881 if (toroot.substr(0, 1) == "/") {
2882 if (full.substr(0, toroot.length) == toroot) {
2883 return full.substr(toroot.length);
2884 } else {
2885 // the file isn't under toroot. Fail.
2886 return null;
2887 }
2888 } else {
2889 if (toroot != "./") {
2890 toroot = "./" + toroot;
2891 }
2892 do {
2893 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2894 var pos = full.lastIndexOf("/");
2895 file = full.substr(pos) + file;
2896 full = full.substr(0, pos);
2897 toroot = toroot.substr(0, toroot.length-3);
2898 }
2899 } while (toroot != "" && toroot != "/");
2900 return file.substr(1);
2901 }
2902}
2903
2904function find_page(url, data)
2905{
2906 var nodes = data;
2907 var result = null;
2908 for (var i in nodes) {
2909 var d = nodes[i];
2910 if (d[1] == url) {
2911 return new Array(i);
2912 }
2913 else if (d[2] != null) {
2914 result = find_page(url, d[2]);
2915 if (result != null) {
2916 return (new Array(i).concat(result));
2917 }
2918 }
2919 }
2920 return null;
2921}
2922
Scott Mainf5089842012-08-14 16:31:07 -07002923function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002924 // load json file for navtree data
2925 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2926 // when the file is loaded, initialize the tree
2927 if(jqxhr.status === 200) {
2928 init_navtree("tree-list", toroot, NAVTREE_DATA);
2929 }
2930 });
Scott Main3b90aff2013-08-01 18:09:35 -07002931
Scott Mainf5089842012-08-14 16:31:07 -07002932 // perform api level toggling because because the whole tree is new to the DOM
2933 var selectedLevel = $("#apiLevelSelector option:selected").val();
2934 toggleVisisbleApis(selectedLevel, "#side-nav");
2935}
2936
2937function init_navtree(navtree_id, toroot, root_nodes)
2938{
2939 var me = new Object();
2940 me.toroot = toroot;
2941 me.node = new Object();
2942
2943 me.node.li = document.getElementById(navtree_id);
2944 me.node.children_data = root_nodes;
2945 me.node.children = new Array();
2946 me.node.children_ul = document.createElement("ul");
2947 me.node.get_children_ul = function() { return me.node.children_ul; };
2948 //me.node.children_ul.className = "children_ul";
2949 me.node.li.appendChild(me.node.children_ul);
2950 me.node.depth = 0;
2951
2952 get_node(me, me.node);
2953
2954 me.this_page = this_page_relative(toroot);
2955 me.breadcrumbs = find_page(me.this_page, root_nodes);
2956 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2957 var mom = me.node;
2958 for (var i in me.breadcrumbs) {
2959 var j = me.breadcrumbs[i];
2960 mom = mom.children[j];
2961 expand_node(me, mom);
2962 }
2963 mom.label_div.className = mom.label_div.className + " selected";
2964 addLoadEvent(function() {
2965 scrollIntoView("nav-tree");
2966 });
2967 }
2968}
2969
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07002970
2971
2972
2973
2974
2975
2976
Robert Lyd2dd6e52012-11-29 21:28:48 -08002977/* TODO: eliminate redundancy with non-google functions */
2978function init_google_navtree(navtree_id, toroot, root_nodes)
2979{
2980 var me = new Object();
2981 me.toroot = toroot;
2982 me.node = new Object();
2983
2984 me.node.li = document.getElementById(navtree_id);
2985 me.node.children_data = root_nodes;
2986 me.node.children = new Array();
2987 me.node.children_ul = document.createElement("ul");
2988 me.node.get_children_ul = function() { return me.node.children_ul; };
2989 //me.node.children_ul.className = "children_ul";
2990 me.node.li.appendChild(me.node.children_ul);
2991 me.node.depth = 0;
2992
2993 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002994}
2995
2996function new_google_node(me, mom, text, link, children_data, api_level)
2997{
2998 var node = new Object();
2999 var child;
3000 node.children = Array();
3001 node.children_data = children_data;
3002 node.depth = mom.depth + 1;
3003 node.get_children_ul = function() {
3004 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07003005 node.children_ul = document.createElement("ul");
3006 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003007 node.li.appendChild(node.children_ul);
3008 }
3009 return node.children_ul;
3010 };
3011 node.li = document.createElement("li");
3012
3013 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003014
3015
Robert Lyd2dd6e52012-11-29 21:28:48 -08003016 if(link) {
3017 child = document.createElement("a");
3018
3019 }
3020 else {
3021 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003022 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003023
3024 }
3025 if (children_data != null) {
3026 node.li.className="nav-section";
3027 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003028 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003029 node.li.appendChild(node.label_div);
3030 get_google_node(me, node);
3031 node.label_div.appendChild(child);
3032 }
3033 else {
3034 node.li.appendChild(child);
3035 }
3036 if(link) {
3037 child.href = me.toroot + link;
3038 }
3039 node.label = document.createTextNode(text);
3040 child.appendChild(node.label);
3041
3042 node.children_ul = null;
3043
3044 return node;
3045}
3046
3047function get_google_node(me, mom)
3048{
3049 mom.children_visited = true;
3050 var linkText;
3051 for (var i in mom.children_data) {
3052 var node_data = mom.children_data[i];
3053 linkText = node_data[0];
3054
3055 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3056 linkText = linkText.substr(19, linkText.length);
3057 }
3058 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3059 node_data[2], node_data[3]);
3060 }
3061}
Scott Mainad08f072013-08-20 16:49:57 -07003062
3063
3064
3065
3066
3067
3068/****** NEW version of script to build google and sample navs dynamically ******/
3069// TODO: update Google reference docs to tolerate this new implementation
3070
Scott Maine624b3f2013-09-12 12:56:41 -07003071var NODE_NAME = 0;
3072var NODE_HREF = 1;
3073var NODE_GROUP = 2;
3074var NODE_TAGS = 3;
3075var NODE_CHILDREN = 4;
3076
Scott Mainad08f072013-08-20 16:49:57 -07003077function init_google_navtree2(navtree_id, data)
3078{
3079 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003080 for (var i in data) {
3081 var node_data = data[i];
3082 $containerUl.append(new_google_node2(node_data));
3083 }
3084
Scott Main70557ee2013-10-30 14:47:40 -07003085 // Make all third-generation list items 'sticky' to prevent them from collapsing
3086 $containerUl.find('li li li.nav-section').addClass('sticky');
3087
Scott Mainad08f072013-08-20 16:49:57 -07003088 initExpandableNavItems("#"+navtree_id);
3089}
3090
3091function new_google_node2(node_data)
3092{
Scott Maine624b3f2013-09-12 12:56:41 -07003093 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003094 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3095 linkText = linkText.substr(19, linkText.length);
3096 }
3097 var $li = $('<li>');
3098 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003099 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003100 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3101 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003102 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003103 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3104 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003105 }
3106 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003107 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003108 $li.addClass("nav-section");
3109 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003110 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003111
Scott Maine624b3f2013-09-12 12:56:41 -07003112 for (var i in node_data[NODE_CHILDREN]) {
3113 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003114 $childUl.append(new_google_node2(child_node_data));
3115 }
3116 $li.append($childUl);
3117 }
3118 $li.prepend($a);
3119
3120 return $li;
3121}
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
Robert Lyd2dd6e52012-11-29 21:28:48 -08003133function showGoogleRefTree() {
3134 init_default_google_navtree(toRoot);
3135 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003136}
3137
3138function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003139 // load json file for navtree data
3140 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3141 // when the file is loaded, initialize the tree
3142 if(jqxhr.status === 200) {
3143 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3144 highlightSidenav();
3145 resizeNav();
3146 }
3147 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003148}
3149
3150function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003151 // load json file for navtree data
3152 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3153 // when the file is loaded, initialize the tree
3154 if(jqxhr.status === 200) {
3155 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3156 highlightSidenav();
3157 resizeNav();
3158 }
3159 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003160}
3161
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003162function showSamplesRefTree() {
3163 init_default_samples_navtree(toRoot);
3164}
3165
3166function init_default_samples_navtree(toroot) {
3167 // load json file for navtree data
3168 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3169 // when the file is loaded, initialize the tree
3170 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003171 // hack to remove the "about the samples" link then put it back in
3172 // after we nuke the list to remove the dummy static list of samples
3173 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3174 $("#nav.samples-nav").empty();
3175 $("#nav.samples-nav").append($firstLi);
3176
Scott Mainad08f072013-08-20 16:49:57 -07003177 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003178 highlightSidenav();
3179 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003180 if ($("#jd-content #samples").length) {
3181 showSamples();
3182 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003183 }
3184 });
3185}
3186
Scott Mainf5089842012-08-14 16:31:07 -07003187/* TOGGLE INHERITED MEMBERS */
3188
3189/* Toggle an inherited class (arrow toggle)
3190 * @param linkObj The link that was clicked.
3191 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3192 * 'null' to simply toggle.
3193 */
3194function toggleInherited(linkObj, expand) {
3195 var base = linkObj.getAttribute("id");
3196 var list = document.getElementById(base + "-list");
3197 var summary = document.getElementById(base + "-summary");
3198 var trigger = document.getElementById(base + "-trigger");
3199 var a = $(linkObj);
3200 if ( (expand == null && a.hasClass("closed")) || expand ) {
3201 list.style.display = "none";
3202 summary.style.display = "block";
3203 trigger.src = toRoot + "assets/images/triangle-opened.png";
3204 a.removeClass("closed");
3205 a.addClass("opened");
3206 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3207 list.style.display = "block";
3208 summary.style.display = "none";
3209 trigger.src = toRoot + "assets/images/triangle-closed.png";
3210 a.removeClass("opened");
3211 a.addClass("closed");
3212 }
3213 return false;
3214}
3215
3216/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3217 * @param linkObj The link that was clicked.
3218 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3219 * 'null' to simply toggle.
3220 */
3221function toggleAllInherited(linkObj, expand) {
3222 var a = $(linkObj);
3223 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3224 var expandos = $(".jd-expando-trigger", table);
3225 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3226 expandos.each(function(i) {
3227 toggleInherited(this, true);
3228 });
3229 a.text("[Collapse]");
3230 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3231 expandos.each(function(i) {
3232 toggleInherited(this, false);
3233 });
3234 a.text("[Expand]");
3235 }
3236 return false;
3237}
3238
3239/* Toggle all inherited members in the class (link in the class title)
3240 */
3241function toggleAllClassInherited() {
3242 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3243 var toggles = $(".toggle-all", $("#body-content"));
3244 if (a.text() == "[Expand All]") {
3245 toggles.each(function(i) {
3246 toggleAllInherited(this, true);
3247 });
3248 a.text("[Collapse All]");
3249 } else {
3250 toggles.each(function(i) {
3251 toggleAllInherited(this, false);
3252 });
3253 a.text("[Expand All]");
3254 }
3255 return false;
3256}
3257
3258/* Expand all inherited members in the class. Used when initiating page search */
3259function ensureAllInheritedExpanded() {
3260 var toggles = $(".toggle-all", $("#body-content"));
3261 toggles.each(function(i) {
3262 toggleAllInherited(this, true);
3263 });
3264 $("#toggleAllClassInherited").text("[Collapse All]");
3265}
3266
3267
3268/* HANDLE KEY EVENTS
3269 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3270 */
3271var agent = navigator['userAgent'].toLowerCase();
3272var mac = agent.indexOf("macintosh") != -1;
3273
3274$(document).keydown( function(e) {
3275var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3276 if (control && e.which == 70) { // 70 is "F"
3277 ensureAllInheritedExpanded();
3278 }
3279});
Scott Main498d7102013-08-21 15:47:38 -07003280
3281
3282
3283
3284
3285
3286/* On-demand functions */
3287
3288/** Move sample code line numbers out of PRE block and into non-copyable column */
3289function initCodeLineNumbers() {
3290 var numbers = $("#codesample-block a.number");
3291 if (numbers.length) {
3292 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3293 }
3294
3295 $(document).ready(function() {
3296 // select entire line when clicked
3297 $("span.code-line").click(function() {
3298 if (!shifted) {
3299 selectText(this);
3300 }
3301 });
3302 // invoke line link on double click
3303 $(".code-line").dblclick(function() {
3304 document.location.hash = $(this).attr('id');
3305 });
3306 // highlight the line when hovering on the number
3307 $("#codesample-line-numbers a.number").mouseover(function() {
3308 var id = $(this).attr('href');
3309 $(id).css('background','#e7e7e7');
3310 });
3311 $("#codesample-line-numbers a.number").mouseout(function() {
3312 var id = $(this).attr('href');
3313 $(id).css('background','none');
3314 });
3315 });
3316}
3317
3318// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3319var shifted = false;
3320$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3321
3322// courtesy of jasonedelman.com
3323function selectText(element) {
3324 var doc = document
3325 , range, selection
3326 ;
3327 if (doc.body.createTextRange) { //ms
3328 range = doc.body.createTextRange();
3329 range.moveToElementText(element);
3330 range.select();
3331 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003332 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003333 range = doc.createRange();
3334 range.selectNodeContents(element);
3335 selection.removeAllRanges();
3336 selection.addRange(range);
3337 }
Scott Main285f0772013-08-22 23:22:09 +00003338}
Scott Main03aca9a2013-10-31 07:20:55 -07003339
3340
3341
3342
3343/** Display links and other information about samples that match the
3344 group specified by the URL */
3345function showSamples() {
3346 var group = $("#samples").attr('class');
3347 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3348
3349 var $ul = $("<ul>");
3350 $selectedLi = $("#nav li.selected");
3351
3352 $selectedLi.children("ul").children("li").each(function() {
3353 var $li = $("<li>").append($(this).find("a").first().clone());
3354 $ul.append($li);
3355 });
3356
3357 $("#samples").append($ul);
3358
3359}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003360
3361
3362
3363/* ########################################################## */
3364/* ################### RESOURCE CARDS ##################### */
3365/* ########################################################## */
3366
3367/** Handle resource queries, collections, and grids (sections). Requires
3368 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3369
3370(function() {
3371 // Prevent the same resource from being loaded more than once per page.
3372 var addedPageResources = {};
3373
3374 $(document).ready(function() {
3375 $('.resource-widget').each(function() {
3376 initResourceWidget(this);
3377 });
3378
3379 /* Pass the line height to ellipsisfade() to adjust the height of the
3380 text container to show the max number of lines possible, without
3381 showing lines that are cut off. This works with the css ellipsis
3382 classes to fade last text line and apply an ellipsis char. */
3383
Scott Mainb16376f2014-05-21 20:35:47 -07003384 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003385 var lineHeight = 15;
3386 $('.card-info .text').ellipsisfade(lineHeight);
3387 });
3388
3389 /*
3390 Three types of resource layouts:
3391 Flow - Uses a fixed row-height flow using float left style.
3392 Carousel - Single card slideshow all same dimension absolute.
3393 Stack - Uses fixed columns and flexible element height.
3394 */
3395 function initResourceWidget(widget) {
3396 var $widget = $(widget);
3397 var isFlow = $widget.hasClass('resource-flow-layout'),
3398 isCarousel = $widget.hasClass('resource-carousel-layout'),
3399 isStack = $widget.hasClass('resource-stack-layout');
3400
3401 // find size of widget by pulling out its class name
3402 var sizeCols = 1;
3403 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3404 if (m) {
3405 sizeCols = parseInt(m[1], 10);
3406 }
3407
3408 var opts = {
3409 cardSizes: ($widget.data('cardsizes') || '').split(','),
3410 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3411 itemsPerPage: $widget.data('itemsperpage'),
3412 sortOrder: $widget.data('sortorder'),
3413 query: $widget.data('query'),
3414 section: $widget.data('section'),
3415 sizeCols: sizeCols
3416 };
3417
3418 // run the search for the set of resources to show
3419
3420 var resources = buildResourceList(opts);
3421
3422 if (isFlow) {
3423 drawResourcesFlowWidget($widget, opts, resources);
3424 } else if (isCarousel) {
3425 drawResourcesCarouselWidget($widget, opts, resources);
3426 } else if (isStack) {
3427 var sections = buildSectionList(opts);
3428 opts['numStacks'] = $widget.data('numstacks');
3429 drawResourcesStackWidget($widget, opts, resources, sections);
3430 }
3431 }
3432
3433 /* Initializes a Resource Carousel Widget */
3434 function drawResourcesCarouselWidget($widget, opts, resources) {
3435 $widget.empty();
3436 var plusone = true; //always show plusone on carousel
3437
3438 $widget.addClass('resource-card slideshow-container')
3439 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3440 .append($('<a>').addClass('slideshow-next').text('Next'));
3441
3442 var css = { 'width': $widget.width() + 'px',
3443 'height': $widget.height() + 'px' };
3444
3445 var $ul = $('<ul>');
3446
3447 for (var i = 0; i < resources.length; ++i) {
3448 //keep url clean for matching and offline mode handling
3449 var urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3450 var $card = $('<a>')
3451 .attr('href', urlPrefix + resources[i].url)
3452 .decorateResourceCard(resources[i],plusone);
3453
3454 $('<li>').css(css)
3455 .append($card)
3456 .appendTo($ul);
3457 }
3458
3459 $('<div>').addClass('frame')
3460 .append($ul)
3461 .appendTo($widget);
3462
3463 $widget.dacSlideshow({
3464 auto: true,
3465 btnPrev: '.slideshow-prev',
3466 btnNext: '.slideshow-next'
3467 });
3468 };
3469
3470 /* Initializes a Resource Card Stack Widget (column-based layout) */
3471 function drawResourcesStackWidget($widget, opts, resources, sections) {
3472 // Don't empty widget, grab all items inside since they will be the first
3473 // items stacked, followed by the resource query
3474 var plusone = true; //by default show plusone on section cards
3475 var cards = $widget.find('.resource-card').detach().toArray();
3476 var numStacks = opts.numStacks || 1;
3477 var $stacks = [];
3478 var urlString;
3479
3480 for (var i = 0; i < numStacks; ++i) {
3481 $stacks[i] = $('<div>').addClass('resource-card-stack')
3482 .appendTo($widget);
3483 }
3484
3485 var sectionResources = [];
3486
3487 // Extract any subsections that are actually resource cards
3488 for (var i = 0; i < sections.length; ++i) {
3489 if (!sections[i].sections || !sections[i].sections.length) {
3490 //keep url clean for matching and offline mode handling
3491 urlPrefix = sections[i].url.indexOf("//") > -1 ? "" : toRoot;
3492 // Render it as a resource card
3493
3494 sectionResources.push(
3495 $('<a>')
3496 .addClass('resource-card section-card')
3497 .attr('href', urlPrefix + sections[i].resource.url)
3498 .decorateResourceCard(sections[i].resource,plusone)[0]
3499 );
3500
3501 } else {
3502 cards.push(
3503 $('<div>')
3504 .addClass('resource-card section-card-menu')
3505 .decorateResourceSection(sections[i],plusone)[0]
3506 );
3507 }
3508 }
3509
3510 cards = cards.concat(sectionResources);
3511
3512 for (var i = 0; i < resources.length; ++i) {
3513 //keep url clean for matching and offline mode handling
3514 urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3515 var $card = $('<a>')
3516 .addClass('resource-card related-card')
3517 .attr('href', urlPrefix + resources[i].url)
3518 .decorateResourceCard(resources[i],plusone);
3519
3520 cards.push($card[0]);
3521 }
3522
3523 for (var i = 0; i < cards.length; ++i) {
3524 // Find the stack with the shortest height, but give preference to
3525 // left to right order.
3526 var minHeight = $stacks[0].height();
3527 var minIndex = 0;
3528
3529 for (var j = 1; j < numStacks; ++j) {
3530 var height = $stacks[j].height();
3531 if (height < minHeight - 45) {
3532 minHeight = height;
3533 minIndex = j;
3534 }
3535 }
3536
3537 $stacks[minIndex].append($(cards[i]));
3538 }
3539
3540 };
3541
3542 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3543 function drawResourcesFlowWidget($widget, opts, resources) {
3544 $widget.empty();
3545 var cardSizes = opts.cardSizes || ['6x6'];
3546 var i = 0, j = 0;
3547 var plusone = true; // by default show plusone on resource cards
3548
3549 while (i < resources.length) {
3550 var cardSize = cardSizes[j++ % cardSizes.length];
3551 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Dirk Doughertyc3921652014-05-13 16:55:26 -07003552 // Some card sizes do not get a plusone button, such as where space is constrained
3553 // or for cards commonly embedded in docs (to improve overall page speed).
3554 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3555 (cardSize == "9x2") || (cardSize == "9x3") ||
3556 (cardSize == "12x2") || (cardSize == "12x3"));
3557
3558 // A stack has a third dimension which is the number of stacked items
3559 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3560 var stackCount = 0;
3561 var $stackDiv = null;
3562
3563 if (isStack) {
3564 // Create a stack container which should have the dimensions defined
3565 // by the product of the items inside.
3566 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3567 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3568 }
3569
3570 // Build each stack item or just a single item
3571 do {
3572 var resource = resources[i];
3573 //keep url clean for matching and offline mode handling
3574 urlPrefix = resource.url.indexOf("//") > -1 ? "" : toRoot;
3575 var $card = $('<a>')
3576 .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
3577 .attr('href', urlPrefix + resource.url);
3578
3579 if (isStack) {
3580 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3581 if (++stackCount == parseInt(isStack[3])) {
3582 $card.addClass('resource-card-row-stack-last');
3583 stackCount = 0;
3584 }
3585 } else {
3586 stackCount = 0;
3587 }
3588
3589 $card.decorateResourceCard(resource,plusone)
3590 .appendTo($stackDiv || $widget);
3591
3592 } while (++i < resources.length && stackCount > 0);
3593 }
3594 }
3595
3596 /* Build a site map of resources using a section as a root. */
3597 function buildSectionList(opts) {
3598 if (opts.section && SECTION_BY_ID[opts.section]) {
3599 return SECTION_BY_ID[opts.section].sections || [];
3600 }
3601 return [];
3602 }
3603
3604 function buildResourceList(opts) {
3605 var maxResults = opts.maxResults || 100;
3606
3607 var query = opts.query || '';
3608 var expressions = parseResourceQuery(query);
3609 var addedResourceIndices = {};
3610 var results = [];
3611
3612 for (var i = 0; i < expressions.length; i++) {
3613 var clauses = expressions[i];
3614
3615 // build initial set of resources from first clause
3616 var firstClause = clauses[0];
3617 var resources = [];
3618 switch (firstClause.attr) {
3619 case 'type':
3620 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3621 break;
3622 case 'lang':
3623 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3624 break;
3625 case 'tag':
3626 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3627 break;
3628 case 'collection':
3629 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3630 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3631 break;
3632 case 'section':
3633 var urls = SITE_MAP[firstClause.value].sections || [];
3634 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3635 break;
3636 }
3637 // console.log(firstClause.attr + ':' + firstClause.value);
3638 resources = resources || [];
3639
3640 // use additional clauses to filter corpus
3641 if (clauses.length > 1) {
3642 var otherClauses = clauses.slice(1);
3643 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3644 }
3645
3646 // filter out resources already added
3647 if (i > 1) {
3648 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3649 }
3650
3651 // add to list of already added indices
3652 for (var j = 0; j < resources.length; j++) {
3653 // console.log(resources[j].title);
3654 addedResourceIndices[resources[j].index] = 1;
3655 }
3656
3657 // concat to final results list
3658 results = results.concat(resources);
3659 }
3660
3661 if (opts.sortOrder && results.length) {
3662 var attr = opts.sortOrder;
3663
3664 if (opts.sortOrder == 'random') {
3665 var i = results.length, j, temp;
3666 while (--i) {
3667 j = Math.floor(Math.random() * (i + 1));
3668 temp = results[i];
3669 results[i] = results[j];
3670 results[j] = temp;
3671 }
3672 } else {
3673 var desc = attr.charAt(0) == '-';
3674 if (desc) {
3675 attr = attr.substring(1);
3676 }
3677 results = results.sort(function(x,y) {
3678 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3679 });
3680 }
3681 }
3682
3683 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3684 results = results.slice(0, maxResults);
3685
3686 for (var j = 0; j < results.length; ++j) {
3687 addedPageResources[results[j].index] = 1;
3688 }
3689
3690 return results;
3691 }
3692
3693
3694 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3695 return function(resource) {
3696 return !addedResourceIndices[resource.index];
3697 };
3698 }
3699
3700
3701 function getResourceMatchesClausesFilter(clauses) {
3702 return function(resource) {
3703 return doesResourceMatchClauses(resource, clauses);
3704 };
3705 }
3706
3707
3708 function doesResourceMatchClauses(resource, clauses) {
3709 for (var i = 0; i < clauses.length; i++) {
3710 var map;
3711 switch (clauses[i].attr) {
3712 case 'type':
3713 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3714 break;
3715 case 'lang':
3716 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3717 break;
3718 case 'tag':
3719 map = IS_RESOURCE_TAGGED[clauses[i].value];
3720 break;
3721 }
3722
3723 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3724 return clauses[i].negative;
3725 }
3726 }
3727 return true;
3728 }
3729
3730
3731 function parseResourceQuery(query) {
3732 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3733 var expressions = [];
3734 var expressionStrs = query.split(',') || [];
3735 for (var i = 0; i < expressionStrs.length; i++) {
3736 var expr = expressionStrs[i] || '';
3737
3738 // Break expression into clauses (clause e.g. 'tag:foo')
3739 var clauses = [];
3740 var clauseStrs = expr.split(/(?=[\+\-])/);
3741 for (var j = 0; j < clauseStrs.length; j++) {
3742 var clauseStr = clauseStrs[j] || '';
3743
3744 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3745 var parts = clauseStr.split(':');
3746 var clause = {};
3747
3748 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3749 if (clause.attr) {
3750 if (clause.attr.charAt(0) == '+') {
3751 clause.attr = clause.attr.substring(1);
3752 } else if (clause.attr.charAt(0) == '-') {
3753 clause.negative = true;
3754 clause.attr = clause.attr.substring(1);
3755 }
3756 }
3757
3758 if (parts.length > 1) {
3759 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3760 }
3761
3762 clauses.push(clause);
3763 }
3764
3765 if (!clauses.length) {
3766 continue;
3767 }
3768
3769 expressions.push(clauses);
3770 }
3771
3772 return expressions;
3773 }
3774})();
3775
3776(function($) {
3777 /* Simple jquery function to create dom for a standard resource card */
3778 $.fn.decorateResourceCard = function(resource,plusone) {
3779 var section = resource.group || resource.type;
3780 var imgUrl;
3781 if (resource.image) {
3782 //keep url clean for matching and offline mode handling
3783 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3784 imgUrl = urlPrefix + resource.image;
3785 }
3786 //add linkout logic here. check url or type, assign a class, map to css :after
3787 $('<div>')
3788 .addClass('card-bg')
3789 .css('background-image', 'url(' + (imgUrl || toRoot + 'assets/images/resource-card-default-android.jpg') + ')')
3790 .appendTo(this);
3791 if (!plusone) {
3792 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3793 .append($('<div>').addClass('section').text(section))
3794 .append($('<div>').addClass('title').html(resource.title))
3795 .append($('<div>').addClass('description ellipsis')
3796 .append($('<div>').addClass('text').html(resource.summary))
3797 .append($('<div>').addClass('util')))
3798 .appendTo(this);
3799 } else {
Dirk Dougherty518ed142014-05-30 21:24:42 -07003800 var plusurl = resource.url.indexOf("//") > -1 ? resource.url : "//developer.android.com/" + resource.url;
Dirk Doughertyc3921652014-05-13 16:55:26 -07003801 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3802 .append($('<div>').addClass('section').text(section))
3803 .append($('<div>').addClass('title').html(resource.title))
3804 .append($('<div>').addClass('description ellipsis')
3805 .append($('<div>').addClass('text').html(resource.summary))
3806 .append($('<div>').addClass('util')
3807 .append($('<div>').addClass('g-plusone')
3808 .attr('data-size', 'small')
3809 .attr('data-align', 'right')
Dirk Dougherty518ed142014-05-30 21:24:42 -07003810 .attr('data-href', plusurl))))
Dirk Doughertyc3921652014-05-13 16:55:26 -07003811 .appendTo(this);
3812 }
3813
3814 return this;
3815 };
3816
3817 /* Simple jquery function to create dom for a resource section card (menu) */
3818 $.fn.decorateResourceSection = function(section,plusone) {
3819 var resource = section.resource;
3820 //keep url clean for matching and offline mode handling
3821 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3822 var $base = $('<a>')
3823 .addClass('card-bg')
3824 .attr('href', resource.url)
3825 .append($('<div>').addClass('card-section-icon')
3826 .append($('<div>').addClass('icon'))
3827 .append($('<div>').addClass('section').html(resource.title)))
3828 .appendTo(this);
3829
3830 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3831
3832 if (section.sections && section.sections.length) {
3833 // Recurse the section sub-tree to find a resource image.
3834 var stack = [section];
3835
3836 while (stack.length) {
3837 if (stack[0].resource.image) {
3838 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3839 break;
3840 }
3841
3842 if (stack[0].sections) {
3843 stack = stack.concat(stack[0].sections);
3844 }
3845
3846 stack.shift();
3847 }
3848
3849 var $ul = $('<ul>')
3850 .appendTo($cardInfo);
3851
3852 var max = section.sections.length > 3 ? 3 : section.sections.length;
3853
3854 for (var i = 0; i < max; ++i) {
3855
3856 var subResource = section.sections[i];
3857 if (!plusone) {
3858 $('<li>')
3859 .append($('<a>').attr('href', subResource.url)
3860 .append($('<div>').addClass('title').html(subResource.title))
3861 .append($('<div>').addClass('description ellipsis')
3862 .append($('<div>').addClass('text').html(subResource.summary))
3863 .append($('<div>').addClass('util'))))
3864 .appendTo($ul);
3865 } else {
3866 $('<li>')
3867 .append($('<a>').attr('href', subResource.url)
3868 .append($('<div>').addClass('title').html(subResource.title))
3869 .append($('<div>').addClass('description ellipsis')
3870 .append($('<div>').addClass('text').html(subResource.summary))
3871 .append($('<div>').addClass('util')
3872 .append($('<div>').addClass('g-plusone')
3873 .attr('data-size', 'small')
3874 .attr('data-align', 'right')
3875 .attr('data-href', resource.url)))))
3876 .appendTo($ul);
3877 }
3878 }
3879
3880 // Add a more row
3881 if (max < section.sections.length) {
3882 $('<li>')
3883 .append($('<a>').attr('href', resource.url)
3884 .append($('<div>')
3885 .addClass('title')
3886 .text('More')))
3887 .appendTo($ul);
3888 }
3889 } else {
3890 // No sub-resources, just render description?
3891 }
3892
3893 return this;
3894 };
3895})(jQuery);
3896/* Calculate the vertical area remaining */
3897(function($) {
3898 $.fn.ellipsisfade= function(lineHeight) {
3899 this.each(function() {
3900 // get element text
3901 var $this = $(this);
3902 var remainingHeight = $this.parent().parent().height();
3903 $this.parent().siblings().each(function ()
3904 {
3905 var h = $(this).height();
3906 remainingHeight = remainingHeight - h;
3907 });
3908
3909 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
3910 $this.parent().css({'height': adjustedRemainingHeight});
3911 $this.css({'height': "auto"});
3912 });
3913
3914 return this;
3915 };
3916}) (jQuery);