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