blob: 23f0660506bdd787e51e9718b4c669b729fa7554 [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
22var navBarIsFixed = false;
23$(document).ready(function() {
Scott Main7e447ed2013-02-19 17:22:37 -080024
Scott Main0e76e7e2013-03-12 10:24:07 -070025 // load json file for JD doc search suggestions
Scott Main719acb42013-12-05 16:05:09 -080026 $.getScript(toRoot + 'jd_lists_unified.js');
Scott Main7e447ed2013-02-19 17:22:37 -080027 // load json file for Android API search suggestions
28 $.getScript(toRoot + 'reference/lists.js');
29 // load json files for Google services API suggestions
Scott Main9f2971d2013-02-26 13:07:41 -080030 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080031 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data
32 if(jqxhr.status === 200) {
Scott Main9f2971d2013-02-26 13:07:41 -080033 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) {
Scott Main7e447ed2013-02-19 17:22:37 -080034 if(jqxhr.status === 200) {
35 // combine GCM and GMS data
36 GOOGLE_DATA = GMS_DATA;
37 var start = GOOGLE_DATA.length;
38 for (var i=0; i<GCM_DATA.length; i++) {
39 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label,
40 link:GCM_DATA[i].link, type:GCM_DATA[i].type});
41 }
42 }
43 });
44 }
45 });
46
Scott Main0e76e7e2013-03-12 10:24:07 -070047 // setup keyboard listener for search shortcut
48 $('body').keyup(function(event) {
49 if (event.which == 191) {
50 $('#search_autocomplete').focus();
51 }
52 });
Scott Main015d6162013-01-29 09:01:52 -080053
Scott Maine4d8f1b2012-06-21 18:03:05 -070054 // init the fullscreen toggle click event
55 $('#nav-swap .fullscreen').click(function(){
56 if ($(this).hasClass('disabled')) {
57 toggleFullscreen(true);
58 } else {
59 toggleFullscreen(false);
60 }
61 });
Scott Main3b90aff2013-08-01 18:09:35 -070062
Scott Maine4d8f1b2012-06-21 18:03:05 -070063 // initialize the divs with custom scrollbars
64 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -070065
Scott Maine4d8f1b2012-06-21 18:03:05 -070066 // add HRs below all H2s (except for a few other h2 variants)
Scott Maindb3678b2012-10-23 14:13:41 -070067 $('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 -070068
69 // set up the search close button
70 $('.search .close').click(function() {
71 $searchInput = $('#search_autocomplete');
72 $searchInput.attr('value', '');
73 $(this).addClass("hide");
74 $("#search-container").removeClass('active');
75 $("#search_autocomplete").blur();
Scott Main0e76e7e2013-03-12 10:24:07 -070076 search_focus_changed($searchInput.get(), false);
77 hideResults();
Scott Maine4d8f1b2012-06-21 18:03:05 -070078 });
79
80 // Set up quicknav
Scott Main3b90aff2013-08-01 18:09:35 -070081 var quicknav_open = false;
Scott Maine4d8f1b2012-06-21 18:03:05 -070082 $("#btn-quicknav").click(function() {
83 if (quicknav_open) {
84 $(this).removeClass('active');
85 quicknav_open = false;
86 collapse();
87 } else {
88 $(this).addClass('active');
89 quicknav_open = true;
90 expand();
91 }
92 })
Scott Main3b90aff2013-08-01 18:09:35 -070093
Scott Maine4d8f1b2012-06-21 18:03:05 -070094 var expand = function() {
95 $('#header-wrap').addClass('quicknav');
96 $('#quicknav').stop().show().animate({opacity:'1'});
97 }
Scott Main3b90aff2013-08-01 18:09:35 -070098
Scott Maine4d8f1b2012-06-21 18:03:05 -070099 var collapse = function() {
100 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
101 $(this).hide();
102 $('#header-wrap').removeClass('quicknav');
103 });
104 }
Scott Main3b90aff2013-08-01 18:09:35 -0700105
106
Scott Maine4d8f1b2012-06-21 18:03:05 -0700107 //Set up search
108 $("#search_autocomplete").focus(function() {
109 $("#search-container").addClass('active');
110 })
111 $("#search-container").mouseover(function() {
112 $("#search-container").addClass('active');
113 $("#search_autocomplete").focus();
114 })
115 $("#search-container").mouseout(function() {
116 if ($("#search_autocomplete").is(":focus")) return;
117 if ($("#search_autocomplete").val() == '') {
118 setTimeout(function(){
119 $("#search-container").removeClass('active');
120 $("#search_autocomplete").blur();
121 },250);
122 }
123 })
124 $("#search_autocomplete").blur(function() {
125 if ($("#search_autocomplete").val() == '') {
126 $("#search-container").removeClass('active');
127 }
128 })
129
Scott Main3b90aff2013-08-01 18:09:35 -0700130
Scott Maine4d8f1b2012-06-21 18:03:05 -0700131 // prep nav expandos
132 var pagePath = document.location.pathname;
133 // account for intl docs by removing the intl/*/ path
134 if (pagePath.indexOf("/intl/") == 0) {
135 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
136 }
Scott Mainac2aef52013-02-12 14:15:23 -0800137
Scott Maine4d8f1b2012-06-21 18:03:05 -0700138 if (pagePath.indexOf(SITE_ROOT) == 0) {
139 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
140 pagePath += 'index.html';
141 }
142 }
143
Scott Main01a25452013-02-12 17:32:27 -0800144 // Need a copy of the pagePath before it gets changed in the next block;
145 // it's needed to perform proper tab highlighting in offline docs (see rootDir below)
146 var pagePathOriginal = pagePath;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700147 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
148 // If running locally, SITE_ROOT will be a relative path, so account for that by
149 // finding the relative URL to this page. This will allow us to find links on the page
150 // leading back to this page.
151 var pathParts = pagePath.split('/');
152 var relativePagePathParts = [];
153 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
154 for (var i = 0; i < upDirs; i++) {
155 relativePagePathParts.push('..');
156 }
157 for (var i = 0; i < upDirs; i++) {
158 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
159 }
160 relativePagePathParts.push(pathParts[pathParts.length - 1]);
161 pagePath = relativePagePathParts.join('/');
162 } else {
163 // Otherwise the page path is already an absolute URL
164 }
165
Scott Mainac2aef52013-02-12 14:15:23 -0800166 // Highlight the header tabs...
167 // highlight Design tab
168 if ($("body").hasClass("design")) {
169 $("#header li.design a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700170 $("#sticky-header").addClass("design");
Scott Mainac2aef52013-02-12 14:15:23 -0800171
172 // highlight Develop tab
173 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) {
174 $("#header li.develop a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700175 $("#sticky-header").addClass("develop");
Scott Mainac2aef52013-02-12 14:15:23 -0800176 // In Develop docs, also highlight appropriate sub-tab
Scott Main01a25452013-02-12 17:32:27 -0800177 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1));
Scott Mainac2aef52013-02-12 14:15:23 -0800178 if (rootDir == "training") {
179 $("#nav-x li.training a").addClass("selected");
180 } else if (rootDir == "guide") {
181 $("#nav-x li.guide a").addClass("selected");
182 } else if (rootDir == "reference") {
183 // If the root is reference, but page is also part of Google Services, select Google
184 if ($("body").hasClass("google")) {
185 $("#nav-x li.google a").addClass("selected");
186 } else {
187 $("#nav-x li.reference a").addClass("selected");
188 }
189 } else if ((rootDir == "tools") || (rootDir == "sdk")) {
190 $("#nav-x li.tools a").addClass("selected");
191 } else if ($("body").hasClass("google")) {
192 $("#nav-x li.google a").addClass("selected");
Dirk Dougherty4f7e5152010-09-16 10:43:40 -0700193 } else if ($("body").hasClass("samples")) {
194 $("#nav-x li.samples a").addClass("selected");
Scott Mainac2aef52013-02-12 14:15:23 -0800195 }
196
197 // highlight Distribute tab
198 } else if ($("body").hasClass("distribute")) {
199 $("#header li.distribute a").addClass("selected");
Dirk Doughertyc3921652014-05-13 16:55:26 -0700200 $("#sticky-header").addClass("distribute");
201
202 var baseFrag = pagePathOriginal.indexOf('/', 1) + 1;
203 var secondFrag = pagePathOriginal.substring(baseFrag, pagePathOriginal.indexOf('/', baseFrag));
204 if (secondFrag == "users") {
205 $("#nav-x li.users a").addClass("selected");
206 } else if (secondFrag == "engage") {
207 $("#nav-x li.engage a").addClass("selected");
208 } else if (secondFrag == "monetize") {
209 $("#nav-x li.monetize a").addClass("selected");
210 } else if (secondFrag == "tools") {
211 $("#nav-x li.disttools a").addClass("selected");
212 } else if (secondFrag == "stories") {
213 $("#nav-x li.stories a").addClass("selected");
214 } else if (secondFrag == "essentials") {
215 $("#nav-x li.essentials a").addClass("selected");
216 } else if (secondFrag == "googleplay") {
217 $("#nav-x li.googleplay a").addClass("selected");
218 }
219 } else if ($("body").hasClass("about")) {
220 $("#sticky-header").addClass("about");
Scott Mainb16376f2014-05-21 20:35:47 -0700221 }
Scott Mainac2aef52013-02-12 14:15:23 -0800222
Scott Mainf6145542013-04-01 16:38:11 -0700223 // set global variable so we can highlight the sidenav a bit later (such as for google reference)
224 // and highlight the sidenav
225 mPagePath = pagePath;
226 highlightSidenav();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700227 buildBreadcrumbs();
Scott Mainac2aef52013-02-12 14:15:23 -0800228
Scott Mainf6145542013-04-01 16:38:11 -0700229 // set up prev/next links if they exist
Scott Maine4d8f1b2012-06-21 18:03:05 -0700230 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700231 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700232 if ($selNavLink.length) {
Scott Mainac2aef52013-02-12 14:15:23 -0800233 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700234
235 // set up prev links
236 var $prevLink = [];
237 var $prevListItem = $selListItem.prev('li');
Scott Main3b90aff2013-08-01 18:09:35 -0700238
Scott Maine4d8f1b2012-06-21 18:03:05 -0700239 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
240false; // navigate across topic boundaries only in design docs
241 if ($prevListItem.length) {
242 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700243 // jump to last topic of previous section
244 $prevLink = $prevListItem.find('a:last');
245 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700246 // jump to previous topic in this section
247 $prevLink = $prevListItem.find('a:eq(0)');
248 }
249 } else {
250 // jump to this section's index page (if it exists)
251 var $parentListItem = $selListItem.parents('li');
252 $prevLink = $selListItem.parents('li').find('a');
Scott Main3b90aff2013-08-01 18:09:35 -0700253
Scott Maine4d8f1b2012-06-21 18:03:05 -0700254 // except if cross boundaries aren't allowed, and we're at the top of a section already
255 // (and there's another parent)
Scott Main3b90aff2013-08-01 18:09:35 -0700256 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
Scott Maine4d8f1b2012-06-21 18:03:05 -0700257 && $selListItem.hasClass('nav-section')) {
258 $prevLink = [];
259 }
260 }
261
Scott Maine4d8f1b2012-06-21 18:03:05 -0700262 // set up next links
263 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700264 var startClass = false;
265 var training = $(".next-class-link").length; // decides whether to provide "next class" link
266 var isCrossingBoundary = false;
Scott Main3b90aff2013-08-01 18:09:35 -0700267
Scott Main1a00f7f2013-10-29 11:11:19 -0700268 if ($selListItem.hasClass('nav-section') && $selListItem.children('div.empty').length == 0) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700269 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700270 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700271
272 // if there aren't any children, go to the next section (required for About pages)
273 if($nextLink.length == 0) {
274 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700275 } else if ($('.topic-start-link').length) {
276 // as long as there's a child link and there is a "topic start link" (we're on a landing)
277 // then set the landing page "start link" text to be the first doc title
278 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700279 }
Scott Main3b90aff2013-08-01 18:09:35 -0700280
Scott Main5a1123e2012-09-26 12:51:28 -0700281 // If the selected page has a description, then it's a class or article homepage
282 if ($selListItem.find('a[description]').length) {
283 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700284 startClass = true;
285 }
286 } else {
287 // jump to the next topic in this section (if it exists)
288 $nextLink = $selListItem.next('li').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700289 if ($nextLink.length == 0) {
Scott Main5a1123e2012-09-26 12:51:28 -0700290 isCrossingBoundary = true;
291 // no more topics in this section, jump to the first topic in the next section
292 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
293 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
294 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Main1a00f7f2013-10-29 11:11:19 -0700295 if ($nextLink.length == 0) {
296 // if that doesn't work, we're at the end of the list, so disable NEXT link
297 $('.next-page-link').attr('href','').addClass("disabled")
298 .click(function() { return false; });
299 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700300 }
301 }
302 }
Scott Main5a1123e2012-09-26 12:51:28 -0700303
304 if (startClass) {
305 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
306
Scott Main3b90aff2013-08-01 18:09:35 -0700307 // if there's no training bar (below the start button),
Scott Main5a1123e2012-09-26 12:51:28 -0700308 // then we need to add a bottom border to button
309 if (!$("#tb").length) {
310 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700311 }
Scott Main5a1123e2012-09-26 12:51:28 -0700312 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
313 $('.content-footer.next-class').show();
314 $('.next-page-link').attr('href','')
315 .removeClass("hide").addClass("disabled")
316 .click(function() { return false; });
Scott Main1a00f7f2013-10-29 11:11:19 -0700317 if ($nextLink.length) {
318 $('.next-class-link').attr('href',$nextLink.attr('href'))
319 .removeClass("hide").append($nextLink.html());
320 $('.next-class-link').find('.new').empty();
321 }
Scott Main5a1123e2012-09-26 12:51:28 -0700322 } else {
323 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
324 }
325
326 if (!startClass && $prevLink.length) {
327 var prevHref = $prevLink.attr('href');
328 if (prevHref == SITE_ROOT + 'index.html') {
329 // Don't show Previous when it leads to the homepage
330 } else {
331 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
332 }
Scott Main3b90aff2013-08-01 18:09:35 -0700333 }
Scott Main5a1123e2012-09-26 12:51:28 -0700334
335 // If this is a training 'article', there should be no prev/next nav
336 // ... if the grandparent is the "nav" ... and it has no child list items...
337 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
338 !$selListItem.find('li').length) {
339 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
340 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700341 }
Scott Main3b90aff2013-08-01 18:09:35 -0700342
Scott Maine4d8f1b2012-06-21 18:03:05 -0700343 }
Scott Main3b90aff2013-08-01 18:09:35 -0700344
345
346
Scott Main5a1123e2012-09-26 12:51:28 -0700347 // Set up the course landing pages for Training with class names and descriptions
348 if ($('body.trainingcourse').length) {
349 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
350 var $classDescriptions = $classLinks.attr('description');
Scott Main3b90aff2013-08-01 18:09:35 -0700351
Scott Main5a1123e2012-09-26 12:51:28 -0700352 var $olClasses = $('<ol class="class-list"></ol>');
353 var $liClass;
354 var $imgIcon;
355 var $h2Title;
356 var $pSummary;
357 var $olLessons;
358 var $liLesson;
359 $classLinks.each(function(index) {
360 $liClass = $('<li></li>');
361 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
362 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>');
Scott Main3b90aff2013-08-01 18:09:35 -0700363
Scott Main5a1123e2012-09-26 12:51:28 -0700364 $olLessons = $('<ol class="lesson-list"></ol>');
Scott Main3b90aff2013-08-01 18:09:35 -0700365
Scott Main5a1123e2012-09-26 12:51:28 -0700366 $lessons = $(this).closest('li').find('ul li a');
Scott Main3b90aff2013-08-01 18:09:35 -0700367
Scott Main5a1123e2012-09-26 12:51:28 -0700368 if ($lessons.length) {
Scott Main3b90aff2013-08-01 18:09:35 -0700369 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" '
370 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700371 $lessons.each(function(index) {
372 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
373 });
374 } else {
Scott Main3b90aff2013-08-01 18:09:35 -0700375 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" '
376 + ' width="64" height="64" alt=""/>');
Scott Main5a1123e2012-09-26 12:51:28 -0700377 $pSummary.addClass('article');
378 }
379
380 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
381 $olClasses.append($liClass);
382 });
383 $('.jd-descr').append($olClasses);
384 }
385
Scott Maine4d8f1b2012-06-21 18:03:05 -0700386 // Set up expand/collapse behavior
Scott Mainad08f072013-08-20 16:49:57 -0700387 initExpandableNavItems("#nav");
Scott Main3b90aff2013-08-01 18:09:35 -0700388
Scott Main3b90aff2013-08-01 18:09:35 -0700389
Scott Maine4d8f1b2012-06-21 18:03:05 -0700390 $(".scroll-pane").scroll(function(event) {
391 event.preventDefault();
392 return false;
393 });
394
395 /* Resize nav height when window height changes */
396 $(window).resize(function() {
397 if ($('#side-nav').length == 0) return;
398 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
399 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
400 // make sidenav behave when resizing the window and side-scolling is a concern
401 if (navBarIsFixed) {
402 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
403 updateSideNavPosition();
404 } else {
405 updateSidenavFullscreenWidth();
406 }
407 }
408 resizeNav();
409 });
410
411
Scott Maine4d8f1b2012-06-21 18:03:05 -0700412 var navBarLeftPos;
413 if ($('#devdoc-nav').length) {
414 setNavBarLeftPos();
415 }
416
417
Scott Maine4d8f1b2012-06-21 18:03:05 -0700418 // Set up play-on-hover <video> tags.
419 $('video.play-on-hover').bind('click', function(){
420 $(this).get(0).load(); // in case the video isn't seekable
421 $(this).get(0).play();
422 });
423
424 // Set up tooltips
425 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700426 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700427 var $target = $(this);
428 var $tooltip = $('<div>')
429 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700430 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700431 .hide()
432 .appendTo('body');
433 $target.removeAttr('title');
434
435 $target.hover(function() {
436 // in
437 var targetRect = $target.offset();
438 targetRect.width = $target.width();
439 targetRect.height = $target.height();
440
441 $tooltip.css({
442 left: targetRect.left,
443 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
444 });
445 $tooltip.addClass('below');
446 $tooltip.show();
447 }, function() {
448 // out
449 $tooltip.hide();
450 });
451 });
452
453 // Set up <h2> deeplinks
454 $('h2').click(function() {
455 var id = $(this).attr('id');
456 if (id) {
457 document.location.hash = id;
458 }
459 });
460
461 //Loads the +1 button
462 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
463 po.src = 'https://apis.google.com/js/plusone.js';
464 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
465
466
Scott Main3b90aff2013-08-01 18:09:35 -0700467 // Revise the sidenav widths to make room for the scrollbar
Scott Maine4d8f1b2012-06-21 18:03:05 -0700468 // which avoids the visible width from changing each time the bar appears
469 var $sidenav = $("#side-nav");
470 var sidenav_width = parseInt($sidenav.innerWidth());
Scott Main3b90aff2013-08-01 18:09:35 -0700471
Scott Maine4d8f1b2012-06-21 18:03:05 -0700472 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
473
474
475 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
Scott Main3b90aff2013-08-01 18:09:35 -0700476
Scott Maine4d8f1b2012-06-21 18:03:05 -0700477 if ($(".scroll-pane").length > 1) {
478 // Check if there's a user preference for the panel heights
479 var cookieHeight = readCookie("reference_height");
480 if (cookieHeight) {
481 restoreHeight(cookieHeight);
482 }
483 }
Scott Main3b90aff2013-08-01 18:09:35 -0700484
Scott Maine4d8f1b2012-06-21 18:03:05 -0700485 resizeNav();
486
Scott Main015d6162013-01-29 09:01:52 -0800487 /* init the language selector based on user cookie for lang */
488 loadLangPref();
489 changeNavLang(getLangPref());
490
491 /* setup event handlers to ensure the overflow menu is visible while picking lang */
492 $("#language select")
493 .mousedown(function() {
494 $("div.morehover").addClass("hover"); })
495 .blur(function() {
496 $("div.morehover").removeClass("hover"); });
497
498 /* some global variable setup */
499 resizePackagesNav = $("#resize-packages-nav");
500 classesNav = $("#classes-nav");
501 devdocNav = $("#devdoc-nav");
502
503 var cookiePath = "";
504 if (location.href.indexOf("/reference/") != -1) {
505 cookiePath = "reference_";
506 } else if (location.href.indexOf("/guide/") != -1) {
507 cookiePath = "guide_";
508 } else if (location.href.indexOf("/tools/") != -1) {
509 cookiePath = "tools_";
510 } else if (location.href.indexOf("/training/") != -1) {
511 cookiePath = "training_";
512 } else if (location.href.indexOf("/design/") != -1) {
513 cookiePath = "design_";
514 } else if (location.href.indexOf("/distribute/") != -1) {
515 cookiePath = "distribute_";
516 }
Scott Maine4d8f1b2012-06-21 18:03:05 -0700517
518});
Scott Main7e447ed2013-02-19 17:22:37 -0800519// END of the onload event
Scott Maine4d8f1b2012-06-21 18:03:05 -0700520
521
Scott Mainad08f072013-08-20 16:49:57 -0700522function initExpandableNavItems(rootTag) {
523 $(rootTag + ' li.nav-section .nav-section-header').click(function() {
524 var section = $(this).closest('li.nav-section');
525 if (section.hasClass('expanded')) {
Scott Mainf0093852013-08-22 11:37:11 -0700526 /* hide me and descendants */
527 section.find('ul').slideUp(250, function() {
528 // remove 'expanded' class from my section and any children
Scott Mainad08f072013-08-20 16:49:57 -0700529 section.closest('li').removeClass('expanded');
Scott Mainf0093852013-08-22 11:37:11 -0700530 $('li.nav-section', section).removeClass('expanded');
Scott Mainad08f072013-08-20 16:49:57 -0700531 resizeNav();
532 });
533 } else {
534 /* show me */
535 // first hide all other siblings
Scott Main70557ee2013-10-30 14:47:40 -0700536 var $others = $('li.nav-section.expanded', $(this).closest('ul')).not('.sticky');
Scott Mainad08f072013-08-20 16:49:57 -0700537 $others.removeClass('expanded').children('ul').slideUp(250);
538
539 // now expand me
540 section.closest('li').addClass('expanded');
541 section.children('ul').slideDown(250, function() {
542 resizeNav();
543 });
544 }
545 });
Scott Mainf0093852013-08-22 11:37:11 -0700546
547 // Stop expand/collapse behavior when clicking on nav section links
548 // (since we're navigating away from the page)
549 // This selector captures the first instance of <a>, but not those with "#" as the href.
550 $('.nav-section-header').find('a:eq(0)').not('a[href="#"]').click(function(evt) {
551 window.location.href = $(this).attr('href');
552 return false;
553 });
Scott Mainad08f072013-08-20 16:49:57 -0700554}
555
Dirk Doughertyc3921652014-05-13 16:55:26 -0700556
557/** Create the list of breadcrumb links in the sticky header */
558function buildBreadcrumbs() {
559 var $breadcrumbUl = $("#sticky-header ul.breadcrumb");
560 // Add the secondary horizontal nav item, if provided
561 var $selectedSecondNav = $("div#nav-x ul.nav-x a.selected").clone().removeClass("selected");
562 if ($selectedSecondNav.length) {
563 $breadcrumbUl.prepend($("<li>").append($selectedSecondNav))
564 }
565 // Add the primary horizontal nav
566 var $selectedFirstNav = $("div#header-wrap ul.nav-x a.selected").clone().removeClass("selected");
567 // If there's no header nav item, use the logo link and title from alt text
568 if ($selectedFirstNav.length < 1) {
569 $selectedFirstNav = $("<a>")
570 .attr('href', $("div#header .logo a").attr('href'))
571 .text($("div#header .logo img").attr('alt'));
572 }
573 $breadcrumbUl.prepend($("<li>").append($selectedFirstNav));
574}
575
576
577
Scott Maine624b3f2013-09-12 12:56:41 -0700578/** Highlight the current page in sidenav, expanding children as appropriate */
Scott Mainf6145542013-04-01 16:38:11 -0700579function highlightSidenav() {
Scott Maine624b3f2013-09-12 12:56:41 -0700580 // if something is already highlighted, undo it. This is for dynamic navigation (Samples index)
581 if ($("ul#nav li.selected").length) {
582 unHighlightSidenav();
583 }
584 // look for URL in sidenav, including the hash
585 var $selNavLink = $('#nav').find('a[href="' + mPagePath + location.hash + '"]');
586
587 // If the selNavLink is still empty, look for it without the hash
588 if ($selNavLink.length == 0) {
589 $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]');
590 }
591
Scott Mainf6145542013-04-01 16:38:11 -0700592 var $selListItem;
593 if ($selNavLink.length) {
Scott Mainf6145542013-04-01 16:38:11 -0700594 // Find this page's <li> in sidenav and set selected
595 $selListItem = $selNavLink.closest('li');
596 $selListItem.addClass('selected');
Scott Main3b90aff2013-08-01 18:09:35 -0700597
Scott Mainf6145542013-04-01 16:38:11 -0700598 // Traverse up the tree and expand all parent nav-sections
599 $selNavLink.parents('li.nav-section').each(function() {
600 $(this).addClass('expanded');
601 $(this).children('ul').show();
602 });
603 }
604}
605
Scott Maine624b3f2013-09-12 12:56:41 -0700606function unHighlightSidenav() {
607 $("ul#nav li.selected").removeClass("selected");
608 $('ul#nav li.nav-section.expanded').removeClass('expanded').children('ul').hide();
609}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700610
611function toggleFullscreen(enable) {
612 var delay = 20;
613 var enabled = true;
614 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
615 if (enable) {
616 // Currently NOT USING fullscreen; enable fullscreen
617 stylesheet.removeAttr('disabled');
618 $('#nav-swap .fullscreen').removeClass('disabled');
619 $('#devdoc-nav').css({left:''});
620 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
621 enabled = true;
622 } else {
623 // Currently USING fullscreen; disable fullscreen
624 stylesheet.attr('disabled', 'disabled');
625 $('#nav-swap .fullscreen').addClass('disabled');
626 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
627 enabled = false;
628 }
629 writeCookie("fullscreen", enabled, null, null);
630 setNavBarLeftPos();
631 resizeNav(delay);
632 updateSideNavPosition();
633 setTimeout(initSidenavHeightResize,delay);
634}
635
636
637function setNavBarLeftPos() {
638 navBarLeftPos = $('#body-content').offset().left;
639}
640
641
642function updateSideNavPosition() {
643 var newLeft = $(window).scrollLeft() - navBarLeftPos;
644 $('#devdoc-nav').css({left: -newLeft});
645 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
646}
Scott Main3b90aff2013-08-01 18:09:35 -0700647
Scott Maine4d8f1b2012-06-21 18:03:05 -0700648// TODO: use $(document).ready instead
649function addLoadEvent(newfun) {
650 var current = window.onload;
651 if (typeof window.onload != 'function') {
652 window.onload = newfun;
653 } else {
654 window.onload = function() {
655 current();
656 newfun();
657 }
658 }
659}
660
661var agent = navigator['userAgent'].toLowerCase();
662// If a mobile phone, set flag and do mobile setup
663if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
664 (agent.indexOf("blackberry") != -1) ||
665 (agent.indexOf("webos") != -1) ||
666 (agent.indexOf("mini") != -1)) { // opera mini browsers
667 isMobile = true;
668}
669
670
Scott Main498d7102013-08-21 15:47:38 -0700671$(document).ready(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700672 $("pre:not(.no-pretty-print)").addClass("prettyprint");
673 prettyPrint();
Scott Main498d7102013-08-21 15:47:38 -0700674});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700675
Scott Maine4d8f1b2012-06-21 18:03:05 -0700676
677
678
679/* ######### RESIZE THE SIDENAV HEIGHT ########## */
680
681function resizeNav(delay) {
682 var $nav = $("#devdoc-nav");
683 var $window = $(window);
684 var navHeight;
Scott Main3b90aff2013-08-01 18:09:35 -0700685
Scott Maine4d8f1b2012-06-21 18:03:05 -0700686 // Get the height of entire window and the total header height.
687 // Then figure out based on scroll position whether the header is visible
688 var windowHeight = $window.height();
689 var scrollTop = $window.scrollTop();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700690 var headerHeight = $('#header-wrapper').outerHeight();
691 var headerVisible = scrollTop < stickyTop;
Scott Main3b90aff2013-08-01 18:09:35 -0700692
693 // get the height of space between nav and top of window.
Scott Maine4d8f1b2012-06-21 18:03:05 -0700694 // Could be either margin or top position, depending on whether the nav is fixed.
Scott Main3b90aff2013-08-01 18:09:35 -0700695 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700696 // add 1 for the #side-nav bottom margin
Scott Main3b90aff2013-08-01 18:09:35 -0700697
Scott Maine4d8f1b2012-06-21 18:03:05 -0700698 // Depending on whether the header is visible, set the side nav's height.
699 if (headerVisible) {
700 // The sidenav height grows as the header goes off screen
Dirk Doughertyc3921652014-05-13 16:55:26 -0700701 navHeight = windowHeight - (headerHeight - scrollTop) - topMargin;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700702 } else {
703 // Once header is off screen, the nav height is almost full window height
704 navHeight = windowHeight - topMargin;
705 }
Scott Main3b90aff2013-08-01 18:09:35 -0700706
707
708
Scott Maine4d8f1b2012-06-21 18:03:05 -0700709 $scrollPanes = $(".scroll-pane");
710 if ($scrollPanes.length > 1) {
711 // subtract the height of the api level widget and nav swapper from the available nav height
712 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
Scott Main3b90aff2013-08-01 18:09:35 -0700713
Scott Maine4d8f1b2012-06-21 18:03:05 -0700714 $("#swapper").css({height:navHeight + "px"});
715 if ($("#nav-tree").is(":visible")) {
716 $("#nav-tree").css({height:navHeight});
717 }
Scott Main3b90aff2013-08-01 18:09:35 -0700718
719 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
Scott Maine4d8f1b2012-06-21 18:03:05 -0700720 //subtract 10px to account for drag bar
Scott Main3b90aff2013-08-01 18:09:35 -0700721
722 // if the window becomes small enough to make the class panel height 0,
Scott Maine4d8f1b2012-06-21 18:03:05 -0700723 // then the package panel should begin to shrink
724 if (parseInt(classesHeight) <= 0) {
725 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
726 $("#packages-nav").css({height:navHeight - 10});
727 }
Scott Main3b90aff2013-08-01 18:09:35 -0700728
Scott Maine4d8f1b2012-06-21 18:03:05 -0700729 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
730 $("#classes-nav .jspContainer").css({height:classesHeight});
Scott Main3b90aff2013-08-01 18:09:35 -0700731
732
Scott Maine4d8f1b2012-06-21 18:03:05 -0700733 } else {
734 $nav.height(navHeight);
735 }
Scott Main3b90aff2013-08-01 18:09:35 -0700736
Scott Maine4d8f1b2012-06-21 18:03:05 -0700737 if (delay) {
738 updateFromResize = true;
739 delayedReInitScrollbars(delay);
740 } else {
741 reInitScrollbars();
742 }
Scott Main3b90aff2013-08-01 18:09:35 -0700743
Scott Maine4d8f1b2012-06-21 18:03:05 -0700744}
745
746var updateScrollbars = false;
747var updateFromResize = false;
748
749/* Re-initialize the scrollbars to account for changed nav size.
750 * This method postpones the actual update by a 1/4 second in order to optimize the
751 * scroll performance while the header is still visible, because re-initializing the
752 * scroll panes is an intensive process.
753 */
754function delayedReInitScrollbars(delay) {
755 // If we're scheduled for an update, but have received another resize request
756 // before the scheduled resize has occured, just ignore the new request
757 // (and wait for the scheduled one).
758 if (updateScrollbars && updateFromResize) {
759 updateFromResize = false;
760 return;
761 }
Scott Main3b90aff2013-08-01 18:09:35 -0700762
Scott Maine4d8f1b2012-06-21 18:03:05 -0700763 // We're scheduled for an update and the update request came from this method's setTimeout
764 if (updateScrollbars && !updateFromResize) {
765 reInitScrollbars();
766 updateScrollbars = false;
767 } else {
768 updateScrollbars = true;
769 updateFromResize = false;
770 setTimeout('delayedReInitScrollbars()',delay);
771 }
772}
773
774/* Re-initialize the scrollbars to account for changed nav size. */
775function reInitScrollbars() {
776 var pane = $(".scroll-pane").each(function(){
777 var api = $(this).data('jsp');
778 if (!api) { setTimeout(reInitScrollbars,300); return;}
779 api.reinitialise( {verticalGutter:0} );
Scott Main3b90aff2013-08-01 18:09:35 -0700780 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700781 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
782}
783
784
785/* Resize the height of the nav panels in the reference,
786 * and save the new size to a cookie */
787function saveNavPanels() {
788 var basePath = getBaseUri(location.pathname);
789 var section = basePath.substring(1,basePath.indexOf("/",1));
790 writeCookie("height", resizePackagesNav.css("height"), section, null);
791}
792
793
794
795function restoreHeight(packageHeight) {
796 $("#resize-packages-nav").height(packageHeight);
797 $("#packages-nav").height(packageHeight);
798 // var classesHeight = navHeight - packageHeight;
799 // $("#classes-nav").css({height:classesHeight});
800 // $("#classes-nav .jspContainer").css({height:classesHeight});
801}
802
803
804
805/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
806
807
808
809
810
Scott Main3b90aff2013-08-01 18:09:35 -0700811/** Scroll the jScrollPane to make the currently selected item visible
Scott Maine4d8f1b2012-06-21 18:03:05 -0700812 This is called when the page finished loading. */
813function scrollIntoView(nav) {
814 var $nav = $("#"+nav);
815 var element = $nav.jScrollPane({/* ...settings... */});
816 var api = element.data('jsp');
817
818 if ($nav.is(':visible')) {
819 var $selected = $(".selected", $nav);
Scott Mainbc729572013-07-30 18:00:51 -0700820 if ($selected.length == 0) {
821 // If no selected item found, exit
822 return;
823 }
Scott Main52dd2062013-08-15 12:22:28 -0700824 // get the selected item's offset from its container nav by measuring the item's offset
825 // relative to the document then subtract the container nav's offset relative to the document
826 var selectedOffset = $selected.offset().top - $nav.offset().top;
827 if (selectedOffset > $nav.height() * .8) { // multiply nav height by .8 so we move up the item
828 // if it's more than 80% down the nav
829 // scroll the item up by an amount equal to 80% the container nav's height
830 api.scrollTo(0, selectedOffset - ($nav.height() * .8), false);
Scott Maine4d8f1b2012-06-21 18:03:05 -0700831 }
832 }
833}
834
835
836
837
838
839
840/* Show popup dialogs */
841function showDialog(id) {
842 $dialog = $("#"+id);
843 $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>');
844 $dialog.wrapInner('<div/>');
845 $dialog.removeClass("hide");
846}
847
848
849
850
851
852/* ######### COOKIES! ########## */
853
854function readCookie(cookie) {
855 var myCookie = cookie_namespace+"_"+cookie+"=";
856 if (document.cookie) {
857 var index = document.cookie.indexOf(myCookie);
858 if (index != -1) {
859 var valStart = index + myCookie.length;
860 var valEnd = document.cookie.indexOf(";", valStart);
861 if (valEnd == -1) {
862 valEnd = document.cookie.length;
863 }
864 var val = document.cookie.substring(valStart, valEnd);
865 return val;
866 }
867 }
868 return 0;
869}
870
871function writeCookie(cookie, val, section, expiration) {
872 if (val==undefined) return;
873 section = section == null ? "_" : "_"+section+"_";
874 if (expiration == null) {
875 var date = new Date();
876 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
877 expiration = date.toGMTString();
878 }
Scott Main3b90aff2013-08-01 18:09:35 -0700879 var cookieValue = cookie_namespace + section + cookie + "=" + val
Scott Maine4d8f1b2012-06-21 18:03:05 -0700880 + "; expires=" + expiration+"; path=/";
881 document.cookie = cookieValue;
882}
883
884/* ######### END COOKIES! ########## */
885
886
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700887var sticky = false;
Dirk Doughertyc3921652014-05-13 16:55:26 -0700888var stickyTop;
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700889var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
Dirk Doughertyc3921652014-05-13 16:55:26 -0700890/* Sets the vertical scoll position at which the sticky bar should appear.
891 This method is called to reset the position when search results appear or hide */
892function setStickyTop() {
893 stickyTop = $('#header-wrapper').outerHeight() - $('#sticky-header').outerHeight();
894}
Scott Maine4d8f1b2012-06-21 18:03:05 -0700895
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700896/*
Scott Mainb16376f2014-05-21 20:35:47 -0700897 * Displays sticky nav bar on pages when dac header scrolls out of view
Dirk Doughertyc3921652014-05-13 16:55:26 -0700898 */
Dirk Doughertyca1230c2014-05-14 20:00:03 -0700899$(window).scroll(function(event) {
900
901 setStickyTop();
902 var hiding = false;
903 var $stickyEl = $('#sticky-header');
904 var $menuEl = $('.menu-container');
905 // Exit if there's no sidenav
906 if ($('#side-nav').length == 0) return;
907 // Exit if the mouse target is a DIV, because that means the event is coming
908 // from a scrollable div and so there's no need to make adjustments to our layout
909 if ($(event.target).nodeName == "DIV") {
910 return;
911 }
912
913 var top = $(window).scrollTop();
914 // we set the navbar fixed when the scroll position is beyond the height of the site header...
915 var shouldBeSticky = top >= stickyTop;
916 // ... except if the document content is shorter than the sidenav height.
917 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing)
918 if ($("#doc-col").height() < $("#side-nav").height()) {
919 shouldBeSticky = false;
920 }
921
922 // Don't continue if the header is sufficently far away
923 // (to avoid intensive resizing that slows scrolling)
924 if (sticky == shouldBeSticky) {
925 return;
926 }
927 // Account for horizontal scroll
928 var scrollLeft = $(window).scrollLeft();
929 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
930 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
931 updateSideNavPosition();
932 prevScrollLeft = scrollLeft;
933 }
934
935 // If sticky header visible and position is now near top, hide sticky
936 if (sticky && !shouldBeSticky) {
937 sticky = false;
938 hiding = true;
939 // make the sidenav static again
940 $('#devdoc-nav')
941 .removeClass('fixed')
942 .css({'width':'auto','margin':''})
943 .prependTo('#side-nav');
944 // delay hide the sticky
945 $menuEl.removeClass('sticky-menu');
946 $stickyEl.fadeOut(250);
947 hiding = false;
948
949 // update the sidenaav position for side scrolling
950 updateSideNavPosition();
951 } else if (!sticky && shouldBeSticky) {
952 sticky = true;
953 $stickyEl.fadeIn(10);
954 $menuEl.addClass('sticky-menu');
955
956 // make the sidenav fixed
957 var width = $('#devdoc-nav').width();
958 $('#devdoc-nav')
959 .addClass('fixed')
960 .css({'width':width+'px'})
961 .prependTo('#body-content');
962
963 // update the sidenaav position for side scrolling
964 updateSideNavPosition();
965
966 } else if (hiding && top < 15) {
967 $menuEl.removeClass('sticky-menu');
968 $stickyEl.hide();
969 hiding = false;
970 }
971 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
972});
973
974/*
975 * Manages secion card states and nav resize to conclude loading
976 */
Dirk Doughertyc3921652014-05-13 16:55:26 -0700977(function() {
978 $(document).ready(function() {
979
Dirk Doughertyc3921652014-05-13 16:55:26 -0700980 // Stack hover states
981 $('.section-card-menu').each(function(index, el) {
982 var height = $(el).height();
983 $(el).css({height:height+'px', position:'relative'});
984 var $cardInfo = $(el).find('.card-info');
985
986 $cardInfo.css({position: 'absolute', bottom:'0px', left:'0px', right:'0px', overflow:'visible'});
987 });
988
Scott Mainb16376f2014-05-21 20:35:47 -0700989 // Resize once loading is finished
990 resizeNav();
991 // Check if there's an anchor that we need to scroll into view
992 offsetScrollForSticky();
Dirk Doughertyc3921652014-05-13 16:55:26 -0700993 });
994
995})();
996
Scott Maine4d8f1b2012-06-21 18:03:05 -0700997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
Scott Maind7026f72013-06-17 15:08:49 -07001010/* MISC LIBRARY FUNCTIONS */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001011
1012
1013
1014
1015
1016function toggle(obj, slide) {
1017 var ul = $("ul:first", obj);
1018 var li = ul.parent();
1019 if (li.hasClass("closed")) {
1020 if (slide) {
1021 ul.slideDown("fast");
1022 } else {
1023 ul.show();
1024 }
1025 li.removeClass("closed");
1026 li.addClass("open");
1027 $(".toggle-img", li).attr("title", "hide pages");
1028 } else {
1029 ul.slideUp("fast");
1030 li.removeClass("open");
1031 li.addClass("closed");
1032 $(".toggle-img", li).attr("title", "show pages");
1033 }
1034}
1035
1036
Scott Maine4d8f1b2012-06-21 18:03:05 -07001037function buildToggleLists() {
1038 $(".toggle-list").each(
1039 function(i) {
1040 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
1041 $(this).addClass("closed");
1042 });
1043}
1044
1045
1046
Scott Maind7026f72013-06-17 15:08:49 -07001047function hideNestedItems(list, toggle) {
1048 $list = $(list);
1049 // hide nested lists
1050 if($list.hasClass('showing')) {
1051 $("li ol", $list).hide('fast');
1052 $list.removeClass('showing');
1053 // show nested lists
1054 } else {
1055 $("li ol", $list).show('fast');
1056 $list.addClass('showing');
1057 }
1058 $(".more,.less",$(toggle)).toggle();
1059}
Scott Maine4d8f1b2012-06-21 18:03:05 -07001060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088/* REFERENCE NAV SWAP */
1089
1090
1091function getNavPref() {
1092 var v = readCookie('reference_nav');
1093 if (v != NAV_PREF_TREE) {
1094 v = NAV_PREF_PANELS;
1095 }
1096 return v;
1097}
1098
1099function chooseDefaultNav() {
1100 nav_pref = getNavPref();
1101 if (nav_pref == NAV_PREF_TREE) {
1102 $("#nav-panels").toggle();
1103 $("#panel-link").toggle();
1104 $("#nav-tree").toggle();
1105 $("#tree-link").toggle();
1106 }
1107}
1108
1109function swapNav() {
1110 if (nav_pref == NAV_PREF_TREE) {
1111 nav_pref = NAV_PREF_PANELS;
1112 } else {
1113 nav_pref = NAV_PREF_TREE;
1114 init_default_navtree(toRoot);
1115 }
1116 var date = new Date();
1117 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1118 writeCookie("nav", nav_pref, "reference", date.toGMTString());
1119
1120 $("#nav-panels").toggle();
1121 $("#panel-link").toggle();
1122 $("#nav-tree").toggle();
1123 $("#tree-link").toggle();
Scott Main3b90aff2013-08-01 18:09:35 -07001124
Scott Maine4d8f1b2012-06-21 18:03:05 -07001125 resizeNav();
1126
1127 // Gross nasty hack to make tree view show up upon first swap by setting height manually
1128 $("#nav-tree .jspContainer:visible")
1129 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
1130 // Another nasty hack to make the scrollbar appear now that we have height
1131 resizeNav();
Scott Main3b90aff2013-08-01 18:09:35 -07001132
Scott Maine4d8f1b2012-06-21 18:03:05 -07001133 if ($("#nav-tree").is(':visible')) {
1134 scrollIntoView("nav-tree");
1135 } else {
1136 scrollIntoView("packages-nav");
1137 scrollIntoView("classes-nav");
1138 }
1139}
1140
1141
1142
Scott Mainf5089842012-08-14 16:31:07 -07001143/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001144/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -07001145/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -07001146
1147function getBaseUri(uri) {
1148 var intlUrl = (uri.substring(0,6) == "/intl/");
1149 if (intlUrl) {
1150 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
1151 base = base.substring(base.indexOf('/')+1, base.length);
1152 //alert("intl, returning base url: /" + base);
1153 return ("/" + base);
1154 } else {
1155 //alert("not intl, returning uri as found.");
1156 return uri;
1157 }
1158}
1159
1160function requestAppendHL(uri) {
1161//append "?hl=<lang> to an outgoing request (such as to blog)
1162 var lang = getLangPref();
1163 if (lang) {
1164 var q = 'hl=' + lang;
1165 uri += '?' + q;
1166 window.location = uri;
1167 return false;
1168 } else {
1169 return true;
1170 }
1171}
1172
1173
Scott Maine4d8f1b2012-06-21 18:03:05 -07001174function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001175 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1176 $links.each(function(i){ // for each link with a translation
1177 var $link = $(this);
1178 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1179 // put the desired language from the attribute as the text
1180 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001181 }
Scott Main6eb95f12012-10-02 17:12:23 -07001182 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001183}
1184
Scott Main015d6162013-01-29 09:01:52 -08001185function changeLangPref(lang, submit) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001186 var date = new Date();
Scott Main3b90aff2013-08-01 18:09:35 -07001187 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
Scott Maine4d8f1b2012-06-21 18:03:05 -07001188 // keep this for 50 years
1189 //alert("expires: " + expires)
1190 writeCookie("pref_lang", lang, null, expires);
Scott Main015d6162013-01-29 09:01:52 -08001191
1192 // ####### TODO: Remove this condition once we're stable on devsite #######
1193 // This condition is only needed if we still need to support legacy GAE server
1194 if (devsite) {
1195 // Switch language when on Devsite server
1196 if (submit) {
1197 $("#setlang").submit();
1198 }
1199 } else {
1200 // Switch language when on legacy GAE server
Scott Main015d6162013-01-29 09:01:52 -08001201 if (submit) {
1202 window.location = getBaseUri(location.pathname);
1203 }
Scott Maine4d8f1b2012-06-21 18:03:05 -07001204 }
1205}
1206
1207function loadLangPref() {
1208 var lang = readCookie("pref_lang");
1209 if (lang != 0) {
1210 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1211 }
1212}
1213
1214function getLangPref() {
1215 var lang = $("#language").find(":selected").attr("value");
1216 if (!lang) {
1217 lang = readCookie("pref_lang");
1218 }
1219 return (lang != 0) ? lang : 'en';
1220}
1221
1222/* ########## END LOCALIZATION ############ */
1223
1224
1225
1226
1227
1228
1229/* Used to hide and reveal supplemental content, such as long code samples.
1230 See the companion CSS in android-developer-docs.css */
1231function toggleContent(obj) {
Scott Maindc63dda2013-08-22 16:03:21 -07001232 var div = $(obj).closest(".toggle-content");
1233 var toggleMe = $(".toggle-content-toggleme:eq(0)",div);
Scott Maine4d8f1b2012-06-21 18:03:05 -07001234 if (div.hasClass("closed")) { // if it's closed, open it
1235 toggleMe.slideDown();
Scott Maindc63dda2013-08-22 16:03:21 -07001236 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001237 div.removeClass("closed").addClass("open");
Scott Maindc63dda2013-08-22 16:03:21 -07001238 $(".toggle-content-img:eq(0)", div).attr("title", "hide").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001239 + "assets/images/triangle-opened.png");
1240 } else { // if it's open, close it
1241 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
Scott Maindc63dda2013-08-22 16:03:21 -07001242 $(".toggle-content-text:eq(0)", obj).toggle();
Scott Maine4d8f1b2012-06-21 18:03:05 -07001243 div.removeClass("open").addClass("closed");
Scott Maindc63dda2013-08-22 16:03:21 -07001244 div.find(".toggle-content").removeClass("open").addClass("closed")
1245 .find(".toggle-content-toggleme").hide();
Scott Main3b90aff2013-08-01 18:09:35 -07001246 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
Scott Maine4d8f1b2012-06-21 18:03:05 -07001247 + "assets/images/triangle-closed.png");
1248 });
1249 }
1250 return false;
1251}
Scott Mainf5089842012-08-14 16:31:07 -07001252
1253
Scott Maindb3678b2012-10-23 14:13:41 -07001254/* New version of expandable content */
1255function toggleExpandable(link,id) {
1256 if($(id).is(':visible')) {
1257 $(id).slideUp();
1258 $(link).removeClass('expanded');
1259 } else {
1260 $(id).slideDown();
1261 $(link).addClass('expanded');
1262 }
1263}
1264
1265function hideExpandable(ids) {
1266 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001267 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001268}
1269
Scott Mainf5089842012-08-14 16:31:07 -07001270
1271
1272
1273
Scott Main3b90aff2013-08-01 18:09:35 -07001274/*
Scott Mainf5089842012-08-14 16:31:07 -07001275 * Slideshow 1.0
1276 * Used on /index.html and /develop/index.html for carousel
1277 *
1278 * Sample usage:
1279 * HTML -
1280 * <div class="slideshow-container">
1281 * <a href="" class="slideshow-prev">Prev</a>
1282 * <a href="" class="slideshow-next">Next</a>
1283 * <ul>
1284 * <li class="item"><img src="images/marquee1.jpg"></li>
1285 * <li class="item"><img src="images/marquee2.jpg"></li>
1286 * <li class="item"><img src="images/marquee3.jpg"></li>
1287 * <li class="item"><img src="images/marquee4.jpg"></li>
1288 * </ul>
1289 * </div>
1290 *
1291 * <script type="text/javascript">
1292 * $('.slideshow-container').dacSlideshow({
1293 * auto: true,
1294 * btnPrev: '.slideshow-prev',
1295 * btnNext: '.slideshow-next'
1296 * });
1297 * </script>
1298 *
1299 * Options:
1300 * btnPrev: optional identifier for previous button
1301 * btnNext: optional identifier for next button
Scott Maineb410352013-01-14 19:03:40 -08001302 * btnPause: optional identifier for pause button
Scott Mainf5089842012-08-14 16:31:07 -07001303 * auto: whether or not to auto-proceed
1304 * speed: animation speed
1305 * autoTime: time between auto-rotation
1306 * easing: easing function for transition
1307 * start: item to select by default
1308 * scroll: direction to scroll in
1309 * pagination: whether or not to include dotted pagination
1310 *
1311 */
1312
1313 (function($) {
1314 $.fn.dacSlideshow = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001315
Scott Mainf5089842012-08-14 16:31:07 -07001316 //Options - see above
1317 o = $.extend({
1318 btnPrev: null,
1319 btnNext: null,
Scott Maineb410352013-01-14 19:03:40 -08001320 btnPause: null,
Scott Mainf5089842012-08-14 16:31:07 -07001321 auto: true,
1322 speed: 500,
1323 autoTime: 12000,
1324 easing: null,
1325 start: 0,
1326 scroll: 1,
1327 pagination: true
1328
1329 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001330
1331 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001332 return this.each(function() {
1333
1334 var running = false;
1335 var animCss = o.vertical ? "top" : "left";
1336 var sizeCss = o.vertical ? "height" : "width";
1337 var div = $(this);
1338 var ul = $("ul", div);
1339 var tLi = $("li", ul);
Scott Main3b90aff2013-08-01 18:09:35 -07001340 var tl = tLi.size();
Scott Mainf5089842012-08-14 16:31:07 -07001341 var timer = null;
1342
1343 var li = $("li", ul);
1344 var itemLength = li.size();
1345 var curr = o.start;
1346
1347 li.css({float: o.vertical ? "none" : "left"});
1348 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1349 div.css({position: "relative", "z-index": "2", left: "0px"});
1350
1351 var liSize = o.vertical ? height(li) : width(li);
1352 var ulSize = liSize * itemLength;
1353 var divSize = liSize;
1354
1355 li.css({width: li.width(), height: li.height()});
1356 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1357
1358 div.css(sizeCss, divSize+"px");
Scott Main3b90aff2013-08-01 18:09:35 -07001359
Scott Mainf5089842012-08-14 16:31:07 -07001360 //Pagination
1361 if (o.pagination) {
1362 var pagination = $("<div class='pagination'></div>");
1363 var pag_ul = $("<ul></ul>");
1364 if (tl > 1) {
1365 for (var i=0;i<tl;i++) {
1366 var li = $("<li>"+i+"</li>");
1367 pag_ul.append(li);
1368 if (i==o.start) li.addClass('active');
1369 li.click(function() {
1370 go(parseInt($(this).text()));
1371 })
1372 }
1373 pagination.append(pag_ul);
1374 div.append(pagination);
1375 }
1376 }
Scott Main3b90aff2013-08-01 18:09:35 -07001377
Scott Mainf5089842012-08-14 16:31:07 -07001378 //Previous button
1379 if(o.btnPrev)
1380 $(o.btnPrev).click(function(e) {
1381 e.preventDefault();
1382 return go(curr-o.scroll);
1383 });
1384
1385 //Next button
1386 if(o.btnNext)
1387 $(o.btnNext).click(function(e) {
1388 e.preventDefault();
1389 return go(curr+o.scroll);
1390 });
Scott Maineb410352013-01-14 19:03:40 -08001391
1392 //Pause button
1393 if(o.btnPause)
1394 $(o.btnPause).click(function(e) {
1395 e.preventDefault();
1396 if ($(this).hasClass('paused')) {
1397 startRotateTimer();
1398 } else {
1399 pauseRotateTimer();
1400 }
1401 });
Scott Main3b90aff2013-08-01 18:09:35 -07001402
Scott Mainf5089842012-08-14 16:31:07 -07001403 //Auto rotation
1404 if(o.auto) startRotateTimer();
Scott Main3b90aff2013-08-01 18:09:35 -07001405
Scott Mainf5089842012-08-14 16:31:07 -07001406 function startRotateTimer() {
1407 clearInterval(timer);
1408 timer = setInterval(function() {
1409 if (curr == tl-1) {
1410 go(0);
1411 } else {
Scott Main3b90aff2013-08-01 18:09:35 -07001412 go(curr+o.scroll);
1413 }
Scott Mainf5089842012-08-14 16:31:07 -07001414 }, o.autoTime);
Scott Maineb410352013-01-14 19:03:40 -08001415 $(o.btnPause).removeClass('paused');
1416 }
1417
1418 function pauseRotateTimer() {
1419 clearInterval(timer);
1420 $(o.btnPause).addClass('paused');
Scott Mainf5089842012-08-14 16:31:07 -07001421 }
1422
1423 //Go to an item
1424 function go(to) {
1425 if(!running) {
1426
1427 if(to<0) {
1428 to = itemLength-1;
1429 } else if (to>itemLength-1) {
1430 to = 0;
1431 }
1432 curr = to;
1433
1434 running = true;
1435
1436 ul.animate(
1437 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1438 function() {
1439 running = false;
1440 }
1441 );
1442
1443 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1444 $( (curr-o.scroll<0 && o.btnPrev)
1445 ||
1446 (curr+o.scroll > itemLength && o.btnNext)
1447 ||
1448 []
1449 ).addClass("disabled");
1450
Scott Main3b90aff2013-08-01 18:09:35 -07001451
Scott Mainf5089842012-08-14 16:31:07 -07001452 var nav_items = $('li', pagination);
1453 nav_items.removeClass('active');
1454 nav_items.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001455
Scott Mainf5089842012-08-14 16:31:07 -07001456
1457 }
1458 if(o.auto) startRotateTimer();
1459 return false;
1460 };
1461 });
1462 };
1463
1464 function css(el, prop) {
1465 return parseInt($.css(el[0], prop)) || 0;
1466 };
1467 function width(el) {
1468 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1469 };
1470 function height(el) {
1471 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1472 };
1473
1474 })(jQuery);
1475
1476
Scott Main3b90aff2013-08-01 18:09:35 -07001477/*
Scott Mainf5089842012-08-14 16:31:07 -07001478 * dacSlideshow 1.0
1479 * Used on develop/index.html for side-sliding tabs
1480 *
1481 * Sample usage:
1482 * HTML -
1483 * <div class="slideshow-container">
1484 * <a href="" class="slideshow-prev">Prev</a>
1485 * <a href="" class="slideshow-next">Next</a>
1486 * <ul>
1487 * <li class="item"><img src="images/marquee1.jpg"></li>
1488 * <li class="item"><img src="images/marquee2.jpg"></li>
1489 * <li class="item"><img src="images/marquee3.jpg"></li>
1490 * <li class="item"><img src="images/marquee4.jpg"></li>
1491 * </ul>
1492 * </div>
1493 *
1494 * <script type="text/javascript">
1495 * $('.slideshow-container').dacSlideshow({
1496 * auto: true,
1497 * btnPrev: '.slideshow-prev',
1498 * btnNext: '.slideshow-next'
1499 * });
1500 * </script>
1501 *
1502 * Options:
1503 * btnPrev: optional identifier for previous button
1504 * btnNext: optional identifier for next button
1505 * auto: whether or not to auto-proceed
1506 * speed: animation speed
1507 * autoTime: time between auto-rotation
1508 * easing: easing function for transition
1509 * start: item to select by default
1510 * scroll: direction to scroll in
1511 * pagination: whether or not to include dotted pagination
1512 *
1513 */
1514 (function($) {
1515 $.fn.dacTabbedList = function(o) {
Scott Main3b90aff2013-08-01 18:09:35 -07001516
Scott Mainf5089842012-08-14 16:31:07 -07001517 //Options - see above
1518 o = $.extend({
1519 speed : 250,
1520 easing: null,
1521 nav_id: null,
1522 frame_id: null
1523 }, o || {});
Scott Main3b90aff2013-08-01 18:09:35 -07001524
1525 //Set up a carousel for each
Scott Mainf5089842012-08-14 16:31:07 -07001526 return this.each(function() {
1527
1528 var curr = 0;
1529 var running = false;
1530 var animCss = "margin-left";
1531 var sizeCss = "width";
1532 var div = $(this);
Scott Main3b90aff2013-08-01 18:09:35 -07001533
Scott Mainf5089842012-08-14 16:31:07 -07001534 var nav = $(o.nav_id, div);
1535 var nav_li = $("li", nav);
Scott Main3b90aff2013-08-01 18:09:35 -07001536 var nav_size = nav_li.size();
Scott Mainf5089842012-08-14 16:31:07 -07001537 var frame = div.find(o.frame_id);
1538 var content_width = $(frame).find('ul').width();
1539 //Buttons
1540 $(nav_li).click(function(e) {
1541 go($(nav_li).index($(this)));
1542 })
Scott Main3b90aff2013-08-01 18:09:35 -07001543
Scott Mainf5089842012-08-14 16:31:07 -07001544 //Go to an item
1545 function go(to) {
1546 if(!running) {
1547 curr = to;
1548 running = true;
1549
1550 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1551 function() {
1552 running = false;
1553 }
1554 );
1555
Scott Main3b90aff2013-08-01 18:09:35 -07001556
Scott Mainf5089842012-08-14 16:31:07 -07001557 nav_li.removeClass('active');
1558 nav_li.eq(to).addClass('active');
Scott Main3b90aff2013-08-01 18:09:35 -07001559
Scott Mainf5089842012-08-14 16:31:07 -07001560
1561 }
1562 return false;
1563 };
1564 });
1565 };
1566
1567 function css(el, prop) {
1568 return parseInt($.css(el[0], prop)) || 0;
1569 };
1570 function width(el) {
1571 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1572 };
1573 function height(el) {
1574 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1575 };
1576
1577 })(jQuery);
1578
1579
1580
1581
1582
1583/* ######################################################## */
1584/* ################ SEARCH SUGGESTIONS ################## */
1585/* ######################################################## */
1586
1587
Scott Main7e447ed2013-02-19 17:22:37 -08001588
Scott Main0e76e7e2013-03-12 10:24:07 -07001589var gSelectedIndex = -1; // the index position of currently highlighted suggestion
1590var gSelectedColumn = -1; // which column of suggestion lists is currently focused
1591
Scott Mainf5089842012-08-14 16:31:07 -07001592var gMatches = new Array();
1593var gLastText = "";
Scott Mainf5089842012-08-14 16:31:07 -07001594var gInitialized = false;
Scott Main7e447ed2013-02-19 17:22:37 -08001595var ROW_COUNT_FRAMEWORK = 20; // max number of results in list
1596var gListLength = 0;
1597
1598
1599var gGoogleMatches = new Array();
1600var ROW_COUNT_GOOGLE = 15; // max number of results in list
1601var gGoogleListLength = 0;
Scott Mainf5089842012-08-14 16:31:07 -07001602
Scott Main0e76e7e2013-03-12 10:24:07 -07001603var gDocsMatches = new Array();
1604var ROW_COUNT_DOCS = 100; // max number of results in list
1605var gDocsListLength = 0;
1606
Scott Mainde295272013-03-25 15:48:35 -07001607function onSuggestionClick(link) {
1608 // When user clicks a suggested document, track it
1609 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(),
1610 'from: ' + $("#search_autocomplete").val()]);
1611}
1612
Scott Mainf5089842012-08-14 16:31:07 -07001613function set_item_selected($li, selected)
1614{
1615 if (selected) {
1616 $li.attr('class','jd-autocomplete jd-selected');
1617 } else {
1618 $li.attr('class','jd-autocomplete');
1619 }
1620}
1621
1622function set_item_values(toroot, $li, match)
1623{
1624 var $link = $('a',$li);
1625 $link.html(match.__hilabel || match.label);
1626 $link.attr('href',toroot + match.link);
1627}
1628
Scott Main719acb42013-12-05 16:05:09 -08001629function set_item_values_jd(toroot, $li, match)
1630{
1631 var $link = $('a',$li);
1632 $link.html(match.title);
1633 $link.attr('href',toroot + match.url);
1634}
1635
Scott Main0e76e7e2013-03-12 10:24:07 -07001636function new_suggestion($list) {
Scott Main7e447ed2013-02-19 17:22:37 -08001637 var $li = $("<li class='jd-autocomplete'></li>");
1638 $list.append($li);
1639
1640 $li.mousedown(function() {
1641 window.location = this.firstChild.getAttribute("href");
1642 });
1643 $li.mouseover(function() {
Scott Main0e76e7e2013-03-12 10:24:07 -07001644 $('.search_filtered_wrapper li').removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001645 $(this).addClass('jd-selected');
Scott Main0e76e7e2013-03-12 10:24:07 -07001646 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered'));
1647 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this);
Scott Main7e447ed2013-02-19 17:22:37 -08001648 });
Scott Mainde295272013-03-25 15:48:35 -07001649 $li.append("<a onclick='onSuggestionClick(this)'></a>");
Scott Main7e447ed2013-02-19 17:22:37 -08001650 $li.attr('class','show-item');
1651 return $li;
1652}
1653
Scott Mainf5089842012-08-14 16:31:07 -07001654function sync_selection_table(toroot)
1655{
Scott Mainf5089842012-08-14 16:31:07 -07001656 var $li; //list item jquery object
1657 var i; //list item iterator
Scott Main7e447ed2013-02-19 17:22:37 -08001658
Scott Main0e76e7e2013-03-12 10:24:07 -07001659 // if there are NO results at all, hide all columns
1660 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) {
1661 $('.suggest-card').hide(300);
1662 return;
1663 }
1664
1665 // if there are api results
Scott Main7e447ed2013-02-19 17:22:37 -08001666 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001667 // reveal suggestion list
1668 $('.suggest-card.dummy').show();
1669 $('.suggest-card.reference').show();
1670 var listIndex = 0; // list index position
Scott Main7e447ed2013-02-19 17:22:37 -08001671
Scott Main0e76e7e2013-03-12 10:24:07 -07001672 // reset the lists
1673 $(".search_filtered_wrapper.reference li").remove();
Scott Main7e447ed2013-02-19 17:22:37 -08001674
Scott Main0e76e7e2013-03-12 10:24:07 -07001675 // ########### ANDROID RESULTS #############
1676 if (gMatches.length > 0) {
Scott Main7e447ed2013-02-19 17:22:37 -08001677
Scott Main0e76e7e2013-03-12 10:24:07 -07001678 // determine android results to show
1679 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ?
1680 gMatches.length : ROW_COUNT_FRAMEWORK;
1681 for (i=0; i<gListLength; i++) {
1682 var $li = new_suggestion($(".suggest-card.reference ul"));
1683 set_item_values(toroot, $li, gMatches[i]);
1684 set_item_selected($li, i == gSelectedIndex);
1685 }
1686 }
Scott Main7e447ed2013-02-19 17:22:37 -08001687
Scott Main0e76e7e2013-03-12 10:24:07 -07001688 // ########### GOOGLE RESULTS #############
1689 if (gGoogleMatches.length > 0) {
1690 // show header for list
1691 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>");
Scott Main7e447ed2013-02-19 17:22:37 -08001692
Scott Main0e76e7e2013-03-12 10:24:07 -07001693 // determine google results to show
1694 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE;
1695 for (i=0; i<gGoogleListLength; i++) {
1696 var $li = new_suggestion($(".suggest-card.reference ul"));
1697 set_item_values(toroot, $li, gGoogleMatches[i]);
1698 set_item_selected($li, i == gSelectedIndex);
1699 }
1700 }
Scott Mainf5089842012-08-14 16:31:07 -07001701 } else {
Scott Main0e76e7e2013-03-12 10:24:07 -07001702 $('.suggest-card.reference').hide();
1703 $('.suggest-card.dummy').hide();
1704 }
1705
1706 // ########### JD DOC RESULTS #############
1707 if (gDocsMatches.length > 0) {
1708 // reset the lists
1709 $(".search_filtered_wrapper.docs li").remove();
1710
1711 // determine google results to show
Scott Main719acb42013-12-05 16:05:09 -08001712 // NOTE: The order of the conditions below for the sugg.type MUST BE SPECIFIC:
1713 // The order must match the reverse order that each section appears as a card in
1714 // the suggestion UI... this may be only for the "develop" grouped items though.
Scott Main0e76e7e2013-03-12 10:24:07 -07001715 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS;
1716 for (i=0; i<gDocsListLength; i++) {
1717 var sugg = gDocsMatches[i];
1718 var $li;
1719 if (sugg.type == "design") {
1720 $li = new_suggestion($(".suggest-card.design ul"));
1721 } else
1722 if (sugg.type == "distribute") {
1723 $li = new_suggestion($(".suggest-card.distribute ul"));
1724 } else
Scott Main719acb42013-12-05 16:05:09 -08001725 if (sugg.type == "samples") {
1726 $li = new_suggestion($(".suggest-card.develop .child-card.samples"));
1727 } else
Scott Main0e76e7e2013-03-12 10:24:07 -07001728 if (sugg.type == "training") {
1729 $li = new_suggestion($(".suggest-card.develop .child-card.training"));
1730 } else
Scott Main719acb42013-12-05 16:05:09 -08001731 if (sugg.type == "about"||"guide"||"tools"||"google") {
Scott Main0e76e7e2013-03-12 10:24:07 -07001732 $li = new_suggestion($(".suggest-card.develop .child-card.guides"));
1733 } else {
1734 continue;
1735 }
1736
Scott Main719acb42013-12-05 16:05:09 -08001737 set_item_values_jd(toroot, $li, sugg);
Scott Main0e76e7e2013-03-12 10:24:07 -07001738 set_item_selected($li, i == gSelectedIndex);
1739 }
1740
1741 // add heading and show or hide card
1742 if ($(".suggest-card.design li").length > 0) {
1743 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>");
1744 $(".suggest-card.design").show(300);
1745 } else {
1746 $('.suggest-card.design').hide(300);
1747 }
1748 if ($(".suggest-card.distribute li").length > 0) {
1749 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>");
1750 $(".suggest-card.distribute").show(300);
1751 } else {
1752 $('.suggest-card.distribute').hide(300);
1753 }
1754 if ($(".child-card.guides li").length > 0) {
1755 $(".child-card.guides").prepend("<li class='header'>Guides:</li>");
1756 $(".child-card.guides li").appendTo(".suggest-card.develop ul");
1757 }
1758 if ($(".child-card.training li").length > 0) {
1759 $(".child-card.training").prepend("<li class='header'>Training:</li>");
1760 $(".child-card.training li").appendTo(".suggest-card.develop ul");
1761 }
Scott Main719acb42013-12-05 16:05:09 -08001762 if ($(".child-card.samples li").length > 0) {
1763 $(".child-card.samples").prepend("<li class='header'>Samples:</li>");
1764 $(".child-card.samples li").appendTo(".suggest-card.develop ul");
1765 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001766
1767 if ($(".suggest-card.develop li").length > 0) {
1768 $(".suggest-card.develop").show(300);
1769 } else {
1770 $('.suggest-card.develop').hide(300);
1771 }
1772
1773 } else {
1774 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300);
Scott Mainf5089842012-08-14 16:31:07 -07001775 }
1776}
1777
Scott Main0e76e7e2013-03-12 10:24:07 -07001778/** Called by the search input's onkeydown and onkeyup events.
1779 * Handles navigation with keyboard arrows, Enter key to invoke search,
1780 * otherwise invokes search suggestions on key-up event.
1781 * @param e The JS event
1782 * @param kd True if the event is key-down
Scott Main3b90aff2013-08-01 18:09:35 -07001783 * @param toroot A string for the site's root path
Scott Main0e76e7e2013-03-12 10:24:07 -07001784 * @returns True if the event should bubble up
1785 */
Scott Mainf5089842012-08-14 16:31:07 -07001786function search_changed(e, kd, toroot)
1787{
Scott Main719acb42013-12-05 16:05:09 -08001788 var currentLang = getLangPref();
Scott Mainf5089842012-08-14 16:31:07 -07001789 var search = document.getElementById("search_autocomplete");
1790 var text = search.value.replace(/(^ +)|( +$)/g, '');
Scott Main0e76e7e2013-03-12 10:24:07 -07001791 // get the ul hosting the currently selected item
1792 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0;
1793 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible");
1794 var $selectedUl = $columns[gSelectedColumn];
1795
Scott Mainf5089842012-08-14 16:31:07 -07001796 // show/hide the close button
1797 if (text != '') {
1798 $(".search .close").removeClass("hide");
1799 } else {
1800 $(".search .close").addClass("hide");
1801 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001802 // 27 = esc
1803 if (e.keyCode == 27) {
1804 // close all search results
1805 if (kd) $('.search .close').trigger('click');
1806 return true;
1807 }
Scott Mainf5089842012-08-14 16:31:07 -07001808 // 13 = enter
Scott Main0e76e7e2013-03-12 10:24:07 -07001809 else if (e.keyCode == 13) {
1810 if (gSelectedIndex < 0) {
1811 $('.suggest-card').hide();
Scott Main7e447ed2013-02-19 17:22:37 -08001812 if ($("#searchResults").is(":hidden") && (search.value != "")) {
1813 // if results aren't showing (and text not empty), return true to allow search to execute
Dirk Doughertyc3921652014-05-13 16:55:26 -07001814 $('body,html').animate({scrollTop:0}, '500', 'swing');
Scott Mainf5089842012-08-14 16:31:07 -07001815 return true;
1816 } else {
1817 // otherwise, results are already showing, so allow ajax to auto refresh the results
1818 // and ignore this Enter press to avoid the reload.
1819 return false;
1820 }
1821 } else if (kd && gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001822 // click the link corresponding to selected item
1823 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click();
Scott Mainf5089842012-08-14 16:31:07 -07001824 return false;
1825 }
1826 }
Scott Mainb16376f2014-05-21 20:35:47 -07001827 // If Google results are showing, return true to allow ajax search to execute
Scott Main0e76e7e2013-03-12 10:24:07 -07001828 else if ($("#searchResults").is(":visible")) {
Scott Mainb16376f2014-05-21 20:35:47 -07001829 // Also, if search_results is scrolled out of view, scroll to top to make results visible
Dirk Doughertyca1230c2014-05-14 20:00:03 -07001830 if ((sticky ) && (search.value != "")) {
1831 $('body,html').animate({scrollTop:0}, '500', 'swing');
1832 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001833 return true;
1834 }
1835 // 38 UP ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001836 else if (kd && (e.keyCode == 38)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001837 // if the next item is a header, skip it
1838 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001839 gSelectedIndex--;
Scott Main7e447ed2013-02-19 17:22:37 -08001840 }
1841 if (gSelectedIndex >= 0) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001842 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001843 gSelectedIndex--;
Scott Main0e76e7e2013-03-12 10:24:07 -07001844 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1845 // If user reaches top, reset selected column
1846 if (gSelectedIndex < 0) {
1847 gSelectedColumn = -1;
1848 }
Scott Mainf5089842012-08-14 16:31:07 -07001849 }
1850 return false;
1851 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001852 // 40 DOWN ARROW
Scott Mainf5089842012-08-14 16:31:07 -07001853 else if (kd && (e.keyCode == 40)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001854 // if the next item is a header, skip it
1855 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) {
Scott Mainf5089842012-08-14 16:31:07 -07001856 gSelectedIndex++;
Scott Main7e447ed2013-02-19 17:22:37 -08001857 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001858 if ((gSelectedIndex < $("li", $selectedUl).length-1) ||
1859 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) {
1860 $('li', $selectedUl).removeClass('jd-selected');
Scott Main7e447ed2013-02-19 17:22:37 -08001861 gSelectedIndex++;
Scott Main0e76e7e2013-03-12 10:24:07 -07001862 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
Scott Mainf5089842012-08-14 16:31:07 -07001863 }
1864 return false;
1865 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001866 // Consider left/right arrow navigation
1867 // NOTE: Order of suggest columns are reverse order (index position 0 is on right)
1868 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) {
1869 // 37 LEFT ARROW
1870 // go left only if current column is not left-most column (last column)
1871 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) {
1872 $('li', $selectedUl).removeClass('jd-selected');
1873 gSelectedColumn++;
1874 $selectedUl = $columns[gSelectedColumn];
1875 // keep or reset the selected item to last item as appropriate
1876 gSelectedIndex = gSelectedIndex >
1877 $("li", $selectedUl).length-1 ?
1878 $("li", $selectedUl).length-1 : gSelectedIndex;
1879 // if the corresponding item is a header, move down
1880 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1881 gSelectedIndex++;
1882 }
Scott Main3b90aff2013-08-01 18:09:35 -07001883 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001884 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1885 return false;
1886 }
1887 // 39 RIGHT ARROW
1888 // go right only if current column is not the right-most column (first column)
1889 else if (e.keyCode == 39 && gSelectedColumn > 0) {
1890 $('li', $selectedUl).removeClass('jd-selected');
1891 gSelectedColumn--;
1892 $selectedUl = $columns[gSelectedColumn];
1893 // keep or reset the selected item to last item as appropriate
1894 gSelectedIndex = gSelectedIndex >
1895 $("li", $selectedUl).length-1 ?
1896 $("li", $selectedUl).length-1 : gSelectedIndex;
1897 // if the corresponding item is a header, move down
1898 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) {
1899 gSelectedIndex++;
1900 }
Scott Main3b90aff2013-08-01 18:09:35 -07001901 // set item selected
Scott Main0e76e7e2013-03-12 10:24:07 -07001902 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected');
1903 return false;
1904 }
1905 }
1906
Scott Main719acb42013-12-05 16:05:09 -08001907 // if key-up event and not arrow down/up/left/right,
1908 // read the search query and add suggestions to gMatches
Scott Main0e76e7e2013-03-12 10:24:07 -07001909 else if (!kd && (e.keyCode != 40)
1910 && (e.keyCode != 38)
1911 && (e.keyCode != 37)
1912 && (e.keyCode != 39)) {
1913 gSelectedIndex = -1;
Scott Mainf5089842012-08-14 16:31:07 -07001914 gMatches = new Array();
1915 matchedCount = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001916 gGoogleMatches = new Array();
1917 matchedCountGoogle = 0;
Scott Main0e76e7e2013-03-12 10:24:07 -07001918 gDocsMatches = new Array();
1919 matchedCountDocs = 0;
Scott Main7e447ed2013-02-19 17:22:37 -08001920
1921 // Search for Android matches
Scott Mainf5089842012-08-14 16:31:07 -07001922 for (var i=0; i<DATA.length; i++) {
1923 var s = DATA[i];
1924 if (text.length != 0 &&
1925 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1926 gMatches[matchedCount] = s;
1927 matchedCount++;
1928 }
1929 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001930 rank_autocomplete_api_results(text, gMatches);
Scott Mainf5089842012-08-14 16:31:07 -07001931 for (var i=0; i<gMatches.length; i++) {
1932 var s = gMatches[i];
Scott Main7e447ed2013-02-19 17:22:37 -08001933 }
1934
1935
1936 // Search for Google matches
1937 for (var i=0; i<GOOGLE_DATA.length; i++) {
1938 var s = GOOGLE_DATA[i];
1939 if (text.length != 0 &&
1940 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1941 gGoogleMatches[matchedCountGoogle] = s;
1942 matchedCountGoogle++;
Scott Mainf5089842012-08-14 16:31:07 -07001943 }
1944 }
Scott Main0e76e7e2013-03-12 10:24:07 -07001945 rank_autocomplete_api_results(text, gGoogleMatches);
Scott Main7e447ed2013-02-19 17:22:37 -08001946 for (var i=0; i<gGoogleMatches.length; i++) {
1947 var s = gGoogleMatches[i];
1948 }
1949
Scott Mainf5089842012-08-14 16:31:07 -07001950 highlight_autocomplete_result_labels(text);
Scott Main0e76e7e2013-03-12 10:24:07 -07001951
1952
1953
Scott Main719acb42013-12-05 16:05:09 -08001954 // Search for matching JD docs
Scott Main0e76e7e2013-03-12 10:24:07 -07001955 if (text.length >= 3) {
Scott Main719acb42013-12-05 16:05:09 -08001956 // Regex to match only the beginning of a word
1957 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g");
1958
1959
1960 // Search for Training classes
1961 for (var i=0; i<TRAINING_RESOURCES.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001962 // current search comparison, with counters for tag and title,
1963 // used later to improve ranking
Scott Main719acb42013-12-05 16:05:09 -08001964 var s = TRAINING_RESOURCES[i];
Scott Main0e76e7e2013-03-12 10:24:07 -07001965 s.matched_tag = 0;
1966 s.matched_title = 0;
1967 var matched = false;
1968
1969 // Check if query matches any tags; work backwards toward 1 to assist ranking
Scott Main719acb42013-12-05 16:05:09 -08001970 for (var j = s.keywords.length - 1; j >= 0; j--) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001971 // it matches a tag
Scott Main719acb42013-12-05 16:05:09 -08001972 if (s.keywords[j].toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001973 matched = true;
1974 s.matched_tag = j + 1; // add 1 to index position
1975 }
1976 }
Scott Main719acb42013-12-05 16:05:09 -08001977 // Don't consider doc title for lessons (only for class landing pages),
1978 // unless the lesson has a tag that already matches
1979 if ((s.lang == currentLang) &&
1980 (!(s.type == "training" && s.url.indexOf("index.html") == -1) || matched)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001981 // it matches the doc title
Scott Main719acb42013-12-05 16:05:09 -08001982 if (s.title.toLowerCase().match(textRegex)) {
Scott Main0e76e7e2013-03-12 10:24:07 -07001983 matched = true;
1984 s.matched_title = 1;
1985 }
1986 }
1987 if (matched) {
1988 gDocsMatches[matchedCountDocs] = s;
1989 matchedCountDocs++;
1990 }
1991 }
Scott Main719acb42013-12-05 16:05:09 -08001992
1993
1994 // Search for API Guides
1995 for (var i=0; i<GUIDE_RESOURCES.length; i++) {
1996 // current search comparison, with counters for tag and title,
1997 // used later to improve ranking
1998 var s = GUIDE_RESOURCES[i];
1999 s.matched_tag = 0;
2000 s.matched_title = 0;
2001 var matched = false;
2002
2003 // Check if query matches any tags; work backwards toward 1 to assist ranking
2004 for (var j = s.keywords.length - 1; j >= 0; j--) {
2005 // it matches a tag
2006 if (s.keywords[j].toLowerCase().match(textRegex)) {
2007 matched = true;
2008 s.matched_tag = j + 1; // add 1 to index position
2009 }
2010 }
2011 // Check if query matches the doc title, but only for current language
2012 if (s.lang == currentLang) {
2013 // if query matches the doc title
2014 if (s.title.toLowerCase().match(textRegex)) {
2015 matched = true;
2016 s.matched_title = 1;
2017 }
2018 }
2019 if (matched) {
2020 gDocsMatches[matchedCountDocs] = s;
2021 matchedCountDocs++;
2022 }
2023 }
2024
2025
2026 // Search for Tools Guides
2027 for (var i=0; i<TOOLS_RESOURCES.length; i++) {
2028 // current search comparison, with counters for tag and title,
2029 // used later to improve ranking
2030 var s = TOOLS_RESOURCES[i];
2031 s.matched_tag = 0;
2032 s.matched_title = 0;
2033 var matched = false;
2034
2035 // Check if query matches any tags; work backwards toward 1 to assist ranking
2036 for (var j = s.keywords.length - 1; j >= 0; j--) {
2037 // it matches a tag
2038 if (s.keywords[j].toLowerCase().match(textRegex)) {
2039 matched = true;
2040 s.matched_tag = j + 1; // add 1 to index position
2041 }
2042 }
2043 // Check if query matches the doc title, but only for current language
2044 if (s.lang == currentLang) {
2045 // if query matches the doc title
2046 if (s.title.toLowerCase().match(textRegex)) {
2047 matched = true;
2048 s.matched_title = 1;
2049 }
2050 }
2051 if (matched) {
2052 gDocsMatches[matchedCountDocs] = s;
2053 matchedCountDocs++;
2054 }
2055 }
2056
2057
2058 // Search for About docs
2059 for (var i=0; i<ABOUT_RESOURCES.length; i++) {
2060 // current search comparison, with counters for tag and title,
2061 // used later to improve ranking
2062 var s = ABOUT_RESOURCES[i];
2063 s.matched_tag = 0;
2064 s.matched_title = 0;
2065 var matched = false;
2066
2067 // Check if query matches any tags; work backwards toward 1 to assist ranking
2068 for (var j = s.keywords.length - 1; j >= 0; j--) {
2069 // it matches a tag
2070 if (s.keywords[j].toLowerCase().match(textRegex)) {
2071 matched = true;
2072 s.matched_tag = j + 1; // add 1 to index position
2073 }
2074 }
2075 // Check if query matches the doc title, but only for current language
2076 if (s.lang == currentLang) {
2077 // if query matches the doc title
2078 if (s.title.toLowerCase().match(textRegex)) {
2079 matched = true;
2080 s.matched_title = 1;
2081 }
2082 }
2083 if (matched) {
2084 gDocsMatches[matchedCountDocs] = s;
2085 matchedCountDocs++;
2086 }
2087 }
2088
2089
2090 // Search for Design guides
2091 for (var i=0; i<DESIGN_RESOURCES.length; i++) {
2092 // current search comparison, with counters for tag and title,
2093 // used later to improve ranking
2094 var s = DESIGN_RESOURCES[i];
2095 s.matched_tag = 0;
2096 s.matched_title = 0;
2097 var matched = false;
2098
2099 // Check if query matches any tags; work backwards toward 1 to assist ranking
2100 for (var j = s.keywords.length - 1; j >= 0; j--) {
2101 // it matches a tag
2102 if (s.keywords[j].toLowerCase().match(textRegex)) {
2103 matched = true;
2104 s.matched_tag = j + 1; // add 1 to index position
2105 }
2106 }
2107 // Check if query matches the doc title, but only for current language
2108 if (s.lang == currentLang) {
2109 // if query matches the doc title
2110 if (s.title.toLowerCase().match(textRegex)) {
2111 matched = true;
2112 s.matched_title = 1;
2113 }
2114 }
2115 if (matched) {
2116 gDocsMatches[matchedCountDocs] = s;
2117 matchedCountDocs++;
2118 }
2119 }
2120
2121
2122 // Search for Distribute guides
2123 for (var i=0; i<DISTRIBUTE_RESOURCES.length; i++) {
2124 // current search comparison, with counters for tag and title,
2125 // used later to improve ranking
2126 var s = DISTRIBUTE_RESOURCES[i];
2127 s.matched_tag = 0;
2128 s.matched_title = 0;
2129 var matched = false;
2130
2131 // Check if query matches any tags; work backwards toward 1 to assist ranking
2132 for (var j = s.keywords.length - 1; j >= 0; j--) {
2133 // it matches a tag
2134 if (s.keywords[j].toLowerCase().match(textRegex)) {
2135 matched = true;
2136 s.matched_tag = j + 1; // add 1 to index position
2137 }
2138 }
2139 // Check if query matches the doc title, but only for current language
2140 if (s.lang == currentLang) {
2141 // if query matches the doc title
2142 if (s.title.toLowerCase().match(textRegex)) {
2143 matched = true;
2144 s.matched_title = 1;
2145 }
2146 }
2147 if (matched) {
2148 gDocsMatches[matchedCountDocs] = s;
2149 matchedCountDocs++;
2150 }
2151 }
2152
2153
2154 // Search for Google guides
2155 for (var i=0; i<GOOGLE_RESOURCES.length; i++) {
2156 // current search comparison, with counters for tag and title,
2157 // used later to improve ranking
2158 var s = GOOGLE_RESOURCES[i];
2159 s.matched_tag = 0;
2160 s.matched_title = 0;
2161 var matched = false;
2162
2163 // Check if query matches any tags; work backwards toward 1 to assist ranking
2164 for (var j = s.keywords.length - 1; j >= 0; j--) {
2165 // it matches a tag
2166 if (s.keywords[j].toLowerCase().match(textRegex)) {
2167 matched = true;
2168 s.matched_tag = j + 1; // add 1 to index position
2169 }
2170 }
2171 // Check if query matches the doc title, but only for current language
2172 if (s.lang == currentLang) {
2173 // if query matches the doc title
2174 if (s.title.toLowerCase().match(textRegex)) {
2175 matched = true;
2176 s.matched_title = 1;
2177 }
2178 }
2179 if (matched) {
2180 gDocsMatches[matchedCountDocs] = s;
2181 matchedCountDocs++;
2182 }
2183 }
2184
2185
2186 // Search for Samples
2187 for (var i=0; i<SAMPLES_RESOURCES.length; i++) {
2188 // current search comparison, with counters for tag and title,
2189 // used later to improve ranking
2190 var s = SAMPLES_RESOURCES[i];
2191 s.matched_tag = 0;
2192 s.matched_title = 0;
2193 var matched = false;
2194 // Check if query matches any tags; work backwards toward 1 to assist ranking
2195 for (var j = s.keywords.length - 1; j >= 0; j--) {
2196 // it matches a tag
2197 if (s.keywords[j].toLowerCase().match(textRegex)) {
2198 matched = true;
2199 s.matched_tag = j + 1; // add 1 to index position
2200 }
2201 }
2202 // Check if query matches the doc title, but only for current language
2203 if (s.lang == currentLang) {
2204 // if query matches the doc title.t
2205 if (s.title.toLowerCase().match(textRegex)) {
2206 matched = true;
2207 s.matched_title = 1;
2208 }
2209 }
2210 if (matched) {
2211 gDocsMatches[matchedCountDocs] = s;
2212 matchedCountDocs++;
2213 }
2214 }
2215
2216 // Rank/sort all the matched pages
Scott Main0e76e7e2013-03-12 10:24:07 -07002217 rank_autocomplete_doc_results(text, gDocsMatches);
2218 }
2219
2220 // draw the suggestions
Scott Mainf5089842012-08-14 16:31:07 -07002221 sync_selection_table(toroot);
2222 return true; // allow the event to bubble up to the search api
2223 }
2224}
2225
Scott Main0e76e7e2013-03-12 10:24:07 -07002226/* Order the jd doc result list based on match quality */
2227function rank_autocomplete_doc_results(query, matches) {
2228 query = query || '';
2229 if (!matches || !matches.length)
2230 return;
2231
2232 var _resultScoreFn = function(match) {
2233 var score = 1.0;
2234
2235 // if the query matched a tag
2236 if (match.matched_tag > 0) {
2237 // multiply score by factor relative to position in tags list (max of 3)
2238 score *= 3 / match.matched_tag;
2239
2240 // if it also matched the title
2241 if (match.matched_title > 0) {
2242 score *= 2;
2243 }
2244 } else if (match.matched_title > 0) {
2245 score *= 3;
2246 }
2247
2248 return score;
2249 };
2250
2251 for (var i=0; i<matches.length; i++) {
2252 matches[i].__resultScore = _resultScoreFn(matches[i]);
2253 }
2254
2255 matches.sort(function(a,b){
2256 var n = b.__resultScore - a.__resultScore;
2257 if (n == 0) // lexicographical sort if scores are the same
2258 n = (a.label < b.label) ? -1 : 1;
2259 return n;
2260 });
2261}
2262
Scott Main7e447ed2013-02-19 17:22:37 -08002263/* Order the result list based on match quality */
Scott Main0e76e7e2013-03-12 10:24:07 -07002264function rank_autocomplete_api_results(query, matches) {
Scott Mainf5089842012-08-14 16:31:07 -07002265 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002266 if (!matches || !matches.length)
Scott Mainf5089842012-08-14 16:31:07 -07002267 return;
2268
2269 // helper function that gets the last occurence index of the given regex
2270 // in the given string, or -1 if not found
2271 var _lastSearch = function(s, re) {
2272 if (s == '')
2273 return -1;
2274 var l = -1;
2275 var tmp;
2276 while ((tmp = s.search(re)) >= 0) {
2277 if (l < 0) l = 0;
2278 l += tmp;
2279 s = s.substr(tmp + 1);
2280 }
2281 return l;
2282 };
2283
2284 // helper function that counts the occurrences of a given character in
2285 // a given string
2286 var _countChar = function(s, c) {
2287 var n = 0;
2288 for (var i=0; i<s.length; i++)
2289 if (s.charAt(i) == c) ++n;
2290 return n;
2291 };
2292
2293 var queryLower = query.toLowerCase();
2294 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
2295 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
2296 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
2297
2298 var _resultScoreFn = function(result) {
2299 // scores are calculated based on exact and prefix matches,
2300 // and then number of path separators (dots) from the last
2301 // match (i.e. favoring classes and deep package names)
2302 var score = 1.0;
2303 var labelLower = result.label.toLowerCase();
2304 var t;
2305 t = _lastSearch(labelLower, partExactAlnumRE);
2306 if (t >= 0) {
2307 // exact part match
2308 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2309 score *= 200 / (partsAfter + 1);
2310 } else {
2311 t = _lastSearch(labelLower, partPrefixAlnumRE);
2312 if (t >= 0) {
2313 // part prefix match
2314 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
2315 score *= 20 / (partsAfter + 1);
2316 }
2317 }
2318
2319 return score;
2320 };
2321
Scott Main7e447ed2013-02-19 17:22:37 -08002322 for (var i=0; i<matches.length; i++) {
Scott Main0e76e7e2013-03-12 10:24:07 -07002323 // if the API is deprecated, default score is 0; otherwise, perform scoring
2324 if (matches[i].deprecated == "true") {
2325 matches[i].__resultScore = 0;
2326 } else {
2327 matches[i].__resultScore = _resultScoreFn(matches[i]);
2328 }
Scott Mainf5089842012-08-14 16:31:07 -07002329 }
2330
Scott Main7e447ed2013-02-19 17:22:37 -08002331 matches.sort(function(a,b){
Scott Mainf5089842012-08-14 16:31:07 -07002332 var n = b.__resultScore - a.__resultScore;
2333 if (n == 0) // lexicographical sort if scores are the same
2334 n = (a.label < b.label) ? -1 : 1;
2335 return n;
2336 });
2337}
2338
Scott Main7e447ed2013-02-19 17:22:37 -08002339/* Add emphasis to part of string that matches query */
Scott Mainf5089842012-08-14 16:31:07 -07002340function highlight_autocomplete_result_labels(query) {
2341 query = query || '';
Scott Main7e447ed2013-02-19 17:22:37 -08002342 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length))
Scott Mainf5089842012-08-14 16:31:07 -07002343 return;
2344
2345 var queryLower = query.toLowerCase();
2346 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
2347 var queryRE = new RegExp(
2348 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
2349 for (var i=0; i<gMatches.length; i++) {
2350 gMatches[i].__hilabel = gMatches[i].label.replace(
2351 queryRE, '<b>$1</b>');
2352 }
Scott Main7e447ed2013-02-19 17:22:37 -08002353 for (var i=0; i<gGoogleMatches.length; i++) {
2354 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace(
2355 queryRE, '<b>$1</b>');
2356 }
Scott Mainf5089842012-08-14 16:31:07 -07002357}
2358
2359function search_focus_changed(obj, focused)
2360{
Scott Main3b90aff2013-08-01 18:09:35 -07002361 if (!focused) {
Scott Mainf5089842012-08-14 16:31:07 -07002362 if(obj.value == ""){
2363 $(".search .close").addClass("hide");
2364 }
Scott Main0e76e7e2013-03-12 10:24:07 -07002365 $(".suggest-card").hide();
Scott Mainf5089842012-08-14 16:31:07 -07002366 }
2367}
2368
2369function submit_search() {
2370 var query = document.getElementById('search_autocomplete').value;
2371 location.hash = 'q=' + query;
2372 loadSearchResults();
Dirk Doughertyc3921652014-05-13 16:55:26 -07002373 $("#searchResults").slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002374 return false;
2375}
2376
2377
2378function hideResults() {
Dirk Doughertyc3921652014-05-13 16:55:26 -07002379 $("#searchResults").slideUp('fast', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002380 $(".search .close").addClass("hide");
2381 location.hash = '';
Scott Main3b90aff2013-08-01 18:09:35 -07002382
Scott Mainf5089842012-08-14 16:31:07 -07002383 $("#search_autocomplete").val("").blur();
Scott Main3b90aff2013-08-01 18:09:35 -07002384
Scott Mainf5089842012-08-14 16:31:07 -07002385 // reset the ajax search callback to nothing, so results don't appear unless ENTER
2386 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
Scott Main0e76e7e2013-03-12 10:24:07 -07002387
2388 // forcefully regain key-up event control (previously jacked by search api)
2389 $("#search_autocomplete").keyup(function(event) {
2390 return search_changed(event, false, toRoot);
2391 });
2392
Scott Mainf5089842012-08-14 16:31:07 -07002393 return false;
2394}
2395
2396
2397
2398/* ########################################################## */
2399/* ################ CUSTOM SEARCH ENGINE ################## */
2400/* ########################################################## */
2401
Scott Mainf5089842012-08-14 16:31:07 -07002402var searchControl;
Scott Main0e76e7e2013-03-12 10:24:07 -07002403google.load('search', '1', {"callback" : function() {
2404 searchControl = new google.search.SearchControl();
2405 } });
Scott Mainf5089842012-08-14 16:31:07 -07002406
2407function loadSearchResults() {
2408 document.getElementById("search_autocomplete").style.color = "#000";
2409
Scott Mainf5089842012-08-14 16:31:07 -07002410 searchControl = new google.search.SearchControl();
2411
2412 // use our existing search form and use tabs when multiple searchers are used
2413 drawOptions = new google.search.DrawOptions();
2414 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
2415 drawOptions.setInput(document.getElementById("search_autocomplete"));
2416
2417 // configure search result options
2418 searchOptions = new google.search.SearcherOptions();
2419 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
2420
2421 // configure each of the searchers, for each tab
2422 devSiteSearcher = new google.search.WebSearch();
2423 devSiteSearcher.setUserDefinedLabel("All");
2424 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
2425
2426 designSearcher = new google.search.WebSearch();
2427 designSearcher.setUserDefinedLabel("Design");
2428 designSearcher.setSiteRestriction("http://developer.android.com/design/");
2429
2430 trainingSearcher = new google.search.WebSearch();
2431 trainingSearcher.setUserDefinedLabel("Training");
2432 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
2433
2434 guidesSearcher = new google.search.WebSearch();
2435 guidesSearcher.setUserDefinedLabel("Guides");
2436 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
2437
2438 referenceSearcher = new google.search.WebSearch();
2439 referenceSearcher.setUserDefinedLabel("Reference");
2440 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
2441
Scott Maindf08ada2012-12-03 08:54:37 -08002442 googleSearcher = new google.search.WebSearch();
2443 googleSearcher.setUserDefinedLabel("Google Services");
2444 googleSearcher.setSiteRestriction("http://developer.android.com/google/");
2445
Scott Mainf5089842012-08-14 16:31:07 -07002446 blogSearcher = new google.search.WebSearch();
2447 blogSearcher.setUserDefinedLabel("Blog");
2448 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
2449
2450 // add each searcher to the search control
2451 searchControl.addSearcher(devSiteSearcher, searchOptions);
2452 searchControl.addSearcher(designSearcher, searchOptions);
2453 searchControl.addSearcher(trainingSearcher, searchOptions);
2454 searchControl.addSearcher(guidesSearcher, searchOptions);
2455 searchControl.addSearcher(referenceSearcher, searchOptions);
Scott Maindf08ada2012-12-03 08:54:37 -08002456 searchControl.addSearcher(googleSearcher, searchOptions);
Scott Mainf5089842012-08-14 16:31:07 -07002457 searchControl.addSearcher(blogSearcher, searchOptions);
2458
2459 // configure result options
2460 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
2461 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
2462 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
2463 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
2464
2465 // upon ajax search, refresh the url and search title
2466 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
2467 updateResultTitle(query);
2468 var query = document.getElementById('search_autocomplete').value;
2469 location.hash = 'q=' + query;
2470 });
2471
Scott Mainde295272013-03-25 15:48:35 -07002472 // once search results load, set up click listeners
2473 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) {
2474 addResultClickListeners();
2475 });
2476
Scott Mainf5089842012-08-14 16:31:07 -07002477 // draw the search results box
2478 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
2479
2480 // get query and execute the search
2481 searchControl.execute(decodeURI(getQuery(location.hash)));
2482
2483 document.getElementById("search_autocomplete").focus();
2484 addTabListeners();
2485}
2486// End of loadSearchResults
2487
2488
2489google.setOnLoadCallback(function(){
2490 if (location.hash.indexOf("q=") == -1) {
2491 // if there's no query in the url, don't search and make sure results are hidden
2492 $('#searchResults').hide();
2493 return;
2494 } else {
2495 // first time loading search results for this page
Dirk Doughertyc3921652014-05-13 16:55:26 -07002496 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002497 $(".search .close").removeClass("hide");
2498 loadSearchResults();
2499 }
2500}, true);
2501
Scott Mainb16376f2014-05-21 20:35:47 -07002502/* Adjust the scroll position to account for sticky header, only if the hash matches an id */
2503function offsetScrollForSticky() {
2504 var hash = location.hash;
2505 var $matchingElement = $(hash);
2506 // If there's no element with the hash as an ID, then look for an <a name=''> with it.
2507 if ($matchingElement.length < 1) {
2508 $matchingElement = $('a[name="' + hash.substr(1) + '"]');
2509 }
2510 // Sanity check that hash is a real hash and that there's an element with that ID on the page
2511 if ((hash.indexOf("#") == 0) && $matchingElement.length) {
2512 // If the position of the target element is near the top of the page (<20px, where we expect it
2513 // to be because we need to move it down 60px to become in view), then move it down 60px
2514 if (Math.abs($matchingElement.offset().top - $(window).scrollTop()) < 20) {
2515 $(window).scrollTop($(window).scrollTop() - 60);
2516 } else {
2517 }
2518 }
2519}
2520
Scott Mainf5089842012-08-14 16:31:07 -07002521// when an event on the browser history occurs (back, forward, load) requery hash and do search
2522$(window).hashchange( function(){
Dirk Doughertyc3921652014-05-13 16:55:26 -07002523 // If the hash isn't a search query or there's an error in the query,
2524 // then adjust the scroll position to account for sticky header, then exit.
Scott Mainf5089842012-08-14 16:31:07 -07002525 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
2526 // If the results pane is open, close it.
2527 if (!$("#searchResults").is(":hidden")) {
2528 hideResults();
2529 }
Scott Mainb16376f2014-05-21 20:35:47 -07002530 offsetScrollForSticky();
Scott Mainf5089842012-08-14 16:31:07 -07002531 return;
2532 }
2533
2534 // Otherwise, we have a search to do
2535 var query = decodeURI(getQuery(location.hash));
2536 searchControl.execute(query);
Dirk Doughertyc3921652014-05-13 16:55:26 -07002537 $('#searchResults').slideDown('slow', setStickyTop);
Scott Mainf5089842012-08-14 16:31:07 -07002538 $("#search_autocomplete").focus();
2539 $(".search .close").removeClass("hide");
2540
2541 updateResultTitle(query);
2542});
2543
2544function updateResultTitle(query) {
2545 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
2546}
2547
2548// forcefully regain key-up event control (previously jacked by search api)
2549$("#search_autocomplete").keyup(function(event) {
2550 return search_changed(event, false, toRoot);
2551});
2552
2553// add event listeners to each tab so we can track the browser history
2554function addTabListeners() {
2555 var tabHeaders = $(".gsc-tabHeader");
2556 for (var i = 0; i < tabHeaders.length; i++) {
2557 $(tabHeaders[i]).attr("id",i).click(function() {
2558 /*
2559 // make a copy of the page numbers for the search left pane
2560 setTimeout(function() {
2561 // remove any residual page numbers
2562 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
Scott Main3b90aff2013-08-01 18:09:35 -07002563 // move the page numbers to the left position; make a clone,
Scott Mainf5089842012-08-14 16:31:07 -07002564 // because the element is drawn to the DOM only once
Scott Main3b90aff2013-08-01 18:09:35 -07002565 // and because we're going to remove it (previous line),
2566 // we need it to be available to move again as the user navigates
Scott Mainf5089842012-08-14 16:31:07 -07002567 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
2568 .clone().appendTo('#searchResults .gsc-tabsArea');
2569 }, 200);
2570 */
2571 });
2572 }
2573 setTimeout(function(){$(tabHeaders[0]).click()},200);
2574}
2575
Scott Mainde295272013-03-25 15:48:35 -07002576// add analytics tracking events to each result link
2577function addResultClickListeners() {
2578 $("#searchResults a.gs-title").each(function(index, link) {
2579 // When user clicks enter for Google search results, track it
2580 $(link).click(function() {
2581 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(),
2582 'from: ' + $("#search_autocomplete").val()]);
2583 });
2584 });
2585}
2586
Scott Mainf5089842012-08-14 16:31:07 -07002587
2588function getQuery(hash) {
2589 var queryParts = hash.split('=');
2590 return queryParts[1];
2591}
2592
2593/* returns the given string with all HTML brackets converted to entities
2594 TODO: move this to the site's JS library */
2595function escapeHTML(string) {
2596 return string.replace(/</g,"&lt;")
2597 .replace(/>/g,"&gt;");
2598}
2599
2600
2601
2602
2603
2604
2605
2606/* ######################################################## */
2607/* ################# JAVADOC REFERENCE ################### */
2608/* ######################################################## */
2609
Scott Main65511c02012-09-07 15:51:32 -07002610/* Initialize some droiddoc stuff, but only if we're in the reference */
Scott Main52dd2062013-08-15 12:22:28 -07002611if (location.pathname.indexOf("/reference") == 0) {
2612 if(!(location.pathname.indexOf("/reference-gms/packages.html") == 0)
2613 && !(location.pathname.indexOf("/reference-gcm/packages.html") == 0)
2614 && !(location.pathname.indexOf("/reference/com/google") == 0)) {
Robert Ly67d75f12012-12-03 12:53:42 -08002615 $(document).ready(function() {
2616 // init available apis based on user pref
2617 changeApiLevel();
2618 initSidenavHeightResize()
2619 });
2620 }
Scott Main65511c02012-09-07 15:51:32 -07002621}
Scott Mainf5089842012-08-14 16:31:07 -07002622
2623var API_LEVEL_COOKIE = "api_level";
2624var minLevel = 1;
2625var maxLevel = 1;
2626
2627/******* SIDENAV DIMENSIONS ************/
Scott Main3b90aff2013-08-01 18:09:35 -07002628
Scott Mainf5089842012-08-14 16:31:07 -07002629 function initSidenavHeightResize() {
2630 // Change the drag bar size to nicely fit the scrollbar positions
2631 var $dragBar = $(".ui-resizable-s");
2632 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
Scott Main3b90aff2013-08-01 18:09:35 -07002633
2634 $( "#resize-packages-nav" ).resizable({
Scott Mainf5089842012-08-14 16:31:07 -07002635 containment: "#nav-panels",
2636 handles: "s",
2637 alsoResize: "#packages-nav",
2638 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
2639 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
2640 });
Scott Main3b90aff2013-08-01 18:09:35 -07002641
Scott Mainf5089842012-08-14 16:31:07 -07002642 }
Scott Main3b90aff2013-08-01 18:09:35 -07002643
Scott Mainf5089842012-08-14 16:31:07 -07002644function updateSidenavFixedWidth() {
2645 if (!navBarIsFixed) return;
2646 $('#devdoc-nav').css({
2647 'width' : $('#side-nav').css('width'),
2648 'margin' : $('#side-nav').css('margin')
2649 });
2650 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
Scott Main3b90aff2013-08-01 18:09:35 -07002651
Scott Mainf5089842012-08-14 16:31:07 -07002652 initSidenavHeightResize();
2653}
2654
2655function updateSidenavFullscreenWidth() {
2656 if (!navBarIsFixed) return;
2657 $('#devdoc-nav').css({
2658 'width' : $('#side-nav').css('width'),
2659 'margin' : $('#side-nav').css('margin')
2660 });
2661 $('#devdoc-nav .totop').css({'left': 'inherit'});
Scott Main3b90aff2013-08-01 18:09:35 -07002662
Scott Mainf5089842012-08-14 16:31:07 -07002663 initSidenavHeightResize();
2664}
2665
2666function buildApiLevelSelector() {
2667 maxLevel = SINCE_DATA.length;
2668 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
2669 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
2670
2671 minLevel = parseInt($("#doc-api-level").attr("class"));
2672 // Handle provisional api levels; the provisional level will always be the highest possible level
2673 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
2674 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
2675 if (isNaN(minLevel) && minLevel.length) {
2676 minLevel = maxLevel;
2677 }
2678 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
2679 for (var i = maxLevel-1; i >= 0; i--) {
2680 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
2681 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
2682 select.append(option);
2683 }
2684
2685 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
2686 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
2687 selectedLevelItem.setAttribute('selected',true);
2688}
2689
2690function changeApiLevel() {
2691 maxLevel = SINCE_DATA.length;
2692 var selectedLevel = maxLevel;
2693
2694 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
2695 toggleVisisbleApis(selectedLevel, "body");
2696
2697 var date = new Date();
2698 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
2699 var expiration = date.toGMTString();
2700 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
2701
2702 if (selectedLevel < minLevel) {
2703 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08002704 $("#naMessage").show().html("<div><p><strong>This " + thing
2705 + " requires API level " + minLevel + " or higher.</strong></p>"
2706 + "<p>This document is hidden because your selected API level for the documentation is "
2707 + selectedLevel + ". You can change the documentation API level with the selector "
2708 + "above the left navigation.</p>"
2709 + "<p>For more information about specifying the API level your app requires, "
2710 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
2711 + ">Supporting Different Platform Versions</a>.</p>"
2712 + "<input type='button' value='OK, make this page visible' "
2713 + "title='Change the API level to " + minLevel + "' "
2714 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
2715 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07002716 } else {
2717 $("#naMessage").hide();
2718 }
2719}
2720
2721function toggleVisisbleApis(selectedLevel, context) {
2722 var apis = $(".api",context);
2723 apis.each(function(i) {
2724 var obj = $(this);
2725 var className = obj.attr("class");
2726 var apiLevelIndex = className.lastIndexOf("-")+1;
2727 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
2728 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
2729 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
2730 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
2731 return;
2732 }
2733 apiLevel = parseInt(apiLevel);
2734
2735 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
2736 var selectedLevelNum = parseInt(selectedLevel)
2737 var apiLevelNum = parseInt(apiLevel);
2738 if (isNaN(apiLevelNum)) {
2739 apiLevelNum = maxLevel;
2740 }
2741
2742 // Grey things out that aren't available and give a tooltip title
2743 if (apiLevelNum > selectedLevelNum) {
2744 obj.addClass("absent").attr("title","Requires API Level \""
Scott Main641c2c22013-10-31 14:48:45 -07002745 + apiLevel + "\" or higher. To reveal, change the target API level "
2746 + "above the left navigation.");
Scott Main3b90aff2013-08-01 18:09:35 -07002747 }
Scott Mainf5089842012-08-14 16:31:07 -07002748 else obj.removeClass("absent").removeAttr("title");
2749 });
2750}
2751
2752
2753
2754
2755/* ################# SIDENAV TREE VIEW ################### */
2756
2757function new_node(me, mom, text, link, children_data, api_level)
2758{
2759 var node = new Object();
2760 node.children = Array();
2761 node.children_data = children_data;
2762 node.depth = mom.depth + 1;
2763
2764 node.li = document.createElement("li");
2765 mom.get_children_ul().appendChild(node.li);
2766
2767 node.label_div = document.createElement("div");
2768 node.label_div.className = "label";
2769 if (api_level != null) {
2770 $(node.label_div).addClass("api");
2771 $(node.label_div).addClass("api-level-"+api_level);
2772 }
2773 node.li.appendChild(node.label_div);
2774
2775 if (children_data != null) {
2776 node.expand_toggle = document.createElement("a");
2777 node.expand_toggle.href = "javascript:void(0)";
2778 node.expand_toggle.onclick = function() {
2779 if (node.expanded) {
2780 $(node.get_children_ul()).slideUp("fast");
2781 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2782 node.expanded = false;
2783 } else {
2784 expand_node(me, node);
2785 }
2786 };
2787 node.label_div.appendChild(node.expand_toggle);
2788
2789 node.plus_img = document.createElement("img");
2790 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2791 node.plus_img.className = "plus";
2792 node.plus_img.width = "8";
2793 node.plus_img.border = "0";
2794 node.expand_toggle.appendChild(node.plus_img);
2795
2796 node.expanded = false;
2797 }
2798
2799 var a = document.createElement("a");
2800 node.label_div.appendChild(a);
2801 node.label = document.createTextNode(text);
2802 a.appendChild(node.label);
2803 if (link) {
2804 a.href = me.toroot + link;
2805 } else {
2806 if (children_data != null) {
2807 a.className = "nolink";
2808 a.href = "javascript:void(0)";
2809 a.onclick = node.expand_toggle.onclick;
2810 // This next line shouldn't be necessary. I'll buy a beer for the first
2811 // person who figures out how to remove this line and have the link
2812 // toggle shut on the first try. --joeo@android.com
2813 node.expanded = false;
2814 }
2815 }
Scott Main3b90aff2013-08-01 18:09:35 -07002816
Scott Mainf5089842012-08-14 16:31:07 -07002817
2818 node.children_ul = null;
2819 node.get_children_ul = function() {
2820 if (!node.children_ul) {
2821 node.children_ul = document.createElement("ul");
2822 node.children_ul.className = "children_ul";
2823 node.children_ul.style.display = "none";
2824 node.li.appendChild(node.children_ul);
2825 }
2826 return node.children_ul;
2827 };
2828
2829 return node;
2830}
2831
Robert Lyd2dd6e52012-11-29 21:28:48 -08002832
2833
2834
Scott Mainf5089842012-08-14 16:31:07 -07002835function expand_node(me, node)
2836{
2837 if (node.children_data && !node.expanded) {
2838 if (node.children_visited) {
2839 $(node.get_children_ul()).slideDown("fast");
2840 } else {
2841 get_node(me, node);
2842 if ($(node.label_div).hasClass("absent")) {
2843 $(node.get_children_ul()).addClass("absent");
Scott Main3b90aff2013-08-01 18:09:35 -07002844 }
Scott Mainf5089842012-08-14 16:31:07 -07002845 $(node.get_children_ul()).slideDown("fast");
2846 }
2847 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2848 node.expanded = true;
2849
2850 // perform api level toggling because new nodes are new to the DOM
2851 var selectedLevel = $("#apiLevelSelector option:selected").val();
2852 toggleVisisbleApis(selectedLevel, "#side-nav");
2853 }
2854}
2855
2856function get_node(me, mom)
2857{
2858 mom.children_visited = true;
2859 for (var i in mom.children_data) {
2860 var node_data = mom.children_data[i];
2861 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2862 node_data[2], node_data[3]);
2863 }
2864}
2865
2866function this_page_relative(toroot)
2867{
2868 var full = document.location.pathname;
2869 var file = "";
2870 if (toroot.substr(0, 1) == "/") {
2871 if (full.substr(0, toroot.length) == toroot) {
2872 return full.substr(toroot.length);
2873 } else {
2874 // the file isn't under toroot. Fail.
2875 return null;
2876 }
2877 } else {
2878 if (toroot != "./") {
2879 toroot = "./" + toroot;
2880 }
2881 do {
2882 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2883 var pos = full.lastIndexOf("/");
2884 file = full.substr(pos) + file;
2885 full = full.substr(0, pos);
2886 toroot = toroot.substr(0, toroot.length-3);
2887 }
2888 } while (toroot != "" && toroot != "/");
2889 return file.substr(1);
2890 }
2891}
2892
2893function find_page(url, data)
2894{
2895 var nodes = data;
2896 var result = null;
2897 for (var i in nodes) {
2898 var d = nodes[i];
2899 if (d[1] == url) {
2900 return new Array(i);
2901 }
2902 else if (d[2] != null) {
2903 result = find_page(url, d[2]);
2904 if (result != null) {
2905 return (new Array(i).concat(result));
2906 }
2907 }
2908 }
2909 return null;
2910}
2911
Scott Mainf5089842012-08-14 16:31:07 -07002912function init_default_navtree(toroot) {
Scott Main25e73002013-03-27 15:24:06 -07002913 // load json file for navtree data
2914 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) {
2915 // when the file is loaded, initialize the tree
2916 if(jqxhr.status === 200) {
2917 init_navtree("tree-list", toroot, NAVTREE_DATA);
2918 }
2919 });
Scott Main3b90aff2013-08-01 18:09:35 -07002920
Scott Mainf5089842012-08-14 16:31:07 -07002921 // perform api level toggling because because the whole tree is new to the DOM
2922 var selectedLevel = $("#apiLevelSelector option:selected").val();
2923 toggleVisisbleApis(selectedLevel, "#side-nav");
2924}
2925
2926function init_navtree(navtree_id, toroot, root_nodes)
2927{
2928 var me = new Object();
2929 me.toroot = toroot;
2930 me.node = new Object();
2931
2932 me.node.li = document.getElementById(navtree_id);
2933 me.node.children_data = root_nodes;
2934 me.node.children = new Array();
2935 me.node.children_ul = document.createElement("ul");
2936 me.node.get_children_ul = function() { return me.node.children_ul; };
2937 //me.node.children_ul.className = "children_ul";
2938 me.node.li.appendChild(me.node.children_ul);
2939 me.node.depth = 0;
2940
2941 get_node(me, me.node);
2942
2943 me.this_page = this_page_relative(toroot);
2944 me.breadcrumbs = find_page(me.this_page, root_nodes);
2945 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2946 var mom = me.node;
2947 for (var i in me.breadcrumbs) {
2948 var j = me.breadcrumbs[i];
2949 mom = mom.children[j];
2950 expand_node(me, mom);
2951 }
2952 mom.label_div.className = mom.label_div.className + " selected";
2953 addLoadEvent(function() {
2954 scrollIntoView("nav-tree");
2955 });
2956 }
2957}
2958
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07002959
2960
2961
2962
2963
2964
2965
Robert Lyd2dd6e52012-11-29 21:28:48 -08002966/* TODO: eliminate redundancy with non-google functions */
2967function init_google_navtree(navtree_id, toroot, root_nodes)
2968{
2969 var me = new Object();
2970 me.toroot = toroot;
2971 me.node = new Object();
2972
2973 me.node.li = document.getElementById(navtree_id);
2974 me.node.children_data = root_nodes;
2975 me.node.children = new Array();
2976 me.node.children_ul = document.createElement("ul");
2977 me.node.get_children_ul = function() { return me.node.children_ul; };
2978 //me.node.children_ul.className = "children_ul";
2979 me.node.li.appendChild(me.node.children_ul);
2980 me.node.depth = 0;
2981
2982 get_google_node(me, me.node);
Robert Lyd2dd6e52012-11-29 21:28:48 -08002983}
2984
2985function new_google_node(me, mom, text, link, children_data, api_level)
2986{
2987 var node = new Object();
2988 var child;
2989 node.children = Array();
2990 node.children_data = children_data;
2991 node.depth = mom.depth + 1;
2992 node.get_children_ul = function() {
2993 if (!node.children_ul) {
Scott Main3b90aff2013-08-01 18:09:35 -07002994 node.children_ul = document.createElement("ul");
2995 node.children_ul.className = "tree-list-children";
Robert Lyd2dd6e52012-11-29 21:28:48 -08002996 node.li.appendChild(node.children_ul);
2997 }
2998 return node.children_ul;
2999 };
3000 node.li = document.createElement("li");
3001
3002 mom.get_children_ul().appendChild(node.li);
Scott Main3b90aff2013-08-01 18:09:35 -07003003
3004
Robert Lyd2dd6e52012-11-29 21:28:48 -08003005 if(link) {
3006 child = document.createElement("a");
3007
3008 }
3009 else {
3010 child = document.createElement("span");
Scott Mainac71b2b2012-11-30 14:40:58 -08003011 child.className = "tree-list-subtitle";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003012
3013 }
3014 if (children_data != null) {
3015 node.li.className="nav-section";
3016 node.label_div = document.createElement("div");
Scott Main3b90aff2013-08-01 18:09:35 -07003017 node.label_div.className = "nav-section-header-ref";
Robert Lyd2dd6e52012-11-29 21:28:48 -08003018 node.li.appendChild(node.label_div);
3019 get_google_node(me, node);
3020 node.label_div.appendChild(child);
3021 }
3022 else {
3023 node.li.appendChild(child);
3024 }
3025 if(link) {
3026 child.href = me.toroot + link;
3027 }
3028 node.label = document.createTextNode(text);
3029 child.appendChild(node.label);
3030
3031 node.children_ul = null;
3032
3033 return node;
3034}
3035
3036function get_google_node(me, mom)
3037{
3038 mom.children_visited = true;
3039 var linkText;
3040 for (var i in mom.children_data) {
3041 var node_data = mom.children_data[i];
3042 linkText = node_data[0];
3043
3044 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3045 linkText = linkText.substr(19, linkText.length);
3046 }
3047 mom.children[i] = new_google_node(me, mom, linkText, node_data[1],
3048 node_data[2], node_data[3]);
3049 }
3050}
Scott Mainad08f072013-08-20 16:49:57 -07003051
3052
3053
3054
3055
3056
3057/****** NEW version of script to build google and sample navs dynamically ******/
3058// TODO: update Google reference docs to tolerate this new implementation
3059
Scott Maine624b3f2013-09-12 12:56:41 -07003060var NODE_NAME = 0;
3061var NODE_HREF = 1;
3062var NODE_GROUP = 2;
3063var NODE_TAGS = 3;
3064var NODE_CHILDREN = 4;
3065
Scott Mainad08f072013-08-20 16:49:57 -07003066function init_google_navtree2(navtree_id, data)
3067{
3068 var $containerUl = $("#"+navtree_id);
Scott Mainad08f072013-08-20 16:49:57 -07003069 for (var i in data) {
3070 var node_data = data[i];
3071 $containerUl.append(new_google_node2(node_data));
3072 }
3073
Scott Main70557ee2013-10-30 14:47:40 -07003074 // Make all third-generation list items 'sticky' to prevent them from collapsing
3075 $containerUl.find('li li li.nav-section').addClass('sticky');
3076
Scott Mainad08f072013-08-20 16:49:57 -07003077 initExpandableNavItems("#"+navtree_id);
3078}
3079
3080function new_google_node2(node_data)
3081{
Scott Maine624b3f2013-09-12 12:56:41 -07003082 var linkText = node_data[NODE_NAME];
Scott Mainad08f072013-08-20 16:49:57 -07003083 if(linkText.match("^"+"com.google.android")=="com.google.android"){
3084 linkText = linkText.substr(19, linkText.length);
3085 }
3086 var $li = $('<li>');
3087 var $a;
Scott Maine624b3f2013-09-12 12:56:41 -07003088 if (node_data[NODE_HREF] != null) {
Scott Main70557ee2013-10-30 14:47:40 -07003089 $a = $('<a href="' + toRoot + node_data[NODE_HREF] + '" title="' + linkText + '" >'
3090 + linkText + '</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003091 } else {
Scott Main70557ee2013-10-30 14:47:40 -07003092 $a = $('<a href="#" onclick="return false;" title="' + linkText + '" >'
3093 + linkText + '/</a>');
Scott Mainad08f072013-08-20 16:49:57 -07003094 }
3095 var $childUl = $('<ul>');
Scott Maine624b3f2013-09-12 12:56:41 -07003096 if (node_data[NODE_CHILDREN] != null) {
Scott Mainad08f072013-08-20 16:49:57 -07003097 $li.addClass("nav-section");
3098 $a = $('<div class="nav-section-header">').append($a);
Scott Maine624b3f2013-09-12 12:56:41 -07003099 if (node_data[NODE_HREF] == null) $a.addClass('empty');
Scott Mainad08f072013-08-20 16:49:57 -07003100
Scott Maine624b3f2013-09-12 12:56:41 -07003101 for (var i in node_data[NODE_CHILDREN]) {
3102 var child_node_data = node_data[NODE_CHILDREN][i];
Scott Mainad08f072013-08-20 16:49:57 -07003103 $childUl.append(new_google_node2(child_node_data));
3104 }
3105 $li.append($childUl);
3106 }
3107 $li.prepend($a);
3108
3109 return $li;
3110}
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
Robert Lyd2dd6e52012-11-29 21:28:48 -08003122function showGoogleRefTree() {
3123 init_default_google_navtree(toRoot);
3124 init_default_gcm_navtree(toRoot);
Robert Lyd2dd6e52012-11-29 21:28:48 -08003125}
3126
3127function init_default_google_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003128 // load json file for navtree data
3129 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) {
3130 // when the file is loaded, initialize the tree
3131 if(jqxhr.status === 200) {
3132 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA);
3133 highlightSidenav();
3134 resizeNav();
3135 }
3136 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003137}
3138
3139function init_default_gcm_navtree(toroot) {
Scott Mainf6145542013-04-01 16:38:11 -07003140 // load json file for navtree data
3141 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) {
3142 // when the file is loaded, initialize the tree
3143 if(jqxhr.status === 200) {
3144 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA);
3145 highlightSidenav();
3146 resizeNav();
3147 }
3148 });
Robert Lyd2dd6e52012-11-29 21:28:48 -08003149}
3150
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003151function showSamplesRefTree() {
3152 init_default_samples_navtree(toRoot);
3153}
3154
3155function init_default_samples_navtree(toroot) {
3156 // load json file for navtree data
3157 $.getScript(toRoot + 'samples_navtree_data.js', function(data, textStatus, jqxhr) {
3158 // when the file is loaded, initialize the tree
3159 if(jqxhr.status === 200) {
Scott Mainf1435b72013-10-30 16:27:38 -07003160 // hack to remove the "about the samples" link then put it back in
3161 // after we nuke the list to remove the dummy static list of samples
3162 var $firstLi = $("#nav.samples-nav > li:first-child").clone();
3163 $("#nav.samples-nav").empty();
3164 $("#nav.samples-nav").append($firstLi);
3165
Scott Mainad08f072013-08-20 16:49:57 -07003166 init_google_navtree2("nav.samples-nav", SAMPLES_NAVTREE_DATA);
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003167 highlightSidenav();
3168 resizeNav();
Scott Main03aca9a2013-10-31 07:20:55 -07003169 if ($("#jd-content #samples").length) {
3170 showSamples();
3171 }
Dirk Dougherty4f7e5152010-09-16 10:43:40 -07003172 }
3173 });
3174}
3175
Scott Mainf5089842012-08-14 16:31:07 -07003176/* TOGGLE INHERITED MEMBERS */
3177
3178/* Toggle an inherited class (arrow toggle)
3179 * @param linkObj The link that was clicked.
3180 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3181 * 'null' to simply toggle.
3182 */
3183function toggleInherited(linkObj, expand) {
3184 var base = linkObj.getAttribute("id");
3185 var list = document.getElementById(base + "-list");
3186 var summary = document.getElementById(base + "-summary");
3187 var trigger = document.getElementById(base + "-trigger");
3188 var a = $(linkObj);
3189 if ( (expand == null && a.hasClass("closed")) || expand ) {
3190 list.style.display = "none";
3191 summary.style.display = "block";
3192 trigger.src = toRoot + "assets/images/triangle-opened.png";
3193 a.removeClass("closed");
3194 a.addClass("opened");
3195 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
3196 list.style.display = "block";
3197 summary.style.display = "none";
3198 trigger.src = toRoot + "assets/images/triangle-closed.png";
3199 a.removeClass("opened");
3200 a.addClass("closed");
3201 }
3202 return false;
3203}
3204
3205/* Toggle all inherited classes in a single table (e.g. all inherited methods)
3206 * @param linkObj The link that was clicked.
3207 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
3208 * 'null' to simply toggle.
3209 */
3210function toggleAllInherited(linkObj, expand) {
3211 var a = $(linkObj);
3212 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
3213 var expandos = $(".jd-expando-trigger", table);
3214 if ( (expand == null && a.text() == "[Expand]") || expand ) {
3215 expandos.each(function(i) {
3216 toggleInherited(this, true);
3217 });
3218 a.text("[Collapse]");
3219 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
3220 expandos.each(function(i) {
3221 toggleInherited(this, false);
3222 });
3223 a.text("[Expand]");
3224 }
3225 return false;
3226}
3227
3228/* Toggle all inherited members in the class (link in the class title)
3229 */
3230function toggleAllClassInherited() {
3231 var a = $("#toggleAllClassInherited"); // get toggle link from class title
3232 var toggles = $(".toggle-all", $("#body-content"));
3233 if (a.text() == "[Expand All]") {
3234 toggles.each(function(i) {
3235 toggleAllInherited(this, true);
3236 });
3237 a.text("[Collapse All]");
3238 } else {
3239 toggles.each(function(i) {
3240 toggleAllInherited(this, false);
3241 });
3242 a.text("[Expand All]");
3243 }
3244 return false;
3245}
3246
3247/* Expand all inherited members in the class. Used when initiating page search */
3248function ensureAllInheritedExpanded() {
3249 var toggles = $(".toggle-all", $("#body-content"));
3250 toggles.each(function(i) {
3251 toggleAllInherited(this, true);
3252 });
3253 $("#toggleAllClassInherited").text("[Collapse All]");
3254}
3255
3256
3257/* HANDLE KEY EVENTS
3258 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
3259 */
3260var agent = navigator['userAgent'].toLowerCase();
3261var mac = agent.indexOf("macintosh") != -1;
3262
3263$(document).keydown( function(e) {
3264var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
3265 if (control && e.which == 70) { // 70 is "F"
3266 ensureAllInheritedExpanded();
3267 }
3268});
Scott Main498d7102013-08-21 15:47:38 -07003269
3270
3271
3272
3273
3274
3275/* On-demand functions */
3276
3277/** Move sample code line numbers out of PRE block and into non-copyable column */
3278function initCodeLineNumbers() {
3279 var numbers = $("#codesample-block a.number");
3280 if (numbers.length) {
3281 $("#codesample-line-numbers").removeClass("hidden").append(numbers);
3282 }
3283
3284 $(document).ready(function() {
3285 // select entire line when clicked
3286 $("span.code-line").click(function() {
3287 if (!shifted) {
3288 selectText(this);
3289 }
3290 });
3291 // invoke line link on double click
3292 $(".code-line").dblclick(function() {
3293 document.location.hash = $(this).attr('id');
3294 });
3295 // highlight the line when hovering on the number
3296 $("#codesample-line-numbers a.number").mouseover(function() {
3297 var id = $(this).attr('href');
3298 $(id).css('background','#e7e7e7');
3299 });
3300 $("#codesample-line-numbers a.number").mouseout(function() {
3301 var id = $(this).attr('href');
3302 $(id).css('background','none');
3303 });
3304 });
3305}
3306
3307// create SHIFT key binder to avoid the selectText method when selecting multiple lines
3308var shifted = false;
3309$(document).bind('keyup keydown', function(e){shifted = e.shiftKey; return true;} );
3310
3311// courtesy of jasonedelman.com
3312function selectText(element) {
3313 var doc = document
3314 , range, selection
3315 ;
3316 if (doc.body.createTextRange) { //ms
3317 range = doc.body.createTextRange();
3318 range.moveToElementText(element);
3319 range.select();
3320 } else if (window.getSelection) { //all others
Scott Main70557ee2013-10-30 14:47:40 -07003321 selection = window.getSelection();
Scott Main498d7102013-08-21 15:47:38 -07003322 range = doc.createRange();
3323 range.selectNodeContents(element);
3324 selection.removeAllRanges();
3325 selection.addRange(range);
3326 }
Scott Main285f0772013-08-22 23:22:09 +00003327}
Scott Main03aca9a2013-10-31 07:20:55 -07003328
3329
3330
3331
3332/** Display links and other information about samples that match the
3333 group specified by the URL */
3334function showSamples() {
3335 var group = $("#samples").attr('class');
3336 $("#samples").html("<p>Here are some samples for <b>" + group + "</b> apps:</p>");
3337
3338 var $ul = $("<ul>");
3339 $selectedLi = $("#nav li.selected");
3340
3341 $selectedLi.children("ul").children("li").each(function() {
3342 var $li = $("<li>").append($(this).find("a").first().clone());
3343 $ul.append($li);
3344 });
3345
3346 $("#samples").append($ul);
3347
3348}
Dirk Doughertyc3921652014-05-13 16:55:26 -07003349
3350
3351
3352/* ########################################################## */
3353/* ################### RESOURCE CARDS ##################### */
3354/* ########################################################## */
3355
3356/** Handle resource queries, collections, and grids (sections). Requires
3357 jd_tag_helpers.js and the *_unified_data.js to be loaded. */
3358
3359(function() {
3360 // Prevent the same resource from being loaded more than once per page.
3361 var addedPageResources = {};
3362
3363 $(document).ready(function() {
3364 $('.resource-widget').each(function() {
3365 initResourceWidget(this);
3366 });
3367
3368 /* Pass the line height to ellipsisfade() to adjust the height of the
3369 text container to show the max number of lines possible, without
3370 showing lines that are cut off. This works with the css ellipsis
3371 classes to fade last text line and apply an ellipsis char. */
3372
Scott Mainb16376f2014-05-21 20:35:47 -07003373 //card text currently uses 15px line height.
Dirk Doughertyc3921652014-05-13 16:55:26 -07003374 var lineHeight = 15;
3375 $('.card-info .text').ellipsisfade(lineHeight);
3376 });
3377
3378 /*
3379 Three types of resource layouts:
3380 Flow - Uses a fixed row-height flow using float left style.
3381 Carousel - Single card slideshow all same dimension absolute.
3382 Stack - Uses fixed columns and flexible element height.
3383 */
3384 function initResourceWidget(widget) {
3385 var $widget = $(widget);
3386 var isFlow = $widget.hasClass('resource-flow-layout'),
3387 isCarousel = $widget.hasClass('resource-carousel-layout'),
3388 isStack = $widget.hasClass('resource-stack-layout');
3389
3390 // find size of widget by pulling out its class name
3391 var sizeCols = 1;
3392 var m = $widget.get(0).className.match(/\bcol-(\d+)\b/);
3393 if (m) {
3394 sizeCols = parseInt(m[1], 10);
3395 }
3396
3397 var opts = {
3398 cardSizes: ($widget.data('cardsizes') || '').split(','),
3399 maxResults: parseInt($widget.data('maxresults') || '100', 10),
3400 itemsPerPage: $widget.data('itemsperpage'),
3401 sortOrder: $widget.data('sortorder'),
3402 query: $widget.data('query'),
3403 section: $widget.data('section'),
3404 sizeCols: sizeCols
3405 };
3406
3407 // run the search for the set of resources to show
3408
3409 var resources = buildResourceList(opts);
3410
3411 if (isFlow) {
3412 drawResourcesFlowWidget($widget, opts, resources);
3413 } else if (isCarousel) {
3414 drawResourcesCarouselWidget($widget, opts, resources);
3415 } else if (isStack) {
3416 var sections = buildSectionList(opts);
3417 opts['numStacks'] = $widget.data('numstacks');
3418 drawResourcesStackWidget($widget, opts, resources, sections);
3419 }
3420 }
3421
3422 /* Initializes a Resource Carousel Widget */
3423 function drawResourcesCarouselWidget($widget, opts, resources) {
3424 $widget.empty();
3425 var plusone = true; //always show plusone on carousel
3426
3427 $widget.addClass('resource-card slideshow-container')
3428 .append($('<a>').addClass('slideshow-prev').text('Prev'))
3429 .append($('<a>').addClass('slideshow-next').text('Next'));
3430
3431 var css = { 'width': $widget.width() + 'px',
3432 'height': $widget.height() + 'px' };
3433
3434 var $ul = $('<ul>');
3435
3436 for (var i = 0; i < resources.length; ++i) {
3437 //keep url clean for matching and offline mode handling
3438 var urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3439 var $card = $('<a>')
3440 .attr('href', urlPrefix + resources[i].url)
3441 .decorateResourceCard(resources[i],plusone);
3442
3443 $('<li>').css(css)
3444 .append($card)
3445 .appendTo($ul);
3446 }
3447
3448 $('<div>').addClass('frame')
3449 .append($ul)
3450 .appendTo($widget);
3451
3452 $widget.dacSlideshow({
3453 auto: true,
3454 btnPrev: '.slideshow-prev',
3455 btnNext: '.slideshow-next'
3456 });
3457 };
3458
3459 /* Initializes a Resource Card Stack Widget (column-based layout) */
3460 function drawResourcesStackWidget($widget, opts, resources, sections) {
3461 // Don't empty widget, grab all items inside since they will be the first
3462 // items stacked, followed by the resource query
3463 var plusone = true; //by default show plusone on section cards
3464 var cards = $widget.find('.resource-card').detach().toArray();
3465 var numStacks = opts.numStacks || 1;
3466 var $stacks = [];
3467 var urlString;
3468
3469 for (var i = 0; i < numStacks; ++i) {
3470 $stacks[i] = $('<div>').addClass('resource-card-stack')
3471 .appendTo($widget);
3472 }
3473
3474 var sectionResources = [];
3475
3476 // Extract any subsections that are actually resource cards
3477 for (var i = 0; i < sections.length; ++i) {
3478 if (!sections[i].sections || !sections[i].sections.length) {
3479 //keep url clean for matching and offline mode handling
3480 urlPrefix = sections[i].url.indexOf("//") > -1 ? "" : toRoot;
3481 // Render it as a resource card
3482
3483 sectionResources.push(
3484 $('<a>')
3485 .addClass('resource-card section-card')
3486 .attr('href', urlPrefix + sections[i].resource.url)
3487 .decorateResourceCard(sections[i].resource,plusone)[0]
3488 );
3489
3490 } else {
3491 cards.push(
3492 $('<div>')
3493 .addClass('resource-card section-card-menu')
3494 .decorateResourceSection(sections[i],plusone)[0]
3495 );
3496 }
3497 }
3498
3499 cards = cards.concat(sectionResources);
3500
3501 for (var i = 0; i < resources.length; ++i) {
3502 //keep url clean for matching and offline mode handling
3503 urlPrefix = resources[i].url.indexOf("//") > -1 ? "" : toRoot;
3504 var $card = $('<a>')
3505 .addClass('resource-card related-card')
3506 .attr('href', urlPrefix + resources[i].url)
3507 .decorateResourceCard(resources[i],plusone);
3508
3509 cards.push($card[0]);
3510 }
3511
3512 for (var i = 0; i < cards.length; ++i) {
3513 // Find the stack with the shortest height, but give preference to
3514 // left to right order.
3515 var minHeight = $stacks[0].height();
3516 var minIndex = 0;
3517
3518 for (var j = 1; j < numStacks; ++j) {
3519 var height = $stacks[j].height();
3520 if (height < minHeight - 45) {
3521 minHeight = height;
3522 minIndex = j;
3523 }
3524 }
3525
3526 $stacks[minIndex].append($(cards[i]));
3527 }
3528
3529 };
3530
3531 /* Initializes a flow widget, see distribute.scss for generating accompanying css */
3532 function drawResourcesFlowWidget($widget, opts, resources) {
3533 $widget.empty();
3534 var cardSizes = opts.cardSizes || ['6x6'];
3535 var i = 0, j = 0;
3536 var plusone = true; // by default show plusone on resource cards
3537
3538 while (i < resources.length) {
3539 var cardSize = cardSizes[j++ % cardSizes.length];
3540 cardSize = cardSize.replace(/^\s+|\s+$/,'');
Scott Mainb16376f2014-05-21 20:35:47 -07003541 // console.log("cardsize is " + cardSize);
Dirk Doughertyc3921652014-05-13 16:55:26 -07003542 // Some card sizes do not get a plusone button, such as where space is constrained
3543 // or for cards commonly embedded in docs (to improve overall page speed).
3544 plusone = !((cardSize == "6x2") || (cardSize == "6x3") ||
3545 (cardSize == "9x2") || (cardSize == "9x3") ||
3546 (cardSize == "12x2") || (cardSize == "12x3"));
3547
3548 // A stack has a third dimension which is the number of stacked items
3549 var isStack = cardSize.match(/(\d+)x(\d+)x(\d+)/);
3550 var stackCount = 0;
3551 var $stackDiv = null;
3552
3553 if (isStack) {
3554 // Create a stack container which should have the dimensions defined
3555 // by the product of the items inside.
3556 $stackDiv = $('<div>').addClass('resource-card-stack resource-card-' + isStack[1]
3557 + 'x' + isStack[2] * isStack[3]) .appendTo($widget);
3558 }
3559
3560 // Build each stack item or just a single item
3561 do {
3562 var resource = resources[i];
3563 //keep url clean for matching and offline mode handling
3564 urlPrefix = resource.url.indexOf("//") > -1 ? "" : toRoot;
3565 var $card = $('<a>')
3566 .addClass('resource-card resource-card-' + cardSize + ' resource-card-' + resource.type)
3567 .attr('href', urlPrefix + resource.url);
3568
3569 if (isStack) {
3570 $card.addClass('resource-card-' + isStack[1] + 'x' + isStack[2]);
3571 if (++stackCount == parseInt(isStack[3])) {
3572 $card.addClass('resource-card-row-stack-last');
3573 stackCount = 0;
3574 }
3575 } else {
3576 stackCount = 0;
3577 }
3578
3579 $card.decorateResourceCard(resource,plusone)
3580 .appendTo($stackDiv || $widget);
3581
3582 } while (++i < resources.length && stackCount > 0);
3583 }
3584 }
3585
3586 /* Build a site map of resources using a section as a root. */
3587 function buildSectionList(opts) {
3588 if (opts.section && SECTION_BY_ID[opts.section]) {
3589 return SECTION_BY_ID[opts.section].sections || [];
3590 }
3591 return [];
3592 }
3593
3594 function buildResourceList(opts) {
3595 var maxResults = opts.maxResults || 100;
3596
3597 var query = opts.query || '';
3598 var expressions = parseResourceQuery(query);
3599 var addedResourceIndices = {};
3600 var results = [];
3601
3602 for (var i = 0; i < expressions.length; i++) {
3603 var clauses = expressions[i];
3604
3605 // build initial set of resources from first clause
3606 var firstClause = clauses[0];
3607 var resources = [];
3608 switch (firstClause.attr) {
3609 case 'type':
3610 resources = ALL_RESOURCES_BY_TYPE[firstClause.value];
3611 break;
3612 case 'lang':
3613 resources = ALL_RESOURCES_BY_LANG[firstClause.value];
3614 break;
3615 case 'tag':
3616 resources = ALL_RESOURCES_BY_TAG[firstClause.value];
3617 break;
3618 case 'collection':
3619 var urls = RESOURCE_COLLECTIONS[firstClause.value].resources || [];
3620 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3621 break;
3622 case 'section':
3623 var urls = SITE_MAP[firstClause.value].sections || [];
3624 resources = urls.map(function(url){ return ALL_RESOURCES_BY_URL[url]; });
3625 break;
3626 }
3627 // console.log(firstClause.attr + ':' + firstClause.value);
3628 resources = resources || [];
3629
3630 // use additional clauses to filter corpus
3631 if (clauses.length > 1) {
3632 var otherClauses = clauses.slice(1);
3633 resources = resources.filter(getResourceMatchesClausesFilter(otherClauses));
3634 }
3635
3636 // filter out resources already added
3637 if (i > 1) {
3638 resources = resources.filter(getResourceNotAlreadyAddedFilter(addedResourceIndices));
3639 }
3640
3641 // add to list of already added indices
3642 for (var j = 0; j < resources.length; j++) {
3643 // console.log(resources[j].title);
3644 addedResourceIndices[resources[j].index] = 1;
3645 }
3646
3647 // concat to final results list
3648 results = results.concat(resources);
3649 }
3650
3651 if (opts.sortOrder && results.length) {
3652 var attr = opts.sortOrder;
3653
3654 if (opts.sortOrder == 'random') {
3655 var i = results.length, j, temp;
3656 while (--i) {
3657 j = Math.floor(Math.random() * (i + 1));
3658 temp = results[i];
3659 results[i] = results[j];
3660 results[j] = temp;
3661 }
3662 } else {
3663 var desc = attr.charAt(0) == '-';
3664 if (desc) {
3665 attr = attr.substring(1);
3666 }
3667 results = results.sort(function(x,y) {
3668 return (desc ? -1 : 1) * (parseInt(x[attr], 10) - parseInt(y[attr], 10));
3669 });
3670 }
3671 }
3672
3673 results = results.filter(getResourceNotAlreadyAddedFilter(addedPageResources));
3674 results = results.slice(0, maxResults);
3675
3676 for (var j = 0; j < results.length; ++j) {
3677 addedPageResources[results[j].index] = 1;
3678 }
3679
3680 return results;
3681 }
3682
3683
3684 function getResourceNotAlreadyAddedFilter(addedResourceIndices) {
3685 return function(resource) {
3686 return !addedResourceIndices[resource.index];
3687 };
3688 }
3689
3690
3691 function getResourceMatchesClausesFilter(clauses) {
3692 return function(resource) {
3693 return doesResourceMatchClauses(resource, clauses);
3694 };
3695 }
3696
3697
3698 function doesResourceMatchClauses(resource, clauses) {
3699 for (var i = 0; i < clauses.length; i++) {
3700 var map;
3701 switch (clauses[i].attr) {
3702 case 'type':
3703 map = IS_RESOURCE_OF_TYPE[clauses[i].value];
3704 break;
3705 case 'lang':
3706 map = IS_RESOURCE_IN_LANG[clauses[i].value];
3707 break;
3708 case 'tag':
3709 map = IS_RESOURCE_TAGGED[clauses[i].value];
3710 break;
3711 }
3712
3713 if (!map || (!!clauses[i].negative ? map[resource.index] : !map[resource.index])) {
3714 return clauses[i].negative;
3715 }
3716 }
3717 return true;
3718 }
3719
3720
3721 function parseResourceQuery(query) {
3722 // Parse query into array of expressions (expression e.g. 'tag:foo + type:video')
3723 var expressions = [];
3724 var expressionStrs = query.split(',') || [];
3725 for (var i = 0; i < expressionStrs.length; i++) {
3726 var expr = expressionStrs[i] || '';
3727
3728 // Break expression into clauses (clause e.g. 'tag:foo')
3729 var clauses = [];
3730 var clauseStrs = expr.split(/(?=[\+\-])/);
3731 for (var j = 0; j < clauseStrs.length; j++) {
3732 var clauseStr = clauseStrs[j] || '';
3733
3734 // Get attribute and value from clause (e.g. attribute='tag', value='foo')
3735 var parts = clauseStr.split(':');
3736 var clause = {};
3737
3738 clause.attr = parts[0].replace(/^\s+|\s+$/g,'');
3739 if (clause.attr) {
3740 if (clause.attr.charAt(0) == '+') {
3741 clause.attr = clause.attr.substring(1);
3742 } else if (clause.attr.charAt(0) == '-') {
3743 clause.negative = true;
3744 clause.attr = clause.attr.substring(1);
3745 }
3746 }
3747
3748 if (parts.length > 1) {
3749 clause.value = parts[1].replace(/^\s+|\s+$/g,'');
3750 }
3751
3752 clauses.push(clause);
3753 }
3754
3755 if (!clauses.length) {
3756 continue;
3757 }
3758
3759 expressions.push(clauses);
3760 }
3761
3762 return expressions;
3763 }
3764})();
3765
3766(function($) {
3767 /* Simple jquery function to create dom for a standard resource card */
3768 $.fn.decorateResourceCard = function(resource,plusone) {
3769 var section = resource.group || resource.type;
3770 var imgUrl;
3771 if (resource.image) {
3772 //keep url clean for matching and offline mode handling
3773 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3774 imgUrl = urlPrefix + resource.image;
3775 }
3776 //add linkout logic here. check url or type, assign a class, map to css :after
3777 $('<div>')
3778 .addClass('card-bg')
3779 .css('background-image', 'url(' + (imgUrl || toRoot + 'assets/images/resource-card-default-android.jpg') + ')')
3780 .appendTo(this);
3781 if (!plusone) {
3782 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3783 .append($('<div>').addClass('section').text(section))
3784 .append($('<div>').addClass('title').html(resource.title))
3785 .append($('<div>').addClass('description ellipsis')
3786 .append($('<div>').addClass('text').html(resource.summary))
3787 .append($('<div>').addClass('util')))
3788 .appendTo(this);
3789 } else {
3790 $('<div>').addClass('card-info' + (!resource.summary ? ' empty-desc' : ''))
3791 .append($('<div>').addClass('section').text(section))
3792 .append($('<div>').addClass('title').html(resource.title))
3793 .append($('<div>').addClass('description ellipsis')
3794 .append($('<div>').addClass('text').html(resource.summary))
3795 .append($('<div>').addClass('util')
3796 .append($('<div>').addClass('g-plusone')
3797 .attr('data-size', 'small')
3798 .attr('data-align', 'right')
3799 .attr('data-href', resource.url))))
3800 .appendTo(this);
3801 }
3802
3803 return this;
3804 };
3805
3806 /* Simple jquery function to create dom for a resource section card (menu) */
3807 $.fn.decorateResourceSection = function(section,plusone) {
3808 var resource = section.resource;
3809 //keep url clean for matching and offline mode handling
3810 var urlPrefix = resource.image.indexOf("//") > -1 ? "" : toRoot;
3811 var $base = $('<a>')
3812 .addClass('card-bg')
3813 .attr('href', resource.url)
3814 .append($('<div>').addClass('card-section-icon')
3815 .append($('<div>').addClass('icon'))
3816 .append($('<div>').addClass('section').html(resource.title)))
3817 .appendTo(this);
3818
3819 var $cardInfo = $('<div>').addClass('card-info').appendTo(this);
3820
3821 if (section.sections && section.sections.length) {
3822 // Recurse the section sub-tree to find a resource image.
3823 var stack = [section];
3824
3825 while (stack.length) {
3826 if (stack[0].resource.image) {
3827 $base.css('background-image', 'url(' + urlPrefix + stack[0].resource.image + ')');
3828 break;
3829 }
3830
3831 if (stack[0].sections) {
3832 stack = stack.concat(stack[0].sections);
3833 }
3834
3835 stack.shift();
3836 }
3837
3838 var $ul = $('<ul>')
3839 .appendTo($cardInfo);
3840
3841 var max = section.sections.length > 3 ? 3 : section.sections.length;
3842
3843 for (var i = 0; i < max; ++i) {
3844
3845 var subResource = section.sections[i];
3846 if (!plusone) {
3847 $('<li>')
3848 .append($('<a>').attr('href', subResource.url)
3849 .append($('<div>').addClass('title').html(subResource.title))
3850 .append($('<div>').addClass('description ellipsis')
3851 .append($('<div>').addClass('text').html(subResource.summary))
3852 .append($('<div>').addClass('util'))))
3853 .appendTo($ul);
3854 } else {
3855 $('<li>')
3856 .append($('<a>').attr('href', subResource.url)
3857 .append($('<div>').addClass('title').html(subResource.title))
3858 .append($('<div>').addClass('description ellipsis')
3859 .append($('<div>').addClass('text').html(subResource.summary))
3860 .append($('<div>').addClass('util')
3861 .append($('<div>').addClass('g-plusone')
3862 .attr('data-size', 'small')
3863 .attr('data-align', 'right')
3864 .attr('data-href', resource.url)))))
3865 .appendTo($ul);
3866 }
3867 }
3868
3869 // Add a more row
3870 if (max < section.sections.length) {
3871 $('<li>')
3872 .append($('<a>').attr('href', resource.url)
3873 .append($('<div>')
3874 .addClass('title')
3875 .text('More')))
3876 .appendTo($ul);
3877 }
3878 } else {
3879 // No sub-resources, just render description?
3880 }
3881
3882 return this;
3883 };
3884})(jQuery);
3885/* Calculate the vertical area remaining */
3886(function($) {
3887 $.fn.ellipsisfade= function(lineHeight) {
3888 this.each(function() {
3889 // get element text
3890 var $this = $(this);
3891 var remainingHeight = $this.parent().parent().height();
3892 $this.parent().siblings().each(function ()
3893 {
3894 var h = $(this).height();
3895 remainingHeight = remainingHeight - h;
3896 });
3897
3898 adjustedRemainingHeight = ((remainingHeight)/lineHeight>>0)*lineHeight
3899 $this.parent().css({'height': adjustedRemainingHeight});
3900 $this.css({'height': "auto"});
3901 });
3902
3903 return this;
3904 };
3905}) (jQuery);