blob: 376f9b828f8b25d1af92f352a24b0021e4cc2205 [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
9
Scott Main1b3db112012-07-03 14:06:22 -070010var basePath = getBaseUri(location.pathname);
11var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1));
12
Scott Maine4d8f1b2012-06-21 18:03:05 -070013
14/****** ON LOAD SET UP STUFF *********/
15
16var navBarIsFixed = false;
17$(document).ready(function() {
18 // init the fullscreen toggle click event
19 $('#nav-swap .fullscreen').click(function(){
20 if ($(this).hasClass('disabled')) {
21 toggleFullscreen(true);
22 } else {
23 toggleFullscreen(false);
24 }
25 });
26
27 // initialize the divs with custom scrollbars
28 $('.scroll-pane').jScrollPane( {verticalGutter:0} );
29
30 // add HRs below all H2s (except for a few other h2 variants)
Scott Maindb3678b2012-10-23 14:13:41 -070031 $('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 -070032
33 // set search's onkeyup handler here so we can show suggestions
34 // even while search results are visible
Scott Main1b3db112012-07-03 14:06:22 -070035 $("#search_autocomplete").keyup(function() {return search_changed(event, false, toRoot)});
Scott Maine4d8f1b2012-06-21 18:03:05 -070036
37 // set up the search close button
38 $('.search .close').click(function() {
39 $searchInput = $('#search_autocomplete');
40 $searchInput.attr('value', '');
41 $(this).addClass("hide");
42 $("#search-container").removeClass('active');
43 $("#search_autocomplete").blur();
44 search_focus_changed($searchInput.get(), false); // see search_autocomplete.js
45 hideResults(); // see search_autocomplete.js
46 });
47 $('.search').click(function() {
48 if (!$('#search_autocomplete').is(":focused")) {
49 $('#search_autocomplete').focus();
50 }
51 });
52
53 // Set up quicknav
54 var quicknav_open = false;
55 $("#btn-quicknav").click(function() {
56 if (quicknav_open) {
57 $(this).removeClass('active');
58 quicknav_open = false;
59 collapse();
60 } else {
61 $(this).addClass('active');
62 quicknav_open = true;
63 expand();
64 }
65 })
66
67 var expand = function() {
68 $('#header-wrap').addClass('quicknav');
69 $('#quicknav').stop().show().animate({opacity:'1'});
70 }
71
72 var collapse = function() {
73 $('#quicknav').stop().animate({opacity:'0'}, 100, function() {
74 $(this).hide();
75 $('#header-wrap').removeClass('quicknav');
76 });
77 }
78
79
80 //Set up search
81 $("#search_autocomplete").focus(function() {
82 $("#search-container").addClass('active');
83 })
84 $("#search-container").mouseover(function() {
85 $("#search-container").addClass('active');
86 $("#search_autocomplete").focus();
87 })
88 $("#search-container").mouseout(function() {
89 if ($("#search_autocomplete").is(":focus")) return;
90 if ($("#search_autocomplete").val() == '') {
91 setTimeout(function(){
92 $("#search-container").removeClass('active');
93 $("#search_autocomplete").blur();
94 },250);
95 }
96 })
97 $("#search_autocomplete").blur(function() {
98 if ($("#search_autocomplete").val() == '') {
99 $("#search-container").removeClass('active');
100 }
101 })
102
103
104 // prep nav expandos
105 var pagePath = document.location.pathname;
106 // account for intl docs by removing the intl/*/ path
107 if (pagePath.indexOf("/intl/") == 0) {
108 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last /
109 }
110
111 if (pagePath.indexOf(SITE_ROOT) == 0) {
112 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') {
113 pagePath += 'index.html';
114 }
115 }
116
117 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') {
118 // If running locally, SITE_ROOT will be a relative path, so account for that by
119 // finding the relative URL to this page. This will allow us to find links on the page
120 // leading back to this page.
121 var pathParts = pagePath.split('/');
122 var relativePagePathParts = [];
123 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3;
124 for (var i = 0; i < upDirs; i++) {
125 relativePagePathParts.push('..');
126 }
127 for (var i = 0; i < upDirs; i++) {
128 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]);
129 }
130 relativePagePathParts.push(pathParts[pathParts.length - 1]);
131 pagePath = relativePagePathParts.join('/');
132 } else {
133 // Otherwise the page path is already an absolute URL
134 }
135
136 // select current page in sidenav and set up prev/next links if they exist
137 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]');
Scott Main5a1123e2012-09-26 12:51:28 -0700138 var $selListItem;
Scott Maine4d8f1b2012-06-21 18:03:05 -0700139 if ($selNavLink.length) {
140 $selListItem = $selNavLink.closest('li');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700141 $selListItem.addClass('selected');
Scott Main502c9392012-11-27 15:00:40 -0800142
143 // Traverse up the tree and expand all parent nav-sections
144 $selNavLink.parents('li.nav-section').each(function() {
145 $(this).addClass('expanded');
146 $(this).children('ul').show();
147 });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700148
149
150 // $selListItem.closest('li.nav-section').closest('li.nav-section').addClass('expanded');
151 // $selListItem.closest('li.nav-section').closest('li.nav-section').children('ul').show();
152
153 // set up prev links
154 var $prevLink = [];
155 var $prevListItem = $selListItem.prev('li');
156
157 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true :
158false; // navigate across topic boundaries only in design docs
159 if ($prevListItem.length) {
160 if ($prevListItem.hasClass('nav-section')) {
Scott Main5a1123e2012-09-26 12:51:28 -0700161 // jump to last topic of previous section
162 $prevLink = $prevListItem.find('a:last');
163 } else if (!$selListItem.hasClass('nav-section')) {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700164 // jump to previous topic in this section
165 $prevLink = $prevListItem.find('a:eq(0)');
166 }
167 } else {
168 // jump to this section's index page (if it exists)
169 var $parentListItem = $selListItem.parents('li');
170 $prevLink = $selListItem.parents('li').find('a');
171
172 // except if cross boundaries aren't allowed, and we're at the top of a section already
173 // (and there's another parent)
174 if (!crossBoundaries && $parentListItem.hasClass('nav-section')
175 && $selListItem.hasClass('nav-section')) {
176 $prevLink = [];
177 }
178 }
179
Scott Maine4d8f1b2012-06-21 18:03:05 -0700180 // set up next links
181 var $nextLink = [];
Scott Maine4d8f1b2012-06-21 18:03:05 -0700182 var startClass = false;
183 var training = $(".next-class-link").length; // decides whether to provide "next class" link
184 var isCrossingBoundary = false;
185
186 if ($selListItem.hasClass('nav-section')) {
187 // we're on an index page, jump to the first topic
Scott Mainb505ca62012-07-26 18:00:14 -0700188 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700189
190 // if there aren't any children, go to the next section (required for About pages)
191 if($nextLink.length == 0) {
192 $nextLink = $selListItem.next('li').find('a');
Scott Mainb505ca62012-07-26 18:00:14 -0700193 } else if ($('.topic-start-link').length) {
194 // as long as there's a child link and there is a "topic start link" (we're on a landing)
195 // then set the landing page "start link" text to be the first doc title
196 $('.topic-start-link').text($nextLink.text().toUpperCase());
Scott Maine4d8f1b2012-06-21 18:03:05 -0700197 }
198
Scott Main5a1123e2012-09-26 12:51:28 -0700199 // If the selected page has a description, then it's a class or article homepage
200 if ($selListItem.find('a[description]').length) {
201 // this means we're on a class landing page
Scott Maine4d8f1b2012-06-21 18:03:05 -0700202 startClass = true;
203 }
204 } else {
205 // jump to the next topic in this section (if it exists)
206 $nextLink = $selListItem.next('li').find('a:eq(0)');
207 if (!$nextLink.length) {
Scott Main5a1123e2012-09-26 12:51:28 -0700208 isCrossingBoundary = true;
209 // no more topics in this section, jump to the first topic in the next section
210 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)');
211 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course)
212 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)');
Scott Maine4d8f1b2012-06-21 18:03:05 -0700213 }
214 }
215 }
Scott Main5a1123e2012-09-26 12:51:28 -0700216
217 if (startClass) {
218 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide");
219
220 // if there's no training bar (below the start button),
221 // then we need to add a bottom border to button
222 if (!$("#tb").length) {
223 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'});
Scott Maine4d8f1b2012-06-21 18:03:05 -0700224 }
Scott Main5a1123e2012-09-26 12:51:28 -0700225 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries
226 $('.content-footer.next-class').show();
227 $('.next-page-link').attr('href','')
228 .removeClass("hide").addClass("disabled")
229 .click(function() { return false; });
230
231 $('.next-class-link').attr('href',$nextLink.attr('href'))
232 .removeClass("hide").append($nextLink.html());
233 $('.next-class-link').find('.new').empty();
234 } else {
235 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide");
236 }
237
238 if (!startClass && $prevLink.length) {
239 var prevHref = $prevLink.attr('href');
240 if (prevHref == SITE_ROOT + 'index.html') {
241 // Don't show Previous when it leads to the homepage
242 } else {
243 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide");
244 }
245 }
246
247 // If this is a training 'article', there should be no prev/next nav
248 // ... if the grandparent is the "nav" ... and it has no child list items...
249 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') &&
250 !$selListItem.find('li').length) {
251 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled")
252 .click(function() { return false; });
Scott Maine4d8f1b2012-06-21 18:03:05 -0700253 }
254
255 }
Scott Main5a1123e2012-09-26 12:51:28 -0700256
257
258
259 // Set up the course landing pages for Training with class names and descriptions
260 if ($('body.trainingcourse').length) {
261 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a');
262 var $classDescriptions = $classLinks.attr('description');
263
264 var $olClasses = $('<ol class="class-list"></ol>');
265 var $liClass;
266 var $imgIcon;
267 var $h2Title;
268 var $pSummary;
269 var $olLessons;
270 var $liLesson;
271 $classLinks.each(function(index) {
272 $liClass = $('<li></li>');
273 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>');
274 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>');
275
276 $olLessons = $('<ol class="lesson-list"></ol>');
277
278 $lessons = $(this).closest('li').find('ul li a');
279
280 if ($lessons.length) {
281 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" alt=""/>');
282 $lessons.each(function(index) {
283 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>');
284 });
285 } else {
286 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" alt=""/>');
287 $pSummary.addClass('article');
288 }
289
290 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons);
291 $olClasses.append($liClass);
292 });
293 $('.jd-descr').append($olClasses);
294 }
295
Scott Maine4d8f1b2012-06-21 18:03:05 -0700296
297
298
299 // Set up expand/collapse behavior
300 $('#nav li.nav-section .nav-section-header').click(function() {
301 var section = $(this).closest('li.nav-section');
302 if (section.hasClass('expanded')) {
303 /* hide me */
304 // if (section.hasClass('selected') || section.find('li').hasClass('selected')) {
305 // /* but not if myself or my descendents are selected */
306 // return;
307 // }
308 section.children('ul').slideUp(250, function() {
309 section.closest('li').removeClass('expanded');
310 resizeNav();
311 });
312 } else {
313 /* show me */
314 // first hide all other siblings
315 var $others = $('li.nav-section.expanded', $(this).closest('ul'));
316 $others.removeClass('expanded').children('ul').slideUp(250);
317
318 // now expand me
319 section.closest('li').addClass('expanded');
320 section.children('ul').slideDown(250, function() {
321 resizeNav();
322 });
323 }
324 });
325
326 $(".scroll-pane").scroll(function(event) {
327 event.preventDefault();
328 return false;
329 });
330
331 /* Resize nav height when window height changes */
332 $(window).resize(function() {
333 if ($('#side-nav').length == 0) return;
334 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
335 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed
336 // make sidenav behave when resizing the window and side-scolling is a concern
337 if (navBarIsFixed) {
338 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) {
339 updateSideNavPosition();
340 } else {
341 updateSidenavFullscreenWidth();
342 }
343 }
344 resizeNav();
345 });
346
347
348 // Set up fixed navbar
349 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll
350 $(window).scroll(function(event) {
351 if ($('#side-nav').length == 0) return;
352 if (event.target.nodeName == "DIV") {
353 // Dump scroll event if the target is a DIV, because that means the event is coming
354 // from a scrollable div and so there's no need to make adjustments to our layout
355 return;
356 }
357 var scrollTop = $(window).scrollTop();
358 var headerHeight = $('#header').outerHeight();
359 var subheaderHeight = $('#nav-x').outerHeight();
360 var searchResultHeight = $('#searchResults').is(":visible") ?
361 $('#searchResults').outerHeight() : 0;
362 var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight;
363 var navBarShouldBeFixed = scrollTop > totalHeaderHeight;
364
365 var scrollLeft = $(window).scrollLeft();
366 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match
367 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) {
368 updateSideNavPosition();
369 prevScrollLeft = scrollLeft;
370 }
371
372 // Don't continue if the header is sufficently far away
373 // (to avoid intensive resizing that slows scrolling)
374 if (navBarIsFixed && navBarShouldBeFixed) {
375 return;
376 }
377
378 if (navBarIsFixed != navBarShouldBeFixed) {
379 if (navBarShouldBeFixed) {
380 // make it fixed
381 var width = $('#devdoc-nav').width();
382 $('#devdoc-nav')
383 .addClass('fixed')
384 .css({'width':width+'px'})
385 .prependTo('#body-content');
386 // add neato "back to top" button
387 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
388
389 // update the sidenaav position for side scrolling
390 updateSideNavPosition();
391 } else {
392 // make it static again
393 $('#devdoc-nav')
394 .removeClass('fixed')
395 .css({'width':'auto','margin':''})
396 .prependTo('#side-nav');
397 $('#devdoc-nav a.totop').hide();
398 }
399 navBarIsFixed = navBarShouldBeFixed;
400 }
401
402 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance
403 });
404
405
406 var navBarLeftPos;
407 if ($('#devdoc-nav').length) {
408 setNavBarLeftPos();
409 }
410
411
412 // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away
413 // from the page)
414 $('.nav-section-header').find('a:eq(0)').click(function(evt) {
415 window.location.href = $(this).attr('href');
416 return false;
417 });
418
419 // Set up play-on-hover <video> tags.
420 $('video.play-on-hover').bind('click', function(){
421 $(this).get(0).load(); // in case the video isn't seekable
422 $(this).get(0).play();
423 });
424
425 // Set up tooltips
426 var TOOLTIP_MARGIN = 10;
Scott Maindb3678b2012-10-23 14:13:41 -0700427 $('acronym,.tooltip-link').each(function() {
Scott Maine4d8f1b2012-06-21 18:03:05 -0700428 var $target = $(this);
429 var $tooltip = $('<div>')
430 .addClass('tooltip-box')
Scott Maindb3678b2012-10-23 14:13:41 -0700431 .append($target.attr('title'))
Scott Maine4d8f1b2012-06-21 18:03:05 -0700432 .hide()
433 .appendTo('body');
434 $target.removeAttr('title');
435
436 $target.hover(function() {
437 // in
438 var targetRect = $target.offset();
439 targetRect.width = $target.width();
440 targetRect.height = $target.height();
441
442 $tooltip.css({
443 left: targetRect.left,
444 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN
445 });
446 $tooltip.addClass('below');
447 $tooltip.show();
448 }, function() {
449 // out
450 $tooltip.hide();
451 });
452 });
453
454 // Set up <h2> deeplinks
455 $('h2').click(function() {
456 var id = $(this).attr('id');
457 if (id) {
458 document.location.hash = id;
459 }
460 });
461
462 //Loads the +1 button
463 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
464 po.src = 'https://apis.google.com/js/plusone.js';
465 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
466
467
468 // Revise the sidenav widths to make room for the scrollbar
469 // which avoids the visible width from changing each time the bar appears
470 var $sidenav = $("#side-nav");
471 var sidenav_width = parseInt($sidenav.innerWidth());
472
473 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width
474
475
476 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
477
478 if ($(".scroll-pane").length > 1) {
479 // Check if there's a user preference for the panel heights
480 var cookieHeight = readCookie("reference_height");
481 if (cookieHeight) {
482 restoreHeight(cookieHeight);
483 }
484 }
485
486 resizeNav();
487
488
489});
490
491
492
493function toggleFullscreen(enable) {
494 var delay = 20;
495 var enabled = true;
496 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]');
497 if (enable) {
498 // Currently NOT USING fullscreen; enable fullscreen
499 stylesheet.removeAttr('disabled');
500 $('#nav-swap .fullscreen').removeClass('disabled');
501 $('#devdoc-nav').css({left:''});
502 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch
503 enabled = true;
504 } else {
505 // Currently USING fullscreen; disable fullscreen
506 stylesheet.attr('disabled', 'disabled');
507 $('#nav-swap .fullscreen').addClass('disabled');
508 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch
509 enabled = false;
510 }
511 writeCookie("fullscreen", enabled, null, null);
512 setNavBarLeftPos();
513 resizeNav(delay);
514 updateSideNavPosition();
515 setTimeout(initSidenavHeightResize,delay);
516}
517
518
519function setNavBarLeftPos() {
520 navBarLeftPos = $('#body-content').offset().left;
521}
522
523
524function updateSideNavPosition() {
525 var newLeft = $(window).scrollLeft() - navBarLeftPos;
526 $('#devdoc-nav').css({left: -newLeft});
527 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))});
528}
529
530
531
532
533
534
535
536
537// TODO: use $(document).ready instead
538function addLoadEvent(newfun) {
539 var current = window.onload;
540 if (typeof window.onload != 'function') {
541 window.onload = newfun;
542 } else {
543 window.onload = function() {
544 current();
545 newfun();
546 }
547 }
548}
549
550var agent = navigator['userAgent'].toLowerCase();
551// If a mobile phone, set flag and do mobile setup
552if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod
553 (agent.indexOf("blackberry") != -1) ||
554 (agent.indexOf("webos") != -1) ||
555 (agent.indexOf("mini") != -1)) { // opera mini browsers
556 isMobile = true;
557}
558
559
560/* loads the lists.js file to the page.
561Loading this in the head was slowing page load time */
562addLoadEvent( function() {
563 var lists = document.createElement("script");
564 lists.setAttribute("type","text/javascript");
565 lists.setAttribute("src", toRoot+"reference/lists.js");
566 document.getElementsByTagName("head")[0].appendChild(lists);
567} );
568
569
570addLoadEvent( function() {
571 $("pre:not(.no-pretty-print)").addClass("prettyprint");
572 prettyPrint();
573} );
574
Scott Maine4d8f1b2012-06-21 18:03:05 -0700575function init() {
576 //resizeNav();
577
578 resizePackagesNav = $("#resize-packages-nav");
579 classesNav = $("#classes-nav");
580 devdocNav = $("#devdoc-nav");
581
582 var cookiePath = "";
583 if (location.href.indexOf("/reference/") != -1) {
584 cookiePath = "reference_";
585 } else if (location.href.indexOf("/guide/") != -1) {
586 cookiePath = "guide_";
587 } else if (location.href.indexOf("/tools/") != -1) {
588 cookiePath = "tools_";
589 } else if (location.href.indexOf("/training/") != -1) {
590 cookiePath = "training_";
591 } else if (location.href.indexOf("/design/") != -1) {
592 cookiePath = "design_";
593 } else if (location.href.indexOf("/distribute/") != -1) {
594 cookiePath = "distribute_";
595 }
596}
597
598
599
600/* ######### RESIZE THE SIDENAV HEIGHT ########## */
601
602function resizeNav(delay) {
603 var $nav = $("#devdoc-nav");
604 var $window = $(window);
605 var navHeight;
606
607 // Get the height of entire window and the total header height.
608 // Then figure out based on scroll position whether the header is visible
609 var windowHeight = $window.height();
610 var scrollTop = $window.scrollTop();
611 var headerHeight = $('#header').outerHeight();
612 var subheaderHeight = $('#nav-x').outerHeight();
613 var headerVisible = (scrollTop < (headerHeight + subheaderHeight));
614
615 // get the height of space between nav and top of window.
616 // Could be either margin or top position, depending on whether the nav is fixed.
617 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1;
618 // add 1 for the #side-nav bottom margin
619
620 // Depending on whether the header is visible, set the side nav's height.
621 if (headerVisible) {
622 // The sidenav height grows as the header goes off screen
623 navHeight = windowHeight - (headerHeight + subheaderHeight - scrollTop) - topMargin;
624 } else {
625 // Once header is off screen, the nav height is almost full window height
626 navHeight = windowHeight - topMargin;
627 }
628
629
630
631 $scrollPanes = $(".scroll-pane");
632 if ($scrollPanes.length > 1) {
633 // subtract the height of the api level widget and nav swapper from the available nav height
634 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true));
635
636 $("#swapper").css({height:navHeight + "px"});
637 if ($("#nav-tree").is(":visible")) {
638 $("#nav-tree").css({height:navHeight});
639 }
640
641 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px";
642 //subtract 10px to account for drag bar
643
644 // if the window becomes small enough to make the class panel height 0,
645 // then the package panel should begin to shrink
646 if (parseInt(classesHeight) <= 0) {
647 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar
648 $("#packages-nav").css({height:navHeight - 10});
649 }
650
651 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'});
652 $("#classes-nav .jspContainer").css({height:classesHeight});
653
654
655 } else {
656 $nav.height(navHeight);
657 }
658
659 if (delay) {
660 updateFromResize = true;
661 delayedReInitScrollbars(delay);
662 } else {
663 reInitScrollbars();
664 }
665
666}
667
668var updateScrollbars = false;
669var updateFromResize = false;
670
671/* Re-initialize the scrollbars to account for changed nav size.
672 * This method postpones the actual update by a 1/4 second in order to optimize the
673 * scroll performance while the header is still visible, because re-initializing the
674 * scroll panes is an intensive process.
675 */
676function delayedReInitScrollbars(delay) {
677 // If we're scheduled for an update, but have received another resize request
678 // before the scheduled resize has occured, just ignore the new request
679 // (and wait for the scheduled one).
680 if (updateScrollbars && updateFromResize) {
681 updateFromResize = false;
682 return;
683 }
684
685 // We're scheduled for an update and the update request came from this method's setTimeout
686 if (updateScrollbars && !updateFromResize) {
687 reInitScrollbars();
688 updateScrollbars = false;
689 } else {
690 updateScrollbars = true;
691 updateFromResize = false;
692 setTimeout('delayedReInitScrollbars()',delay);
693 }
694}
695
696/* Re-initialize the scrollbars to account for changed nav size. */
697function reInitScrollbars() {
698 var pane = $(".scroll-pane").each(function(){
699 var api = $(this).data('jsp');
700 if (!api) { setTimeout(reInitScrollbars,300); return;}
701 api.reinitialise( {verticalGutter:0} );
702 });
703 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller
704}
705
706
707/* Resize the height of the nav panels in the reference,
708 * and save the new size to a cookie */
709function saveNavPanels() {
710 var basePath = getBaseUri(location.pathname);
711 var section = basePath.substring(1,basePath.indexOf("/",1));
712 writeCookie("height", resizePackagesNav.css("height"), section, null);
713}
714
715
716
717function restoreHeight(packageHeight) {
718 $("#resize-packages-nav").height(packageHeight);
719 $("#packages-nav").height(packageHeight);
720 // var classesHeight = navHeight - packageHeight;
721 // $("#classes-nav").css({height:classesHeight});
722 // $("#classes-nav .jspContainer").css({height:classesHeight});
723}
724
725
726
727/* ######### END RESIZE THE SIDENAV HEIGHT ########## */
728
729
730
731
732
733/** Scroll the jScrollPane to make the currently selected item visible
734 This is called when the page finished loading. */
735function scrollIntoView(nav) {
736 var $nav = $("#"+nav);
737 var element = $nav.jScrollPane({/* ...settings... */});
738 var api = element.data('jsp');
739
740 if ($nav.is(':visible')) {
741 var $selected = $(".selected", $nav);
742 if ($selected.length == 0) return;
743
744 var selectedOffset = $selected.position().top;
745 if (selectedOffset + 90 > $nav.height()) { // add 90 so that we scroll up even
746 // if the current item is close to the bottom
747 api.scrollTo(0, selectedOffset - ($nav.height() / 4), false); // scroll the item into view
748 // to be 1/4 of the way from the top
749 }
750 }
751}
752
753
754
755
756
757
758/* Show popup dialogs */
759function showDialog(id) {
760 $dialog = $("#"+id);
761 $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>');
762 $dialog.wrapInner('<div/>');
763 $dialog.removeClass("hide");
764}
765
766
767
768
769
770/* ######### COOKIES! ########## */
771
772function readCookie(cookie) {
773 var myCookie = cookie_namespace+"_"+cookie+"=";
774 if (document.cookie) {
775 var index = document.cookie.indexOf(myCookie);
776 if (index != -1) {
777 var valStart = index + myCookie.length;
778 var valEnd = document.cookie.indexOf(";", valStart);
779 if (valEnd == -1) {
780 valEnd = document.cookie.length;
781 }
782 var val = document.cookie.substring(valStart, valEnd);
783 return val;
784 }
785 }
786 return 0;
787}
788
789function writeCookie(cookie, val, section, expiration) {
790 if (val==undefined) return;
791 section = section == null ? "_" : "_"+section+"_";
792 if (expiration == null) {
793 var date = new Date();
794 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
795 expiration = date.toGMTString();
796 }
797 var cookieValue = cookie_namespace + section + cookie + "=" + val
798 + "; expires=" + expiration+"; path=/";
799 document.cookie = cookieValue;
800}
801
802/* ######### END COOKIES! ########## */
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828/*
829
830REMEMBER THE PREVIOUS PAGE FOR EACH TAB
831
832function loadLast(cookiePath) {
833 var location = window.location.href;
834 if (location.indexOf("/"+cookiePath+"/") != -1) {
835 return true;
836 }
837 var lastPage = readCookie(cookiePath + "_lastpage");
838 if (lastPage) {
839 window.location = lastPage;
840 return false;
841 }
842 return true;
843}
844
845
846
847$(window).unload(function(){
848 var path = getBaseUri(location.pathname);
849 if (path.indexOf("/reference/") != -1) {
850 writeCookie("lastpage", path, "reference", null);
851 } else if (path.indexOf("/guide/") != -1) {
852 writeCookie("lastpage", path, "guide", null);
853 } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) {
854 writeCookie("lastpage", path, "resources", null);
855 }
856});
857
858*/
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873function toggle(obj, slide) {
874 var ul = $("ul:first", obj);
875 var li = ul.parent();
876 if (li.hasClass("closed")) {
877 if (slide) {
878 ul.slideDown("fast");
879 } else {
880 ul.show();
881 }
882 li.removeClass("closed");
883 li.addClass("open");
884 $(".toggle-img", li).attr("title", "hide pages");
885 } else {
886 ul.slideUp("fast");
887 li.removeClass("open");
888 li.addClass("closed");
889 $(".toggle-img", li).attr("title", "show pages");
890 }
891}
892
893
894
895
896
897function buildToggleLists() {
898 $(".toggle-list").each(
899 function(i) {
900 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
901 $(this).addClass("closed");
902 });
903}
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936/* REFERENCE NAV SWAP */
937
938
939function getNavPref() {
940 var v = readCookie('reference_nav');
941 if (v != NAV_PREF_TREE) {
942 v = NAV_PREF_PANELS;
943 }
944 return v;
945}
946
947function chooseDefaultNav() {
948 nav_pref = getNavPref();
949 if (nav_pref == NAV_PREF_TREE) {
950 $("#nav-panels").toggle();
951 $("#panel-link").toggle();
952 $("#nav-tree").toggle();
953 $("#tree-link").toggle();
954 }
955}
956
957function swapNav() {
958 if (nav_pref == NAV_PREF_TREE) {
959 nav_pref = NAV_PREF_PANELS;
960 } else {
961 nav_pref = NAV_PREF_TREE;
962 init_default_navtree(toRoot);
963 }
964 var date = new Date();
965 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
966 writeCookie("nav", nav_pref, "reference", date.toGMTString());
967
968 $("#nav-panels").toggle();
969 $("#panel-link").toggle();
970 $("#nav-tree").toggle();
971 $("#tree-link").toggle();
972
973 resizeNav();
974
975 // Gross nasty hack to make tree view show up upon first swap by setting height manually
976 $("#nav-tree .jspContainer:visible")
977 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'});
978 // Another nasty hack to make the scrollbar appear now that we have height
979 resizeNav();
980
981 if ($("#nav-tree").is(':visible')) {
982 scrollIntoView("nav-tree");
983 } else {
984 scrollIntoView("packages-nav");
985 scrollIntoView("classes-nav");
986 }
987}
988
989
990
Scott Mainf5089842012-08-14 16:31:07 -0700991/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700992/* ########## LOCALIZATION ############ */
Scott Mainf5089842012-08-14 16:31:07 -0700993/* ############################################ */
Scott Maine4d8f1b2012-06-21 18:03:05 -0700994
995function getBaseUri(uri) {
996 var intlUrl = (uri.substring(0,6) == "/intl/");
997 if (intlUrl) {
998 base = uri.substring(uri.indexOf('intl/')+5,uri.length);
999 base = base.substring(base.indexOf('/')+1, base.length);
1000 //alert("intl, returning base url: /" + base);
1001 return ("/" + base);
1002 } else {
1003 //alert("not intl, returning uri as found.");
1004 return uri;
1005 }
1006}
1007
1008function requestAppendHL(uri) {
1009//append "?hl=<lang> to an outgoing request (such as to blog)
1010 var lang = getLangPref();
1011 if (lang) {
1012 var q = 'hl=' + lang;
1013 uri += '?' + q;
1014 window.location = uri;
1015 return false;
1016 } else {
1017 return true;
1018 }
1019}
1020
1021
Scott Maine4d8f1b2012-06-21 18:03:05 -07001022function changeNavLang(lang) {
Scott Main6eb95f12012-10-02 17:12:23 -07001023 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]");
1024 $links.each(function(i){ // for each link with a translation
1025 var $link = $(this);
1026 if (lang != "en") { // No need to worry about English, because a language change invokes new request
1027 // put the desired language from the attribute as the text
1028 $link.text($link.attr(lang+"-lang"))
Scott Maine4d8f1b2012-06-21 18:03:05 -07001029 }
Scott Main6eb95f12012-10-02 17:12:23 -07001030 });
Scott Maine4d8f1b2012-06-21 18:03:05 -07001031}
1032
1033function changeDocLang(lang) {
Scott Maine4d8f1b2012-06-21 18:03:05 -07001034 changeNavLang(lang);
1035}
1036
1037function changeLangPref(lang, refresh) {
1038 var date = new Date();
1039 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
1040 // keep this for 50 years
1041 //alert("expires: " + expires)
1042 writeCookie("pref_lang", lang, null, expires);
1043 changeDocLang(lang);
1044 if (refresh) {
1045 l = getBaseUri(location.pathname);
1046 window.location = l;
1047 }
1048}
1049
1050function loadLangPref() {
1051 var lang = readCookie("pref_lang");
1052 if (lang != 0) {
1053 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1054 }
1055}
1056
1057function getLangPref() {
1058 var lang = $("#language").find(":selected").attr("value");
1059 if (!lang) {
1060 lang = readCookie("pref_lang");
1061 }
1062 return (lang != 0) ? lang : 'en';
1063}
1064
1065/* ########## END LOCALIZATION ############ */
1066
1067
1068
1069
1070
1071
1072/* Used to hide and reveal supplemental content, such as long code samples.
1073 See the companion CSS in android-developer-docs.css */
1074function toggleContent(obj) {
1075 var div = $(obj.parentNode.parentNode);
1076 var toggleMe = $(".toggle-content-toggleme",div);
1077 if (div.hasClass("closed")) { // if it's closed, open it
1078 toggleMe.slideDown();
1079 $(".toggle-content-text", obj).toggle();
1080 div.removeClass("closed").addClass("open");
1081 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot
1082 + "assets/images/triangle-opened.png");
1083 } else { // if it's open, close it
1084 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
1085 $(".toggle-content-text", obj).toggle();
1086 div.removeClass("open").addClass("closed");
1087 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
1088 + "assets/images/triangle-closed.png");
1089 });
1090 }
1091 return false;
1092}
Scott Mainf5089842012-08-14 16:31:07 -07001093
1094
Scott Maindb3678b2012-10-23 14:13:41 -07001095/* New version of expandable content */
1096function toggleExpandable(link,id) {
1097 if($(id).is(':visible')) {
1098 $(id).slideUp();
1099 $(link).removeClass('expanded');
1100 } else {
1101 $(id).slideDown();
1102 $(link).addClass('expanded');
1103 }
1104}
1105
1106function hideExpandable(ids) {
1107 $(ids).slideUp();
Scott Main55d99832012-11-12 23:03:59 -08001108 $(ids).prev('h4').find('a.expandable').removeClass('expanded');
Scott Maindb3678b2012-10-23 14:13:41 -07001109}
1110
Scott Mainf5089842012-08-14 16:31:07 -07001111
1112
1113
1114
1115/*
1116 * Slideshow 1.0
1117 * Used on /index.html and /develop/index.html for carousel
1118 *
1119 * Sample usage:
1120 * HTML -
1121 * <div class="slideshow-container">
1122 * <a href="" class="slideshow-prev">Prev</a>
1123 * <a href="" class="slideshow-next">Next</a>
1124 * <ul>
1125 * <li class="item"><img src="images/marquee1.jpg"></li>
1126 * <li class="item"><img src="images/marquee2.jpg"></li>
1127 * <li class="item"><img src="images/marquee3.jpg"></li>
1128 * <li class="item"><img src="images/marquee4.jpg"></li>
1129 * </ul>
1130 * </div>
1131 *
1132 * <script type="text/javascript">
1133 * $('.slideshow-container').dacSlideshow({
1134 * auto: true,
1135 * btnPrev: '.slideshow-prev',
1136 * btnNext: '.slideshow-next'
1137 * });
1138 * </script>
1139 *
1140 * Options:
1141 * btnPrev: optional identifier for previous button
1142 * btnNext: optional identifier for next button
1143 * auto: whether or not to auto-proceed
1144 * speed: animation speed
1145 * autoTime: time between auto-rotation
1146 * easing: easing function for transition
1147 * start: item to select by default
1148 * scroll: direction to scroll in
1149 * pagination: whether or not to include dotted pagination
1150 *
1151 */
1152
1153 (function($) {
1154 $.fn.dacSlideshow = function(o) {
1155
1156 //Options - see above
1157 o = $.extend({
1158 btnPrev: null,
1159 btnNext: null,
1160 auto: true,
1161 speed: 500,
1162 autoTime: 12000,
1163 easing: null,
1164 start: 0,
1165 scroll: 1,
1166 pagination: true
1167
1168 }, o || {});
1169
1170 //Set up a carousel for each
1171 return this.each(function() {
1172
1173 var running = false;
1174 var animCss = o.vertical ? "top" : "left";
1175 var sizeCss = o.vertical ? "height" : "width";
1176 var div = $(this);
1177 var ul = $("ul", div);
1178 var tLi = $("li", ul);
1179 var tl = tLi.size();
1180 var timer = null;
1181
1182 var li = $("li", ul);
1183 var itemLength = li.size();
1184 var curr = o.start;
1185
1186 li.css({float: o.vertical ? "none" : "left"});
1187 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1188 div.css({position: "relative", "z-index": "2", left: "0px"});
1189
1190 var liSize = o.vertical ? height(li) : width(li);
1191 var ulSize = liSize * itemLength;
1192 var divSize = liSize;
1193
1194 li.css({width: li.width(), height: li.height()});
1195 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1196
1197 div.css(sizeCss, divSize+"px");
1198
1199 //Pagination
1200 if (o.pagination) {
1201 var pagination = $("<div class='pagination'></div>");
1202 var pag_ul = $("<ul></ul>");
1203 if (tl > 1) {
1204 for (var i=0;i<tl;i++) {
1205 var li = $("<li>"+i+"</li>");
1206 pag_ul.append(li);
1207 if (i==o.start) li.addClass('active');
1208 li.click(function() {
1209 go(parseInt($(this).text()));
1210 })
1211 }
1212 pagination.append(pag_ul);
1213 div.append(pagination);
1214 }
1215 }
1216
1217 //Previous button
1218 if(o.btnPrev)
1219 $(o.btnPrev).click(function(e) {
1220 e.preventDefault();
1221 return go(curr-o.scroll);
1222 });
1223
1224 //Next button
1225 if(o.btnNext)
1226 $(o.btnNext).click(function(e) {
1227 e.preventDefault();
1228 return go(curr+o.scroll);
1229 });
1230
1231 //Auto rotation
1232 if(o.auto) startRotateTimer();
1233
1234 function startRotateTimer() {
1235 clearInterval(timer);
1236 timer = setInterval(function() {
1237 if (curr == tl-1) {
1238 go(0);
1239 } else {
1240 go(curr+o.scroll);
1241 }
1242 }, o.autoTime);
1243 }
1244
1245 //Go to an item
1246 function go(to) {
1247 if(!running) {
1248
1249 if(to<0) {
1250 to = itemLength-1;
1251 } else if (to>itemLength-1) {
1252 to = 0;
1253 }
1254 curr = to;
1255
1256 running = true;
1257
1258 ul.animate(
1259 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1260 function() {
1261 running = false;
1262 }
1263 );
1264
1265 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1266 $( (curr-o.scroll<0 && o.btnPrev)
1267 ||
1268 (curr+o.scroll > itemLength && o.btnNext)
1269 ||
1270 []
1271 ).addClass("disabled");
1272
1273
1274 var nav_items = $('li', pagination);
1275 nav_items.removeClass('active');
1276 nav_items.eq(to).addClass('active');
1277
1278
1279 }
1280 if(o.auto) startRotateTimer();
1281 return false;
1282 };
1283 });
1284 };
1285
1286 function css(el, prop) {
1287 return parseInt($.css(el[0], prop)) || 0;
1288 };
1289 function width(el) {
1290 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1291 };
1292 function height(el) {
1293 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1294 };
1295
1296 })(jQuery);
1297
1298
1299/*
1300 * dacSlideshow 1.0
1301 * Used on develop/index.html for side-sliding tabs
1302 *
1303 * Sample usage:
1304 * HTML -
1305 * <div class="slideshow-container">
1306 * <a href="" class="slideshow-prev">Prev</a>
1307 * <a href="" class="slideshow-next">Next</a>
1308 * <ul>
1309 * <li class="item"><img src="images/marquee1.jpg"></li>
1310 * <li class="item"><img src="images/marquee2.jpg"></li>
1311 * <li class="item"><img src="images/marquee3.jpg"></li>
1312 * <li class="item"><img src="images/marquee4.jpg"></li>
1313 * </ul>
1314 * </div>
1315 *
1316 * <script type="text/javascript">
1317 * $('.slideshow-container').dacSlideshow({
1318 * auto: true,
1319 * btnPrev: '.slideshow-prev',
1320 * btnNext: '.slideshow-next'
1321 * });
1322 * </script>
1323 *
1324 * Options:
1325 * btnPrev: optional identifier for previous button
1326 * btnNext: optional identifier for next button
1327 * auto: whether or not to auto-proceed
1328 * speed: animation speed
1329 * autoTime: time between auto-rotation
1330 * easing: easing function for transition
1331 * start: item to select by default
1332 * scroll: direction to scroll in
1333 * pagination: whether or not to include dotted pagination
1334 *
1335 */
1336 (function($) {
1337 $.fn.dacTabbedList = function(o) {
1338
1339 //Options - see above
1340 o = $.extend({
1341 speed : 250,
1342 easing: null,
1343 nav_id: null,
1344 frame_id: null
1345 }, o || {});
1346
1347 //Set up a carousel for each
1348 return this.each(function() {
1349
1350 var curr = 0;
1351 var running = false;
1352 var animCss = "margin-left";
1353 var sizeCss = "width";
1354 var div = $(this);
1355
1356 var nav = $(o.nav_id, div);
1357 var nav_li = $("li", nav);
1358 var nav_size = nav_li.size();
1359 var frame = div.find(o.frame_id);
1360 var content_width = $(frame).find('ul').width();
1361 //Buttons
1362 $(nav_li).click(function(e) {
1363 go($(nav_li).index($(this)));
1364 })
1365
1366 //Go to an item
1367 function go(to) {
1368 if(!running) {
1369 curr = to;
1370 running = true;
1371
1372 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1373 function() {
1374 running = false;
1375 }
1376 );
1377
1378
1379 nav_li.removeClass('active');
1380 nav_li.eq(to).addClass('active');
1381
1382
1383 }
1384 return false;
1385 };
1386 });
1387 };
1388
1389 function css(el, prop) {
1390 return parseInt($.css(el[0], prop)) || 0;
1391 };
1392 function width(el) {
1393 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1394 };
1395 function height(el) {
1396 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1397 };
1398
1399 })(jQuery);
1400
1401
1402
1403
1404
1405/* ######################################################## */
1406/* ################ SEARCH SUGGESTIONS ################## */
1407/* ######################################################## */
1408
1409
1410var gSelectedIndex = -1;
1411var gSelectedID = -1;
1412var gMatches = new Array();
1413var gLastText = "";
1414var ROW_COUNT = 20;
1415var gInitialized = false;
1416
1417function set_item_selected($li, selected)
1418{
1419 if (selected) {
1420 $li.attr('class','jd-autocomplete jd-selected');
1421 } else {
1422 $li.attr('class','jd-autocomplete');
1423 }
1424}
1425
1426function set_item_values(toroot, $li, match)
1427{
1428 var $link = $('a',$li);
1429 $link.html(match.__hilabel || match.label);
1430 $link.attr('href',toroot + match.link);
1431}
1432
1433function sync_selection_table(toroot)
1434{
1435 var $list = $("#search_filtered");
1436 var $li; //list item jquery object
1437 var i; //list item iterator
1438 gSelectedID = -1;
1439
1440 //initialize the table; draw it for the first time (but not visible).
1441 if (!gInitialized) {
1442 for (i=0; i<ROW_COUNT; i++) {
1443 var $li = $("<li class='jd-autocomplete'></li>");
1444 $list.append($li);
1445
1446 $li.mousedown(function() {
1447 window.location = this.firstChild.getAttribute("href");
1448 });
1449 $li.mouseover(function() {
1450 $('#search_filtered li').removeClass('jd-selected');
1451 $(this).addClass('jd-selected');
1452 gSelectedIndex = $('#search_filtered li').index(this);
1453 });
1454 $li.append('<a></a>');
1455 }
1456 gInitialized = true;
1457 }
1458
1459 //if we have results, make the table visible and initialize result info
1460 if (gMatches.length > 0) {
1461 $('#search_filtered_div').removeClass('no-display');
1462 var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
1463 for (i=0; i<N; i++) {
1464 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1465 $li.attr('class','show-item');
1466 set_item_values(toroot, $li, gMatches[i]);
1467 set_item_selected($li, i == gSelectedIndex);
1468 if (i == gSelectedIndex) {
1469 gSelectedID = gMatches[i].id;
1470 }
1471 }
1472 //start hiding rows that are no longer matches
1473 for (; i<ROW_COUNT; i++) {
1474 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1475 $li.attr('class','no-display');
1476 }
1477 //if there are more results we're not showing, so say so.
1478/* if (gMatches.length > ROW_COUNT) {
1479 li = list.rows[ROW_COUNT];
1480 li.className = "show-item";
1481 c1 = li.cells[0];
1482 c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more";
1483 } else {
1484 list.rows[ROW_COUNT].className = "hide-item";
1485 }*/
1486 //if we have no results, hide the table
1487 } else {
1488 $('#search_filtered_div').addClass('no-display');
1489 }
1490}
1491
1492function search_changed(e, kd, toroot)
1493{
1494 var search = document.getElementById("search_autocomplete");
1495 var text = search.value.replace(/(^ +)|( +$)/g, '');
1496
1497 // show/hide the close button
1498 if (text != '') {
1499 $(".search .close").removeClass("hide");
1500 } else {
1501 $(".search .close").addClass("hide");
1502 }
1503
1504 // 13 = enter
1505 if (e.keyCode == 13) {
1506 $('#search_filtered_div').addClass('no-display');
1507 if (!$('#search_filtered_div').hasClass('no-display') || (gSelectedIndex < 0)) {
1508 if ($("#searchResults").is(":hidden")) {
1509 // if results aren't showing, return true to allow search to execute
1510 return true;
1511 } else {
1512 // otherwise, results are already showing, so allow ajax to auto refresh the results
1513 // and ignore this Enter press to avoid the reload.
1514 return false;
1515 }
1516 } else if (kd && gSelectedIndex >= 0) {
1517 window.location = toroot + gMatches[gSelectedIndex].link;
1518 return false;
1519 }
1520 }
1521 // 38 -- arrow up
1522 else if (kd && (e.keyCode == 38)) {
1523 if (gSelectedIndex >= 0) {
1524 $('#search_filtered li').removeClass('jd-selected');
1525 gSelectedIndex--;
1526 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1527 }
1528 return false;
1529 }
1530 // 40 -- arrow down
1531 else if (kd && (e.keyCode == 40)) {
1532 if (gSelectedIndex < gMatches.length-1
1533 && gSelectedIndex < ROW_COUNT-1) {
1534 $('#search_filtered li').removeClass('jd-selected');
1535 gSelectedIndex++;
1536 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1537 }
1538 return false;
1539 }
1540 else if (!kd && (e.keyCode != 40) && (e.keyCode != 38)) {
1541 gMatches = new Array();
1542 matchedCount = 0;
1543 gSelectedIndex = -1;
1544 for (var i=0; i<DATA.length; i++) {
1545 var s = DATA[i];
1546 if (text.length != 0 &&
1547 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1548 gMatches[matchedCount] = s;
1549 matchedCount++;
1550 }
1551 }
1552 rank_autocomplete_results(text);
1553 for (var i=0; i<gMatches.length; i++) {
1554 var s = gMatches[i];
1555 if (gSelectedID == s.id) {
1556 gSelectedIndex = i;
1557 }
1558 }
1559 highlight_autocomplete_result_labels(text);
1560 sync_selection_table(toroot);
1561 return true; // allow the event to bubble up to the search api
1562 }
1563}
1564
1565function rank_autocomplete_results(query) {
1566 query = query || '';
1567 if (!gMatches || !gMatches.length)
1568 return;
1569
1570 // helper function that gets the last occurence index of the given regex
1571 // in the given string, or -1 if not found
1572 var _lastSearch = function(s, re) {
1573 if (s == '')
1574 return -1;
1575 var l = -1;
1576 var tmp;
1577 while ((tmp = s.search(re)) >= 0) {
1578 if (l < 0) l = 0;
1579 l += tmp;
1580 s = s.substr(tmp + 1);
1581 }
1582 return l;
1583 };
1584
1585 // helper function that counts the occurrences of a given character in
1586 // a given string
1587 var _countChar = function(s, c) {
1588 var n = 0;
1589 for (var i=0; i<s.length; i++)
1590 if (s.charAt(i) == c) ++n;
1591 return n;
1592 };
1593
1594 var queryLower = query.toLowerCase();
1595 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1596 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1597 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1598
1599 var _resultScoreFn = function(result) {
1600 // scores are calculated based on exact and prefix matches,
1601 // and then number of path separators (dots) from the last
1602 // match (i.e. favoring classes and deep package names)
1603 var score = 1.0;
1604 var labelLower = result.label.toLowerCase();
1605 var t;
1606 t = _lastSearch(labelLower, partExactAlnumRE);
1607 if (t >= 0) {
1608 // exact part match
1609 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1610 score *= 200 / (partsAfter + 1);
1611 } else {
1612 t = _lastSearch(labelLower, partPrefixAlnumRE);
1613 if (t >= 0) {
1614 // part prefix match
1615 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1616 score *= 20 / (partsAfter + 1);
1617 }
1618 }
1619
1620 return score;
1621 };
1622
1623 for (var i=0; i<gMatches.length; i++) {
1624 gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);
1625 }
1626
1627 gMatches.sort(function(a,b){
1628 var n = b.__resultScore - a.__resultScore;
1629 if (n == 0) // lexicographical sort if scores are the same
1630 n = (a.label < b.label) ? -1 : 1;
1631 return n;
1632 });
1633}
1634
1635function highlight_autocomplete_result_labels(query) {
1636 query = query || '';
1637 if (!gMatches || !gMatches.length)
1638 return;
1639
1640 var queryLower = query.toLowerCase();
1641 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
1642 var queryRE = new RegExp(
1643 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
1644 for (var i=0; i<gMatches.length; i++) {
1645 gMatches[i].__hilabel = gMatches[i].label.replace(
1646 queryRE, '<b>$1</b>');
1647 }
1648}
1649
1650function search_focus_changed(obj, focused)
1651{
1652 if (!focused) {
1653 if(obj.value == ""){
1654 $(".search .close").addClass("hide");
1655 }
1656 document.getElementById("search_filtered_div").className = "no-display";
1657 }
1658}
1659
1660function submit_search() {
1661 var query = document.getElementById('search_autocomplete').value;
1662 location.hash = 'q=' + query;
1663 loadSearchResults();
1664 $("#searchResults").slideDown('slow');
1665 return false;
1666}
1667
1668
1669function hideResults() {
1670 $("#searchResults").slideUp();
1671 $(".search .close").addClass("hide");
1672 location.hash = '';
1673
1674 $("#search_autocomplete").val("").blur();
1675
1676 // reset the ajax search callback to nothing, so results don't appear unless ENTER
1677 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
1678 return false;
1679}
1680
1681
1682
1683/* ########################################################## */
1684/* ################ CUSTOM SEARCH ENGINE ################## */
1685/* ########################################################## */
1686
1687google.load('search', '1');
1688var searchControl;
1689
1690function loadSearchResults() {
1691 document.getElementById("search_autocomplete").style.color = "#000";
1692
1693 // create search control
1694 searchControl = new google.search.SearchControl();
1695
1696 // use our existing search form and use tabs when multiple searchers are used
1697 drawOptions = new google.search.DrawOptions();
1698 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
1699 drawOptions.setInput(document.getElementById("search_autocomplete"));
1700
1701 // configure search result options
1702 searchOptions = new google.search.SearcherOptions();
1703 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
1704
1705 // configure each of the searchers, for each tab
1706 devSiteSearcher = new google.search.WebSearch();
1707 devSiteSearcher.setUserDefinedLabel("All");
1708 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
1709
1710 designSearcher = new google.search.WebSearch();
1711 designSearcher.setUserDefinedLabel("Design");
1712 designSearcher.setSiteRestriction("http://developer.android.com/design/");
1713
1714 trainingSearcher = new google.search.WebSearch();
1715 trainingSearcher.setUserDefinedLabel("Training");
1716 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
1717
1718 guidesSearcher = new google.search.WebSearch();
1719 guidesSearcher.setUserDefinedLabel("Guides");
1720 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
1721
1722 referenceSearcher = new google.search.WebSearch();
1723 referenceSearcher.setUserDefinedLabel("Reference");
1724 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
1725
1726 blogSearcher = new google.search.WebSearch();
1727 blogSearcher.setUserDefinedLabel("Blog");
1728 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
1729
1730 // add each searcher to the search control
1731 searchControl.addSearcher(devSiteSearcher, searchOptions);
1732 searchControl.addSearcher(designSearcher, searchOptions);
1733 searchControl.addSearcher(trainingSearcher, searchOptions);
1734 searchControl.addSearcher(guidesSearcher, searchOptions);
1735 searchControl.addSearcher(referenceSearcher, searchOptions);
1736 searchControl.addSearcher(blogSearcher, searchOptions);
1737
1738 // configure result options
1739 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
1740 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
1741 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
1742 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
1743
1744 // upon ajax search, refresh the url and search title
1745 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
1746 updateResultTitle(query);
1747 var query = document.getElementById('search_autocomplete').value;
1748 location.hash = 'q=' + query;
1749 });
1750
1751 // draw the search results box
1752 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
1753
1754 // get query and execute the search
1755 searchControl.execute(decodeURI(getQuery(location.hash)));
1756
1757 document.getElementById("search_autocomplete").focus();
1758 addTabListeners();
1759}
1760// End of loadSearchResults
1761
1762
1763google.setOnLoadCallback(function(){
1764 if (location.hash.indexOf("q=") == -1) {
1765 // if there's no query in the url, don't search and make sure results are hidden
1766 $('#searchResults').hide();
1767 return;
1768 } else {
1769 // first time loading search results for this page
1770 $('#searchResults').slideDown('slow');
1771 $(".search .close").removeClass("hide");
1772 loadSearchResults();
1773 }
1774}, true);
1775
1776// when an event on the browser history occurs (back, forward, load) requery hash and do search
1777$(window).hashchange( function(){
1778 // Exit if the hash isn't a search query or there's an error in the query
1779 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
1780 // If the results pane is open, close it.
1781 if (!$("#searchResults").is(":hidden")) {
1782 hideResults();
1783 }
1784 return;
1785 }
1786
1787 // Otherwise, we have a search to do
1788 var query = decodeURI(getQuery(location.hash));
1789 searchControl.execute(query);
1790 $('#searchResults').slideDown('slow');
1791 $("#search_autocomplete").focus();
1792 $(".search .close").removeClass("hide");
1793
1794 updateResultTitle(query);
1795});
1796
1797function updateResultTitle(query) {
1798 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
1799}
1800
1801// forcefully regain key-up event control (previously jacked by search api)
1802$("#search_autocomplete").keyup(function(event) {
1803 return search_changed(event, false, toRoot);
1804});
1805
1806// add event listeners to each tab so we can track the browser history
1807function addTabListeners() {
1808 var tabHeaders = $(".gsc-tabHeader");
1809 for (var i = 0; i < tabHeaders.length; i++) {
1810 $(tabHeaders[i]).attr("id",i).click(function() {
1811 /*
1812 // make a copy of the page numbers for the search left pane
1813 setTimeout(function() {
1814 // remove any residual page numbers
1815 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
1816 // move the page numbers to the left position; make a clone,
1817 // because the element is drawn to the DOM only once
1818 // and because we're going to remove it (previous line),
1819 // we need it to be available to move again as the user navigates
1820 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
1821 .clone().appendTo('#searchResults .gsc-tabsArea');
1822 }, 200);
1823 */
1824 });
1825 }
1826 setTimeout(function(){$(tabHeaders[0]).click()},200);
1827}
1828
1829
1830function getQuery(hash) {
1831 var queryParts = hash.split('=');
1832 return queryParts[1];
1833}
1834
1835/* returns the given string with all HTML brackets converted to entities
1836 TODO: move this to the site's JS library */
1837function escapeHTML(string) {
1838 return string.replace(/</g,"&lt;")
1839 .replace(/>/g,"&gt;");
1840}
1841
1842
1843
1844
1845
1846
1847
1848/* ######################################################## */
1849/* ################# JAVADOC REFERENCE ################### */
1850/* ######################################################## */
1851
Scott Main65511c02012-09-07 15:51:32 -07001852/* Initialize some droiddoc stuff, but only if we're in the reference */
1853if (location.pathname.indexOf("/reference") == 0) {
1854 $(document).ready(function() {
1855 // init available apis based on user pref
1856 changeApiLevel();
1857 initSidenavHeightResize()
1858 });
1859}
Scott Mainf5089842012-08-14 16:31:07 -07001860
1861var API_LEVEL_COOKIE = "api_level";
1862var minLevel = 1;
1863var maxLevel = 1;
1864
1865/******* SIDENAV DIMENSIONS ************/
1866
1867 function initSidenavHeightResize() {
1868 // Change the drag bar size to nicely fit the scrollbar positions
1869 var $dragBar = $(".ui-resizable-s");
1870 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1871
1872 $( "#resize-packages-nav" ).resizable({
1873 containment: "#nav-panels",
1874 handles: "s",
1875 alsoResize: "#packages-nav",
1876 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1877 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
1878 });
1879
1880 }
1881
1882function updateSidenavFixedWidth() {
1883 if (!navBarIsFixed) return;
1884 $('#devdoc-nav').css({
1885 'width' : $('#side-nav').css('width'),
1886 'margin' : $('#side-nav').css('margin')
1887 });
1888 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1889
1890 initSidenavHeightResize();
1891}
1892
1893function updateSidenavFullscreenWidth() {
1894 if (!navBarIsFixed) return;
1895 $('#devdoc-nav').css({
1896 'width' : $('#side-nav').css('width'),
1897 'margin' : $('#side-nav').css('margin')
1898 });
1899 $('#devdoc-nav .totop').css({'left': 'inherit'});
1900
1901 initSidenavHeightResize();
1902}
1903
1904function buildApiLevelSelector() {
1905 maxLevel = SINCE_DATA.length;
1906 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1907 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1908
1909 minLevel = parseInt($("#doc-api-level").attr("class"));
1910 // Handle provisional api levels; the provisional level will always be the highest possible level
1911 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1912 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1913 if (isNaN(minLevel) && minLevel.length) {
1914 minLevel = maxLevel;
1915 }
1916 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1917 for (var i = maxLevel-1; i >= 0; i--) {
1918 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1919 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1920 select.append(option);
1921 }
1922
1923 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1924 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1925 selectedLevelItem.setAttribute('selected',true);
1926}
1927
1928function changeApiLevel() {
1929 maxLevel = SINCE_DATA.length;
1930 var selectedLevel = maxLevel;
1931
1932 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1933 toggleVisisbleApis(selectedLevel, "body");
1934
1935 var date = new Date();
1936 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1937 var expiration = date.toGMTString();
1938 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1939
1940 if (selectedLevel < minLevel) {
1941 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
Scott Main8f24ca82012-11-16 10:34:22 -08001942 $("#naMessage").show().html("<div><p><strong>This " + thing
1943 + " requires API level " + minLevel + " or higher.</strong></p>"
1944 + "<p>This document is hidden because your selected API level for the documentation is "
1945 + selectedLevel + ". You can change the documentation API level with the selector "
1946 + "above the left navigation.</p>"
1947 + "<p>For more information about specifying the API level your app requires, "
1948 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'"
1949 + ">Supporting Different Platform Versions</a>.</p>"
1950 + "<input type='button' value='OK, make this page visible' "
1951 + "title='Change the API level to " + minLevel + "' "
1952 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />"
1953 + "</div>");
Scott Mainf5089842012-08-14 16:31:07 -07001954 } else {
1955 $("#naMessage").hide();
1956 }
1957}
1958
1959function toggleVisisbleApis(selectedLevel, context) {
1960 var apis = $(".api",context);
1961 apis.each(function(i) {
1962 var obj = $(this);
1963 var className = obj.attr("class");
1964 var apiLevelIndex = className.lastIndexOf("-")+1;
1965 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1966 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1967 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
1968 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
1969 return;
1970 }
1971 apiLevel = parseInt(apiLevel);
1972
1973 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
1974 var selectedLevelNum = parseInt(selectedLevel)
1975 var apiLevelNum = parseInt(apiLevel);
1976 if (isNaN(apiLevelNum)) {
1977 apiLevelNum = maxLevel;
1978 }
1979
1980 // Grey things out that aren't available and give a tooltip title
1981 if (apiLevelNum > selectedLevelNum) {
1982 obj.addClass("absent").attr("title","Requires API Level \""
1983 + apiLevel + "\" or higher");
1984 }
1985 else obj.removeClass("absent").removeAttr("title");
1986 });
1987}
1988
1989
1990
1991
1992/* ################# SIDENAV TREE VIEW ################### */
1993
1994function new_node(me, mom, text, link, children_data, api_level)
1995{
1996 var node = new Object();
1997 node.children = Array();
1998 node.children_data = children_data;
1999 node.depth = mom.depth + 1;
2000
2001 node.li = document.createElement("li");
2002 mom.get_children_ul().appendChild(node.li);
2003
2004 node.label_div = document.createElement("div");
2005 node.label_div.className = "label";
2006 if (api_level != null) {
2007 $(node.label_div).addClass("api");
2008 $(node.label_div).addClass("api-level-"+api_level);
2009 }
2010 node.li.appendChild(node.label_div);
2011
2012 if (children_data != null) {
2013 node.expand_toggle = document.createElement("a");
2014 node.expand_toggle.href = "javascript:void(0)";
2015 node.expand_toggle.onclick = function() {
2016 if (node.expanded) {
2017 $(node.get_children_ul()).slideUp("fast");
2018 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2019 node.expanded = false;
2020 } else {
2021 expand_node(me, node);
2022 }
2023 };
2024 node.label_div.appendChild(node.expand_toggle);
2025
2026 node.plus_img = document.createElement("img");
2027 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
2028 node.plus_img.className = "plus";
2029 node.plus_img.width = "8";
2030 node.plus_img.border = "0";
2031 node.expand_toggle.appendChild(node.plus_img);
2032
2033 node.expanded = false;
2034 }
2035
2036 var a = document.createElement("a");
2037 node.label_div.appendChild(a);
2038 node.label = document.createTextNode(text);
2039 a.appendChild(node.label);
2040 if (link) {
2041 a.href = me.toroot + link;
2042 } else {
2043 if (children_data != null) {
2044 a.className = "nolink";
2045 a.href = "javascript:void(0)";
2046 a.onclick = node.expand_toggle.onclick;
2047 // This next line shouldn't be necessary. I'll buy a beer for the first
2048 // person who figures out how to remove this line and have the link
2049 // toggle shut on the first try. --joeo@android.com
2050 node.expanded = false;
2051 }
2052 }
2053
2054
2055 node.children_ul = null;
2056 node.get_children_ul = function() {
2057 if (!node.children_ul) {
2058 node.children_ul = document.createElement("ul");
2059 node.children_ul.className = "children_ul";
2060 node.children_ul.style.display = "none";
2061 node.li.appendChild(node.children_ul);
2062 }
2063 return node.children_ul;
2064 };
2065
2066 return node;
2067}
2068
2069function expand_node(me, node)
2070{
2071 if (node.children_data && !node.expanded) {
2072 if (node.children_visited) {
2073 $(node.get_children_ul()).slideDown("fast");
2074 } else {
2075 get_node(me, node);
2076 if ($(node.label_div).hasClass("absent")) {
2077 $(node.get_children_ul()).addClass("absent");
2078 }
2079 $(node.get_children_ul()).slideDown("fast");
2080 }
2081 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2082 node.expanded = true;
2083
2084 // perform api level toggling because new nodes are new to the DOM
2085 var selectedLevel = $("#apiLevelSelector option:selected").val();
2086 toggleVisisbleApis(selectedLevel, "#side-nav");
2087 }
2088}
2089
2090function get_node(me, mom)
2091{
2092 mom.children_visited = true;
2093 for (var i in mom.children_data) {
2094 var node_data = mom.children_data[i];
2095 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2096 node_data[2], node_data[3]);
2097 }
2098}
2099
2100function this_page_relative(toroot)
2101{
2102 var full = document.location.pathname;
2103 var file = "";
2104 if (toroot.substr(0, 1) == "/") {
2105 if (full.substr(0, toroot.length) == toroot) {
2106 return full.substr(toroot.length);
2107 } else {
2108 // the file isn't under toroot. Fail.
2109 return null;
2110 }
2111 } else {
2112 if (toroot != "./") {
2113 toroot = "./" + toroot;
2114 }
2115 do {
2116 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2117 var pos = full.lastIndexOf("/");
2118 file = full.substr(pos) + file;
2119 full = full.substr(0, pos);
2120 toroot = toroot.substr(0, toroot.length-3);
2121 }
2122 } while (toroot != "" && toroot != "/");
2123 return file.substr(1);
2124 }
2125}
2126
2127function find_page(url, data)
2128{
2129 var nodes = data;
2130 var result = null;
2131 for (var i in nodes) {
2132 var d = nodes[i];
2133 if (d[1] == url) {
2134 return new Array(i);
2135 }
2136 else if (d[2] != null) {
2137 result = find_page(url, d[2]);
2138 if (result != null) {
2139 return (new Array(i).concat(result));
2140 }
2141 }
2142 }
2143 return null;
2144}
2145
2146function load_navtree_data(toroot) {
2147 var navtreeData = document.createElement("script");
2148 navtreeData.setAttribute("type","text/javascript");
2149 navtreeData.setAttribute("src", toroot+"navtree_data.js");
2150 $("head").append($(navtreeData));
2151}
2152
2153function init_default_navtree(toroot) {
2154 init_navtree("tree-list", toroot, NAVTREE_DATA);
2155
2156 // perform api level toggling because because the whole tree is new to the DOM
2157 var selectedLevel = $("#apiLevelSelector option:selected").val();
2158 toggleVisisbleApis(selectedLevel, "#side-nav");
2159}
2160
2161function init_navtree(navtree_id, toroot, root_nodes)
2162{
2163 var me = new Object();
2164 me.toroot = toroot;
2165 me.node = new Object();
2166
2167 me.node.li = document.getElementById(navtree_id);
2168 me.node.children_data = root_nodes;
2169 me.node.children = new Array();
2170 me.node.children_ul = document.createElement("ul");
2171 me.node.get_children_ul = function() { return me.node.children_ul; };
2172 //me.node.children_ul.className = "children_ul";
2173 me.node.li.appendChild(me.node.children_ul);
2174 me.node.depth = 0;
2175
2176 get_node(me, me.node);
2177
2178 me.this_page = this_page_relative(toroot);
2179 me.breadcrumbs = find_page(me.this_page, root_nodes);
2180 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2181 var mom = me.node;
2182 for (var i in me.breadcrumbs) {
2183 var j = me.breadcrumbs[i];
2184 mom = mom.children[j];
2185 expand_node(me, mom);
2186 }
2187 mom.label_div.className = mom.label_div.className + " selected";
2188 addLoadEvent(function() {
2189 scrollIntoView("nav-tree");
2190 });
2191 }
2192}
2193
2194/* TOGGLE INHERITED MEMBERS */
2195
2196/* Toggle an inherited class (arrow toggle)
2197 * @param linkObj The link that was clicked.
2198 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2199 * 'null' to simply toggle.
2200 */
2201function toggleInherited(linkObj, expand) {
2202 var base = linkObj.getAttribute("id");
2203 var list = document.getElementById(base + "-list");
2204 var summary = document.getElementById(base + "-summary");
2205 var trigger = document.getElementById(base + "-trigger");
2206 var a = $(linkObj);
2207 if ( (expand == null && a.hasClass("closed")) || expand ) {
2208 list.style.display = "none";
2209 summary.style.display = "block";
2210 trigger.src = toRoot + "assets/images/triangle-opened.png";
2211 a.removeClass("closed");
2212 a.addClass("opened");
2213 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2214 list.style.display = "block";
2215 summary.style.display = "none";
2216 trigger.src = toRoot + "assets/images/triangle-closed.png";
2217 a.removeClass("opened");
2218 a.addClass("closed");
2219 }
2220 return false;
2221}
2222
2223/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2224 * @param linkObj The link that was clicked.
2225 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2226 * 'null' to simply toggle.
2227 */
2228function toggleAllInherited(linkObj, expand) {
2229 var a = $(linkObj);
2230 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2231 var expandos = $(".jd-expando-trigger", table);
2232 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2233 expandos.each(function(i) {
2234 toggleInherited(this, true);
2235 });
2236 a.text("[Collapse]");
2237 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2238 expandos.each(function(i) {
2239 toggleInherited(this, false);
2240 });
2241 a.text("[Expand]");
2242 }
2243 return false;
2244}
2245
2246/* Toggle all inherited members in the class (link in the class title)
2247 */
2248function toggleAllClassInherited() {
2249 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2250 var toggles = $(".toggle-all", $("#body-content"));
2251 if (a.text() == "[Expand All]") {
2252 toggles.each(function(i) {
2253 toggleAllInherited(this, true);
2254 });
2255 a.text("[Collapse All]");
2256 } else {
2257 toggles.each(function(i) {
2258 toggleAllInherited(this, false);
2259 });
2260 a.text("[Expand All]");
2261 }
2262 return false;
2263}
2264
2265/* Expand all inherited members in the class. Used when initiating page search */
2266function ensureAllInheritedExpanded() {
2267 var toggles = $(".toggle-all", $("#body-content"));
2268 toggles.each(function(i) {
2269 toggleAllInherited(this, true);
2270 });
2271 $("#toggleAllClassInherited").text("[Collapse All]");
2272}
2273
2274
2275/* HANDLE KEY EVENTS
2276 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2277 */
2278var agent = navigator['userAgent'].toLowerCase();
2279var mac = agent.indexOf("macintosh") != -1;
2280
2281$(document).keydown( function(e) {
2282var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2283 if (control && e.which == 70) { // 70 is "F"
2284 ensureAllInheritedExpanded();
2285 }
2286});
2287
2288
2289