blob: 6ce7c56eb4bd0dd88c5d83ec6a67573e3644b1d0 [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)
31 $('h2').not('#qv h2').not('#tb h2').not('#devdoc-nav h2').css({marginBottom:0}).after('<hr/>');
32
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;
387 $('acronym').each(function() {
388 var $target = $(this);
389 var $tooltip = $('<div>')
390 .addClass('tooltip-box')
391 .text($target.attr('title'))
392 .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
982function changeTabLang(lang) {
983 var nodes = $("#header-tabs").find("."+lang);
984 for (i=0; i < nodes.length; i++) { // for each node in this language
985 var node = $(nodes[i]);
986 node.siblings().css("display","none"); // hide all siblings
987 if (node.not(":empty").length != 0) { //if this languages node has a translation, show it
988 node.css("display","inline");
989 } else { //otherwise, show English instead
990 node.css("display","none");
991 node.siblings().filter(".en").css("display","inline");
992 }
993 }
994}
995
996function changeNavLang(lang) {
997 var nodes = $("#devdoc-nav").find("."+lang);
998 for (i=0; i < nodes.length; i++) { // for each node in this language
999 var node = $(nodes[i]);
1000 node.siblings().css("display","none"); // hide all siblings
1001 if (node.not(":empty").length != 0) { // if this languages node has a translation, show it
1002 node.css("display","inline");
1003 } else { // otherwise, show English instead
1004 node.css("display","none");
1005 node.siblings().filter(".en").css("display","inline");
1006 }
1007 }
1008}
1009
1010function changeDocLang(lang) {
1011 changeTabLang(lang);
1012 changeNavLang(lang);
1013}
1014
1015function changeLangPref(lang, refresh) {
1016 var date = new Date();
1017 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000)));
1018 // keep this for 50 years
1019 //alert("expires: " + expires)
1020 writeCookie("pref_lang", lang, null, expires);
1021 changeDocLang(lang);
1022 if (refresh) {
1023 l = getBaseUri(location.pathname);
1024 window.location = l;
1025 }
1026}
1027
1028function loadLangPref() {
1029 var lang = readCookie("pref_lang");
1030 if (lang != 0) {
1031 $("#language").find("option[value='"+lang+"']").attr("selected",true);
1032 }
1033}
1034
1035function getLangPref() {
1036 var lang = $("#language").find(":selected").attr("value");
1037 if (!lang) {
1038 lang = readCookie("pref_lang");
1039 }
1040 return (lang != 0) ? lang : 'en';
1041}
1042
1043/* ########## END LOCALIZATION ############ */
1044
1045
1046
1047
1048
1049
1050/* Used to hide and reveal supplemental content, such as long code samples.
1051 See the companion CSS in android-developer-docs.css */
1052function toggleContent(obj) {
1053 var div = $(obj.parentNode.parentNode);
1054 var toggleMe = $(".toggle-content-toggleme",div);
1055 if (div.hasClass("closed")) { // if it's closed, open it
1056 toggleMe.slideDown();
1057 $(".toggle-content-text", obj).toggle();
1058 div.removeClass("closed").addClass("open");
1059 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot
1060 + "assets/images/triangle-opened.png");
1061 } else { // if it's open, close it
1062 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow
1063 $(".toggle-content-text", obj).toggle();
1064 div.removeClass("open").addClass("closed");
1065 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot
1066 + "assets/images/triangle-closed.png");
1067 });
1068 }
1069 return false;
1070}
Scott Mainf5089842012-08-14 16:31:07 -07001071
1072
1073
1074
1075
1076
1077/*
1078 * Slideshow 1.0
1079 * Used on /index.html and /develop/index.html for carousel
1080 *
1081 * Sample usage:
1082 * HTML -
1083 * <div class="slideshow-container">
1084 * <a href="" class="slideshow-prev">Prev</a>
1085 * <a href="" class="slideshow-next">Next</a>
1086 * <ul>
1087 * <li class="item"><img src="images/marquee1.jpg"></li>
1088 * <li class="item"><img src="images/marquee2.jpg"></li>
1089 * <li class="item"><img src="images/marquee3.jpg"></li>
1090 * <li class="item"><img src="images/marquee4.jpg"></li>
1091 * </ul>
1092 * </div>
1093 *
1094 * <script type="text/javascript">
1095 * $('.slideshow-container').dacSlideshow({
1096 * auto: true,
1097 * btnPrev: '.slideshow-prev',
1098 * btnNext: '.slideshow-next'
1099 * });
1100 * </script>
1101 *
1102 * Options:
1103 * btnPrev: optional identifier for previous button
1104 * btnNext: optional identifier for next button
1105 * auto: whether or not to auto-proceed
1106 * speed: animation speed
1107 * autoTime: time between auto-rotation
1108 * easing: easing function for transition
1109 * start: item to select by default
1110 * scroll: direction to scroll in
1111 * pagination: whether or not to include dotted pagination
1112 *
1113 */
1114
1115 (function($) {
1116 $.fn.dacSlideshow = function(o) {
1117
1118 //Options - see above
1119 o = $.extend({
1120 btnPrev: null,
1121 btnNext: null,
1122 auto: true,
1123 speed: 500,
1124 autoTime: 12000,
1125 easing: null,
1126 start: 0,
1127 scroll: 1,
1128 pagination: true
1129
1130 }, o || {});
1131
1132 //Set up a carousel for each
1133 return this.each(function() {
1134
1135 var running = false;
1136 var animCss = o.vertical ? "top" : "left";
1137 var sizeCss = o.vertical ? "height" : "width";
1138 var div = $(this);
1139 var ul = $("ul", div);
1140 var tLi = $("li", ul);
1141 var tl = tLi.size();
1142 var timer = null;
1143
1144 var li = $("li", ul);
1145 var itemLength = li.size();
1146 var curr = o.start;
1147
1148 li.css({float: o.vertical ? "none" : "left"});
1149 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
1150 div.css({position: "relative", "z-index": "2", left: "0px"});
1151
1152 var liSize = o.vertical ? height(li) : width(li);
1153 var ulSize = liSize * itemLength;
1154 var divSize = liSize;
1155
1156 li.css({width: li.width(), height: li.height()});
1157 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));
1158
1159 div.css(sizeCss, divSize+"px");
1160
1161 //Pagination
1162 if (o.pagination) {
1163 var pagination = $("<div class='pagination'></div>");
1164 var pag_ul = $("<ul></ul>");
1165 if (tl > 1) {
1166 for (var i=0;i<tl;i++) {
1167 var li = $("<li>"+i+"</li>");
1168 pag_ul.append(li);
1169 if (i==o.start) li.addClass('active');
1170 li.click(function() {
1171 go(parseInt($(this).text()));
1172 })
1173 }
1174 pagination.append(pag_ul);
1175 div.append(pagination);
1176 }
1177 }
1178
1179 //Previous button
1180 if(o.btnPrev)
1181 $(o.btnPrev).click(function(e) {
1182 e.preventDefault();
1183 return go(curr-o.scroll);
1184 });
1185
1186 //Next button
1187 if(o.btnNext)
1188 $(o.btnNext).click(function(e) {
1189 e.preventDefault();
1190 return go(curr+o.scroll);
1191 });
1192
1193 //Auto rotation
1194 if(o.auto) startRotateTimer();
1195
1196 function startRotateTimer() {
1197 clearInterval(timer);
1198 timer = setInterval(function() {
1199 if (curr == tl-1) {
1200 go(0);
1201 } else {
1202 go(curr+o.scroll);
1203 }
1204 }, o.autoTime);
1205 }
1206
1207 //Go to an item
1208 function go(to) {
1209 if(!running) {
1210
1211 if(to<0) {
1212 to = itemLength-1;
1213 } else if (to>itemLength-1) {
1214 to = 0;
1215 }
1216 curr = to;
1217
1218 running = true;
1219
1220 ul.animate(
1221 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
1222 function() {
1223 running = false;
1224 }
1225 );
1226
1227 $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
1228 $( (curr-o.scroll<0 && o.btnPrev)
1229 ||
1230 (curr+o.scroll > itemLength && o.btnNext)
1231 ||
1232 []
1233 ).addClass("disabled");
1234
1235
1236 var nav_items = $('li', pagination);
1237 nav_items.removeClass('active');
1238 nav_items.eq(to).addClass('active');
1239
1240
1241 }
1242 if(o.auto) startRotateTimer();
1243 return false;
1244 };
1245 });
1246 };
1247
1248 function css(el, prop) {
1249 return parseInt($.css(el[0], prop)) || 0;
1250 };
1251 function width(el) {
1252 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1253 };
1254 function height(el) {
1255 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1256 };
1257
1258 })(jQuery);
1259
1260
1261/*
1262 * dacSlideshow 1.0
1263 * Used on develop/index.html for side-sliding tabs
1264 *
1265 * Sample usage:
1266 * HTML -
1267 * <div class="slideshow-container">
1268 * <a href="" class="slideshow-prev">Prev</a>
1269 * <a href="" class="slideshow-next">Next</a>
1270 * <ul>
1271 * <li class="item"><img src="images/marquee1.jpg"></li>
1272 * <li class="item"><img src="images/marquee2.jpg"></li>
1273 * <li class="item"><img src="images/marquee3.jpg"></li>
1274 * <li class="item"><img src="images/marquee4.jpg"></li>
1275 * </ul>
1276 * </div>
1277 *
1278 * <script type="text/javascript">
1279 * $('.slideshow-container').dacSlideshow({
1280 * auto: true,
1281 * btnPrev: '.slideshow-prev',
1282 * btnNext: '.slideshow-next'
1283 * });
1284 * </script>
1285 *
1286 * Options:
1287 * btnPrev: optional identifier for previous button
1288 * btnNext: optional identifier for next button
1289 * auto: whether or not to auto-proceed
1290 * speed: animation speed
1291 * autoTime: time between auto-rotation
1292 * easing: easing function for transition
1293 * start: item to select by default
1294 * scroll: direction to scroll in
1295 * pagination: whether or not to include dotted pagination
1296 *
1297 */
1298 (function($) {
1299 $.fn.dacTabbedList = function(o) {
1300
1301 //Options - see above
1302 o = $.extend({
1303 speed : 250,
1304 easing: null,
1305 nav_id: null,
1306 frame_id: null
1307 }, o || {});
1308
1309 //Set up a carousel for each
1310 return this.each(function() {
1311
1312 var curr = 0;
1313 var running = false;
1314 var animCss = "margin-left";
1315 var sizeCss = "width";
1316 var div = $(this);
1317
1318 var nav = $(o.nav_id, div);
1319 var nav_li = $("li", nav);
1320 var nav_size = nav_li.size();
1321 var frame = div.find(o.frame_id);
1322 var content_width = $(frame).find('ul').width();
1323 //Buttons
1324 $(nav_li).click(function(e) {
1325 go($(nav_li).index($(this)));
1326 })
1327
1328 //Go to an item
1329 function go(to) {
1330 if(!running) {
1331 curr = to;
1332 running = true;
1333
1334 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing,
1335 function() {
1336 running = false;
1337 }
1338 );
1339
1340
1341 nav_li.removeClass('active');
1342 nav_li.eq(to).addClass('active');
1343
1344
1345 }
1346 return false;
1347 };
1348 });
1349 };
1350
1351 function css(el, prop) {
1352 return parseInt($.css(el[0], prop)) || 0;
1353 };
1354 function width(el) {
1355 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
1356 };
1357 function height(el) {
1358 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
1359 };
1360
1361 })(jQuery);
1362
1363
1364
1365
1366
1367/* ######################################################## */
1368/* ################ SEARCH SUGGESTIONS ################## */
1369/* ######################################################## */
1370
1371
1372var gSelectedIndex = -1;
1373var gSelectedID = -1;
1374var gMatches = new Array();
1375var gLastText = "";
1376var ROW_COUNT = 20;
1377var gInitialized = false;
1378
1379function set_item_selected($li, selected)
1380{
1381 if (selected) {
1382 $li.attr('class','jd-autocomplete jd-selected');
1383 } else {
1384 $li.attr('class','jd-autocomplete');
1385 }
1386}
1387
1388function set_item_values(toroot, $li, match)
1389{
1390 var $link = $('a',$li);
1391 $link.html(match.__hilabel || match.label);
1392 $link.attr('href',toroot + match.link);
1393}
1394
1395function sync_selection_table(toroot)
1396{
1397 var $list = $("#search_filtered");
1398 var $li; //list item jquery object
1399 var i; //list item iterator
1400 gSelectedID = -1;
1401
1402 //initialize the table; draw it for the first time (but not visible).
1403 if (!gInitialized) {
1404 for (i=0; i<ROW_COUNT; i++) {
1405 var $li = $("<li class='jd-autocomplete'></li>");
1406 $list.append($li);
1407
1408 $li.mousedown(function() {
1409 window.location = this.firstChild.getAttribute("href");
1410 });
1411 $li.mouseover(function() {
1412 $('#search_filtered li').removeClass('jd-selected');
1413 $(this).addClass('jd-selected');
1414 gSelectedIndex = $('#search_filtered li').index(this);
1415 });
1416 $li.append('<a></a>');
1417 }
1418 gInitialized = true;
1419 }
1420
1421 //if we have results, make the table visible and initialize result info
1422 if (gMatches.length > 0) {
1423 $('#search_filtered_div').removeClass('no-display');
1424 var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
1425 for (i=0; i<N; i++) {
1426 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1427 $li.attr('class','show-item');
1428 set_item_values(toroot, $li, gMatches[i]);
1429 set_item_selected($li, i == gSelectedIndex);
1430 if (i == gSelectedIndex) {
1431 gSelectedID = gMatches[i].id;
1432 }
1433 }
1434 //start hiding rows that are no longer matches
1435 for (; i<ROW_COUNT; i++) {
1436 $li = $('#search_filtered li:nth-child('+(i+1)+')');
1437 $li.attr('class','no-display');
1438 }
1439 //if there are more results we're not showing, so say so.
1440/* if (gMatches.length > ROW_COUNT) {
1441 li = list.rows[ROW_COUNT];
1442 li.className = "show-item";
1443 c1 = li.cells[0];
1444 c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more";
1445 } else {
1446 list.rows[ROW_COUNT].className = "hide-item";
1447 }*/
1448 //if we have no results, hide the table
1449 } else {
1450 $('#search_filtered_div').addClass('no-display');
1451 }
1452}
1453
1454function search_changed(e, kd, toroot)
1455{
1456 var search = document.getElementById("search_autocomplete");
1457 var text = search.value.replace(/(^ +)|( +$)/g, '');
1458
1459 // show/hide the close button
1460 if (text != '') {
1461 $(".search .close").removeClass("hide");
1462 } else {
1463 $(".search .close").addClass("hide");
1464 }
1465
1466 // 13 = enter
1467 if (e.keyCode == 13) {
1468 $('#search_filtered_div').addClass('no-display');
1469 if (!$('#search_filtered_div').hasClass('no-display') || (gSelectedIndex < 0)) {
1470 if ($("#searchResults").is(":hidden")) {
1471 // if results aren't showing, return true to allow search to execute
1472 return true;
1473 } else {
1474 // otherwise, results are already showing, so allow ajax to auto refresh the results
1475 // and ignore this Enter press to avoid the reload.
1476 return false;
1477 }
1478 } else if (kd && gSelectedIndex >= 0) {
1479 window.location = toroot + gMatches[gSelectedIndex].link;
1480 return false;
1481 }
1482 }
1483 // 38 -- arrow up
1484 else if (kd && (e.keyCode == 38)) {
1485 if (gSelectedIndex >= 0) {
1486 $('#search_filtered li').removeClass('jd-selected');
1487 gSelectedIndex--;
1488 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1489 }
1490 return false;
1491 }
1492 // 40 -- arrow down
1493 else if (kd && (e.keyCode == 40)) {
1494 if (gSelectedIndex < gMatches.length-1
1495 && gSelectedIndex < ROW_COUNT-1) {
1496 $('#search_filtered li').removeClass('jd-selected');
1497 gSelectedIndex++;
1498 $('#search_filtered li:nth-child('+(gSelectedIndex+1)+')').addClass('jd-selected');
1499 }
1500 return false;
1501 }
1502 else if (!kd && (e.keyCode != 40) && (e.keyCode != 38)) {
1503 gMatches = new Array();
1504 matchedCount = 0;
1505 gSelectedIndex = -1;
1506 for (var i=0; i<DATA.length; i++) {
1507 var s = DATA[i];
1508 if (text.length != 0 &&
1509 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) {
1510 gMatches[matchedCount] = s;
1511 matchedCount++;
1512 }
1513 }
1514 rank_autocomplete_results(text);
1515 for (var i=0; i<gMatches.length; i++) {
1516 var s = gMatches[i];
1517 if (gSelectedID == s.id) {
1518 gSelectedIndex = i;
1519 }
1520 }
1521 highlight_autocomplete_result_labels(text);
1522 sync_selection_table(toroot);
1523 return true; // allow the event to bubble up to the search api
1524 }
1525}
1526
1527function rank_autocomplete_results(query) {
1528 query = query || '';
1529 if (!gMatches || !gMatches.length)
1530 return;
1531
1532 // helper function that gets the last occurence index of the given regex
1533 // in the given string, or -1 if not found
1534 var _lastSearch = function(s, re) {
1535 if (s == '')
1536 return -1;
1537 var l = -1;
1538 var tmp;
1539 while ((tmp = s.search(re)) >= 0) {
1540 if (l < 0) l = 0;
1541 l += tmp;
1542 s = s.substr(tmp + 1);
1543 }
1544 return l;
1545 };
1546
1547 // helper function that counts the occurrences of a given character in
1548 // a given string
1549 var _countChar = function(s, c) {
1550 var n = 0;
1551 for (var i=0; i<s.length; i++)
1552 if (s.charAt(i) == c) ++n;
1553 return n;
1554 };
1555
1556 var queryLower = query.toLowerCase();
1557 var queryAlnum = (queryLower.match(/\w+/) || [''])[0];
1558 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum);
1559 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b');
1560
1561 var _resultScoreFn = function(result) {
1562 // scores are calculated based on exact and prefix matches,
1563 // and then number of path separators (dots) from the last
1564 // match (i.e. favoring classes and deep package names)
1565 var score = 1.0;
1566 var labelLower = result.label.toLowerCase();
1567 var t;
1568 t = _lastSearch(labelLower, partExactAlnumRE);
1569 if (t >= 0) {
1570 // exact part match
1571 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1572 score *= 200 / (partsAfter + 1);
1573 } else {
1574 t = _lastSearch(labelLower, partPrefixAlnumRE);
1575 if (t >= 0) {
1576 // part prefix match
1577 var partsAfter = _countChar(labelLower.substr(t + 1), '.');
1578 score *= 20 / (partsAfter + 1);
1579 }
1580 }
1581
1582 return score;
1583 };
1584
1585 for (var i=0; i<gMatches.length; i++) {
1586 gMatches[i].__resultScore = _resultScoreFn(gMatches[i]);
1587 }
1588
1589 gMatches.sort(function(a,b){
1590 var n = b.__resultScore - a.__resultScore;
1591 if (n == 0) // lexicographical sort if scores are the same
1592 n = (a.label < b.label) ? -1 : 1;
1593 return n;
1594 });
1595}
1596
1597function highlight_autocomplete_result_labels(query) {
1598 query = query || '';
1599 if (!gMatches || !gMatches.length)
1600 return;
1601
1602 var queryLower = query.toLowerCase();
1603 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0];
1604 var queryRE = new RegExp(
1605 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig');
1606 for (var i=0; i<gMatches.length; i++) {
1607 gMatches[i].__hilabel = gMatches[i].label.replace(
1608 queryRE, '<b>$1</b>');
1609 }
1610}
1611
1612function search_focus_changed(obj, focused)
1613{
1614 if (!focused) {
1615 if(obj.value == ""){
1616 $(".search .close").addClass("hide");
1617 }
1618 document.getElementById("search_filtered_div").className = "no-display";
1619 }
1620}
1621
1622function submit_search() {
1623 var query = document.getElementById('search_autocomplete').value;
1624 location.hash = 'q=' + query;
1625 loadSearchResults();
1626 $("#searchResults").slideDown('slow');
1627 return false;
1628}
1629
1630
1631function hideResults() {
1632 $("#searchResults").slideUp();
1633 $(".search .close").addClass("hide");
1634 location.hash = '';
1635
1636 $("#search_autocomplete").val("").blur();
1637
1638 // reset the ajax search callback to nothing, so results don't appear unless ENTER
1639 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {});
1640 return false;
1641}
1642
1643
1644
1645/* ########################################################## */
1646/* ################ CUSTOM SEARCH ENGINE ################## */
1647/* ########################################################## */
1648
1649google.load('search', '1');
1650var searchControl;
1651
1652function loadSearchResults() {
1653 document.getElementById("search_autocomplete").style.color = "#000";
1654
1655 // create search control
1656 searchControl = new google.search.SearchControl();
1657
1658 // use our existing search form and use tabs when multiple searchers are used
1659 drawOptions = new google.search.DrawOptions();
1660 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
1661 drawOptions.setInput(document.getElementById("search_autocomplete"));
1662
1663 // configure search result options
1664 searchOptions = new google.search.SearcherOptions();
1665 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN);
1666
1667 // configure each of the searchers, for each tab
1668 devSiteSearcher = new google.search.WebSearch();
1669 devSiteSearcher.setUserDefinedLabel("All");
1670 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u");
1671
1672 designSearcher = new google.search.WebSearch();
1673 designSearcher.setUserDefinedLabel("Design");
1674 designSearcher.setSiteRestriction("http://developer.android.com/design/");
1675
1676 trainingSearcher = new google.search.WebSearch();
1677 trainingSearcher.setUserDefinedLabel("Training");
1678 trainingSearcher.setSiteRestriction("http://developer.android.com/training/");
1679
1680 guidesSearcher = new google.search.WebSearch();
1681 guidesSearcher.setUserDefinedLabel("Guides");
1682 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/");
1683
1684 referenceSearcher = new google.search.WebSearch();
1685 referenceSearcher.setUserDefinedLabel("Reference");
1686 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/");
1687
1688 blogSearcher = new google.search.WebSearch();
1689 blogSearcher.setUserDefinedLabel("Blog");
1690 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com");
1691
1692 // add each searcher to the search control
1693 searchControl.addSearcher(devSiteSearcher, searchOptions);
1694 searchControl.addSearcher(designSearcher, searchOptions);
1695 searchControl.addSearcher(trainingSearcher, searchOptions);
1696 searchControl.addSearcher(guidesSearcher, searchOptions);
1697 searchControl.addSearcher(referenceSearcher, searchOptions);
1698 searchControl.addSearcher(blogSearcher, searchOptions);
1699
1700 // configure result options
1701 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET);
1702 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
1703 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT);
1704 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING);
1705
1706 // upon ajax search, refresh the url and search title
1707 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {
1708 updateResultTitle(query);
1709 var query = document.getElementById('search_autocomplete').value;
1710 location.hash = 'q=' + query;
1711 });
1712
1713 // draw the search results box
1714 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions);
1715
1716 // get query and execute the search
1717 searchControl.execute(decodeURI(getQuery(location.hash)));
1718
1719 document.getElementById("search_autocomplete").focus();
1720 addTabListeners();
1721}
1722// End of loadSearchResults
1723
1724
1725google.setOnLoadCallback(function(){
1726 if (location.hash.indexOf("q=") == -1) {
1727 // if there's no query in the url, don't search and make sure results are hidden
1728 $('#searchResults').hide();
1729 return;
1730 } else {
1731 // first time loading search results for this page
1732 $('#searchResults').slideDown('slow');
1733 $(".search .close").removeClass("hide");
1734 loadSearchResults();
1735 }
1736}, true);
1737
1738// when an event on the browser history occurs (back, forward, load) requery hash and do search
1739$(window).hashchange( function(){
1740 // Exit if the hash isn't a search query or there's an error in the query
1741 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) {
1742 // If the results pane is open, close it.
1743 if (!$("#searchResults").is(":hidden")) {
1744 hideResults();
1745 }
1746 return;
1747 }
1748
1749 // Otherwise, we have a search to do
1750 var query = decodeURI(getQuery(location.hash));
1751 searchControl.execute(query);
1752 $('#searchResults').slideDown('slow');
1753 $("#search_autocomplete").focus();
1754 $(".search .close").removeClass("hide");
1755
1756 updateResultTitle(query);
1757});
1758
1759function updateResultTitle(query) {
1760 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>");
1761}
1762
1763// forcefully regain key-up event control (previously jacked by search api)
1764$("#search_autocomplete").keyup(function(event) {
1765 return search_changed(event, false, toRoot);
1766});
1767
1768// add event listeners to each tab so we can track the browser history
1769function addTabListeners() {
1770 var tabHeaders = $(".gsc-tabHeader");
1771 for (var i = 0; i < tabHeaders.length; i++) {
1772 $(tabHeaders[i]).attr("id",i).click(function() {
1773 /*
1774 // make a copy of the page numbers for the search left pane
1775 setTimeout(function() {
1776 // remove any residual page numbers
1777 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove();
1778 // move the page numbers to the left position; make a clone,
1779 // because the element is drawn to the DOM only once
1780 // and because we're going to remove it (previous line),
1781 // we need it to be available to move again as the user navigates
1782 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible')
1783 .clone().appendTo('#searchResults .gsc-tabsArea');
1784 }, 200);
1785 */
1786 });
1787 }
1788 setTimeout(function(){$(tabHeaders[0]).click()},200);
1789}
1790
1791
1792function getQuery(hash) {
1793 var queryParts = hash.split('=');
1794 return queryParts[1];
1795}
1796
1797/* returns the given string with all HTML brackets converted to entities
1798 TODO: move this to the site's JS library */
1799function escapeHTML(string) {
1800 return string.replace(/</g,"&lt;")
1801 .replace(/>/g,"&gt;");
1802}
1803
1804
1805
1806
1807
1808
1809
1810/* ######################################################## */
1811/* ################# JAVADOC REFERENCE ################### */
1812/* ######################################################## */
1813
1814/* Initialize some droiddoc stuff */
1815$(document).ready(function() {
1816
1817 // init available apis based on user pref
1818 changeApiLevel();
1819 initSidenavHeightResize()
1820});
1821
1822var API_LEVEL_COOKIE = "api_level";
1823var minLevel = 1;
1824var maxLevel = 1;
1825
1826/******* SIDENAV DIMENSIONS ************/
1827
1828 function initSidenavHeightResize() {
1829 // Change the drag bar size to nicely fit the scrollbar positions
1830 var $dragBar = $(".ui-resizable-s");
1831 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"});
1832
1833 $( "#resize-packages-nav" ).resizable({
1834 containment: "#nav-panels",
1835 handles: "s",
1836 alsoResize: "#packages-nav",
1837 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */
1838 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */
1839 });
1840
1841 }
1842
1843function updateSidenavFixedWidth() {
1844 if (!navBarIsFixed) return;
1845 $('#devdoc-nav').css({
1846 'width' : $('#side-nav').css('width'),
1847 'margin' : $('#side-nav').css('margin')
1848 });
1849 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'});
1850
1851 initSidenavHeightResize();
1852}
1853
1854function updateSidenavFullscreenWidth() {
1855 if (!navBarIsFixed) return;
1856 $('#devdoc-nav').css({
1857 'width' : $('#side-nav').css('width'),
1858 'margin' : $('#side-nav').css('margin')
1859 });
1860 $('#devdoc-nav .totop').css({'left': 'inherit'});
1861
1862 initSidenavHeightResize();
1863}
1864
1865function buildApiLevelSelector() {
1866 maxLevel = SINCE_DATA.length;
1867 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE));
1868 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default
1869
1870 minLevel = parseInt($("#doc-api-level").attr("class"));
1871 // Handle provisional api levels; the provisional level will always be the highest possible level
1872 // Provisional api levels will also have a length; other stuff that's just missing a level won't,
1873 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class)
1874 if (isNaN(minLevel) && minLevel.length) {
1875 minLevel = maxLevel;
1876 }
1877 var select = $("#apiLevelSelector").html("").change(changeApiLevel);
1878 for (var i = maxLevel-1; i >= 0; i--) {
1879 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]);
1880 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames)
1881 select.append(option);
1882 }
1883
1884 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true)
1885 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0);
1886 selectedLevelItem.setAttribute('selected',true);
1887}
1888
1889function changeApiLevel() {
1890 maxLevel = SINCE_DATA.length;
1891 var selectedLevel = maxLevel;
1892
1893 selectedLevel = parseInt($("#apiLevelSelector option:selected").val());
1894 toggleVisisbleApis(selectedLevel, "body");
1895
1896 var date = new Date();
1897 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
1898 var expiration = date.toGMTString();
1899 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration);
1900
1901 if (selectedLevel < minLevel) {
1902 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class";
1903 $("#naMessage").show().html("<div><p><strong>This " + thing + " is not available with API level " + selectedLevel + ".</strong></p>"
1904 + "<p>To use this " + thing + ", you must develop your app using a build target "
1905 + "that supports API level " + $("#doc-api-level").attr("class") + " or higher. To read these "
1906 + "APIs, change the value of the API level filter above.</p>"
1907 + "<p><a href='" +toRoot+ "guide/appendix/api-levels.html'>What is the API level?</a></p></div>");
1908 } else {
1909 $("#naMessage").hide();
1910 }
1911}
1912
1913function toggleVisisbleApis(selectedLevel, context) {
1914 var apis = $(".api",context);
1915 apis.each(function(i) {
1916 var obj = $(this);
1917 var className = obj.attr("class");
1918 var apiLevelIndex = className.lastIndexOf("-")+1;
1919 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex);
1920 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length;
1921 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex);
1922 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail
1923 return;
1924 }
1925 apiLevel = parseInt(apiLevel);
1926
1927 // Handle provisional api levels; if this item's level is the provisional one, set it to the max
1928 var selectedLevelNum = parseInt(selectedLevel)
1929 var apiLevelNum = parseInt(apiLevel);
1930 if (isNaN(apiLevelNum)) {
1931 apiLevelNum = maxLevel;
1932 }
1933
1934 // Grey things out that aren't available and give a tooltip title
1935 if (apiLevelNum > selectedLevelNum) {
1936 obj.addClass("absent").attr("title","Requires API Level \""
1937 + apiLevel + "\" or higher");
1938 }
1939 else obj.removeClass("absent").removeAttr("title");
1940 });
1941}
1942
1943
1944
1945
1946/* ################# SIDENAV TREE VIEW ################### */
1947
1948function new_node(me, mom, text, link, children_data, api_level)
1949{
1950 var node = new Object();
1951 node.children = Array();
1952 node.children_data = children_data;
1953 node.depth = mom.depth + 1;
1954
1955 node.li = document.createElement("li");
1956 mom.get_children_ul().appendChild(node.li);
1957
1958 node.label_div = document.createElement("div");
1959 node.label_div.className = "label";
1960 if (api_level != null) {
1961 $(node.label_div).addClass("api");
1962 $(node.label_div).addClass("api-level-"+api_level);
1963 }
1964 node.li.appendChild(node.label_div);
1965
1966 if (children_data != null) {
1967 node.expand_toggle = document.createElement("a");
1968 node.expand_toggle.href = "javascript:void(0)";
1969 node.expand_toggle.onclick = function() {
1970 if (node.expanded) {
1971 $(node.get_children_ul()).slideUp("fast");
1972 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
1973 node.expanded = false;
1974 } else {
1975 expand_node(me, node);
1976 }
1977 };
1978 node.label_div.appendChild(node.expand_toggle);
1979
1980 node.plus_img = document.createElement("img");
1981 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
1982 node.plus_img.className = "plus";
1983 node.plus_img.width = "8";
1984 node.plus_img.border = "0";
1985 node.expand_toggle.appendChild(node.plus_img);
1986
1987 node.expanded = false;
1988 }
1989
1990 var a = document.createElement("a");
1991 node.label_div.appendChild(a);
1992 node.label = document.createTextNode(text);
1993 a.appendChild(node.label);
1994 if (link) {
1995 a.href = me.toroot + link;
1996 } else {
1997 if (children_data != null) {
1998 a.className = "nolink";
1999 a.href = "javascript:void(0)";
2000 a.onclick = node.expand_toggle.onclick;
2001 // This next line shouldn't be necessary. I'll buy a beer for the first
2002 // person who figures out how to remove this line and have the link
2003 // toggle shut on the first try. --joeo@android.com
2004 node.expanded = false;
2005 }
2006 }
2007
2008
2009 node.children_ul = null;
2010 node.get_children_ul = function() {
2011 if (!node.children_ul) {
2012 node.children_ul = document.createElement("ul");
2013 node.children_ul.className = "children_ul";
2014 node.children_ul.style.display = "none";
2015 node.li.appendChild(node.children_ul);
2016 }
2017 return node.children_ul;
2018 };
2019
2020 return node;
2021}
2022
2023function expand_node(me, node)
2024{
2025 if (node.children_data && !node.expanded) {
2026 if (node.children_visited) {
2027 $(node.get_children_ul()).slideDown("fast");
2028 } else {
2029 get_node(me, node);
2030 if ($(node.label_div).hasClass("absent")) {
2031 $(node.get_children_ul()).addClass("absent");
2032 }
2033 $(node.get_children_ul()).slideDown("fast");
2034 }
2035 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
2036 node.expanded = true;
2037
2038 // perform api level toggling because new nodes are new to the DOM
2039 var selectedLevel = $("#apiLevelSelector option:selected").val();
2040 toggleVisisbleApis(selectedLevel, "#side-nav");
2041 }
2042}
2043
2044function get_node(me, mom)
2045{
2046 mom.children_visited = true;
2047 for (var i in mom.children_data) {
2048 var node_data = mom.children_data[i];
2049 mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
2050 node_data[2], node_data[3]);
2051 }
2052}
2053
2054function this_page_relative(toroot)
2055{
2056 var full = document.location.pathname;
2057 var file = "";
2058 if (toroot.substr(0, 1) == "/") {
2059 if (full.substr(0, toroot.length) == toroot) {
2060 return full.substr(toroot.length);
2061 } else {
2062 // the file isn't under toroot. Fail.
2063 return null;
2064 }
2065 } else {
2066 if (toroot != "./") {
2067 toroot = "./" + toroot;
2068 }
2069 do {
2070 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
2071 var pos = full.lastIndexOf("/");
2072 file = full.substr(pos) + file;
2073 full = full.substr(0, pos);
2074 toroot = toroot.substr(0, toroot.length-3);
2075 }
2076 } while (toroot != "" && toroot != "/");
2077 return file.substr(1);
2078 }
2079}
2080
2081function find_page(url, data)
2082{
2083 var nodes = data;
2084 var result = null;
2085 for (var i in nodes) {
2086 var d = nodes[i];
2087 if (d[1] == url) {
2088 return new Array(i);
2089 }
2090 else if (d[2] != null) {
2091 result = find_page(url, d[2]);
2092 if (result != null) {
2093 return (new Array(i).concat(result));
2094 }
2095 }
2096 }
2097 return null;
2098}
2099
2100function load_navtree_data(toroot) {
2101 var navtreeData = document.createElement("script");
2102 navtreeData.setAttribute("type","text/javascript");
2103 navtreeData.setAttribute("src", toroot+"navtree_data.js");
2104 $("head").append($(navtreeData));
2105}
2106
2107function init_default_navtree(toroot) {
2108 init_navtree("tree-list", toroot, NAVTREE_DATA);
2109
2110 // perform api level toggling because because the whole tree is new to the DOM
2111 var selectedLevel = $("#apiLevelSelector option:selected").val();
2112 toggleVisisbleApis(selectedLevel, "#side-nav");
2113}
2114
2115function init_navtree(navtree_id, toroot, root_nodes)
2116{
2117 var me = new Object();
2118 me.toroot = toroot;
2119 me.node = new Object();
2120
2121 me.node.li = document.getElementById(navtree_id);
2122 me.node.children_data = root_nodes;
2123 me.node.children = new Array();
2124 me.node.children_ul = document.createElement("ul");
2125 me.node.get_children_ul = function() { return me.node.children_ul; };
2126 //me.node.children_ul.className = "children_ul";
2127 me.node.li.appendChild(me.node.children_ul);
2128 me.node.depth = 0;
2129
2130 get_node(me, me.node);
2131
2132 me.this_page = this_page_relative(toroot);
2133 me.breadcrumbs = find_page(me.this_page, root_nodes);
2134 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
2135 var mom = me.node;
2136 for (var i in me.breadcrumbs) {
2137 var j = me.breadcrumbs[i];
2138 mom = mom.children[j];
2139 expand_node(me, mom);
2140 }
2141 mom.label_div.className = mom.label_div.className + " selected";
2142 addLoadEvent(function() {
2143 scrollIntoView("nav-tree");
2144 });
2145 }
2146}
2147
2148/* TOGGLE INHERITED MEMBERS */
2149
2150/* Toggle an inherited class (arrow toggle)
2151 * @param linkObj The link that was clicked.
2152 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2153 * 'null' to simply toggle.
2154 */
2155function toggleInherited(linkObj, expand) {
2156 var base = linkObj.getAttribute("id");
2157 var list = document.getElementById(base + "-list");
2158 var summary = document.getElementById(base + "-summary");
2159 var trigger = document.getElementById(base + "-trigger");
2160 var a = $(linkObj);
2161 if ( (expand == null && a.hasClass("closed")) || expand ) {
2162 list.style.display = "none";
2163 summary.style.display = "block";
2164 trigger.src = toRoot + "assets/images/triangle-opened.png";
2165 a.removeClass("closed");
2166 a.addClass("opened");
2167 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
2168 list.style.display = "block";
2169 summary.style.display = "none";
2170 trigger.src = toRoot + "assets/images/triangle-closed.png";
2171 a.removeClass("opened");
2172 a.addClass("closed");
2173 }
2174 return false;
2175}
2176
2177/* Toggle all inherited classes in a single table (e.g. all inherited methods)
2178 * @param linkObj The link that was clicked.
2179 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed.
2180 * 'null' to simply toggle.
2181 */
2182function toggleAllInherited(linkObj, expand) {
2183 var a = $(linkObj);
2184 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody
2185 var expandos = $(".jd-expando-trigger", table);
2186 if ( (expand == null && a.text() == "[Expand]") || expand ) {
2187 expandos.each(function(i) {
2188 toggleInherited(this, true);
2189 });
2190 a.text("[Collapse]");
2191 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
2192 expandos.each(function(i) {
2193 toggleInherited(this, false);
2194 });
2195 a.text("[Expand]");
2196 }
2197 return false;
2198}
2199
2200/* Toggle all inherited members in the class (link in the class title)
2201 */
2202function toggleAllClassInherited() {
2203 var a = $("#toggleAllClassInherited"); // get toggle link from class title
2204 var toggles = $(".toggle-all", $("#body-content"));
2205 if (a.text() == "[Expand All]") {
2206 toggles.each(function(i) {
2207 toggleAllInherited(this, true);
2208 });
2209 a.text("[Collapse All]");
2210 } else {
2211 toggles.each(function(i) {
2212 toggleAllInherited(this, false);
2213 });
2214 a.text("[Expand All]");
2215 }
2216 return false;
2217}
2218
2219/* Expand all inherited members in the class. Used when initiating page search */
2220function ensureAllInheritedExpanded() {
2221 var toggles = $(".toggle-all", $("#body-content"));
2222 toggles.each(function(i) {
2223 toggleAllInherited(this, true);
2224 });
2225 $("#toggleAllClassInherited").text("[Collapse All]");
2226}
2227
2228
2229/* HANDLE KEY EVENTS
2230 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search)
2231 */
2232var agent = navigator['userAgent'].toLowerCase();
2233var mac = agent.indexOf("macintosh") != -1;
2234
2235$(document).keydown( function(e) {
2236var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key
2237 if (control && e.which == 70) { // 70 is "F"
2238 ensureAllInheritedExpanded();
2239 }
2240});
2241
2242
2243