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