docs.js revision 0e76e7e4dd44d9e8893cbf2e077a96e655628e99
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 9var mPagePath; // initialized in ready() function 10 11var basePath = getBaseUri(location.pathname); 12var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1)); 13var GOOGLE_DATA; // combined data for google service apis, used for search suggest 14 15// Ensure that all ajax getScript() requests allow caching 16$.ajaxSetup({ 17 cache: true 18}); 19 20/****** ON LOAD SET UP STUFF *********/ 21 22var navBarIsFixed = false; 23$(document).ready(function() { 24 25 // load json file for JD doc search suggestions 26 $.getScript(toRoot + 'reference/jd_lists.js'); 27 // load json file for Android API search suggestions 28 $.getScript(toRoot + 'reference/lists.js'); 29 // load json files for Google services API suggestions 30 $.getScript(toRoot + 'reference/gcm_lists.js', function(data, textStatus, jqxhr) { 31 // once the GCM json (GCM_DATA) is loaded, load the GMS json (GMS_DATA) and merge the data 32 if(jqxhr.status === 200) { 33 $.getScript(toRoot + 'reference/gms_lists.js', function(data, textStatus, jqxhr) { 34 if(jqxhr.status === 200) { 35 // combine GCM and GMS data 36 GOOGLE_DATA = GMS_DATA; 37 var start = GOOGLE_DATA.length; 38 for (var i=0; i<GCM_DATA.length; i++) { 39 GOOGLE_DATA.push({id:start+i, label:GCM_DATA[i].label, 40 link:GCM_DATA[i].link, type:GCM_DATA[i].type}); 41 } 42 } 43 }); 44 } 45 }); 46 47 // setup keyboard listener for search shortcut 48 $('body').keyup(function(event) { 49 if (event.which == 191) { 50 $('#search_autocomplete').focus(); 51 } 52 }); 53 54 // init the fullscreen toggle click event 55 $('#nav-swap .fullscreen').click(function(){ 56 if ($(this).hasClass('disabled')) { 57 toggleFullscreen(true); 58 } else { 59 toggleFullscreen(false); 60 } 61 }); 62 63 // initialize the divs with custom scrollbars 64 $('.scroll-pane').jScrollPane( {verticalGutter:0} ); 65 66 // add HRs below all H2s (except for a few other h2 variants) 67 $('h2').not('#qv h2').not('#tb h2').not('.sidebox h2').not('#devdoc-nav h2').not('h2.norule').css({marginBottom:0}).after('<hr/>'); 68 69 // set up the search close button 70 $('.search .close').click(function() { 71 $searchInput = $('#search_autocomplete'); 72 $searchInput.attr('value', ''); 73 $(this).addClass("hide"); 74 $("#search-container").removeClass('active'); 75 $("#search_autocomplete").blur(); 76 search_focus_changed($searchInput.get(), false); 77 hideResults(); 78 }); 79 80 // Set up quicknav 81 var quicknav_open = false; 82 $("#btn-quicknav").click(function() { 83 if (quicknav_open) { 84 $(this).removeClass('active'); 85 quicknav_open = false; 86 collapse(); 87 } else { 88 $(this).addClass('active'); 89 quicknav_open = true; 90 expand(); 91 } 92 }) 93 94 var expand = function() { 95 $('#header-wrap').addClass('quicknav'); 96 $('#quicknav').stop().show().animate({opacity:'1'}); 97 } 98 99 var collapse = function() { 100 $('#quicknav').stop().animate({opacity:'0'}, 100, function() { 101 $(this).hide(); 102 $('#header-wrap').removeClass('quicknav'); 103 }); 104 } 105 106 107 //Set up search 108 $("#search_autocomplete").focus(function() { 109 $("#search-container").addClass('active'); 110 }) 111 $("#search-container").mouseover(function() { 112 $("#search-container").addClass('active'); 113 $("#search_autocomplete").focus(); 114 }) 115 $("#search-container").mouseout(function() { 116 if ($("#search_autocomplete").is(":focus")) return; 117 if ($("#search_autocomplete").val() == '') { 118 setTimeout(function(){ 119 $("#search-container").removeClass('active'); 120 $("#search_autocomplete").blur(); 121 },250); 122 } 123 }) 124 $("#search_autocomplete").blur(function() { 125 if ($("#search_autocomplete").val() == '') { 126 $("#search-container").removeClass('active'); 127 } 128 }) 129 130 131 // prep nav expandos 132 var pagePath = document.location.pathname; 133 // account for intl docs by removing the intl/*/ path 134 if (pagePath.indexOf("/intl/") == 0) { 135 pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last / 136 } 137 138 if (pagePath.indexOf(SITE_ROOT) == 0) { 139 if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') { 140 pagePath += 'index.html'; 141 } 142 } 143 144 // Need a copy of the pagePath before it gets changed in the next block; 145 // it's needed to perform proper tab highlighting in offline docs (see rootDir below) 146 var pagePathOriginal = pagePath; 147 if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') { 148 // If running locally, SITE_ROOT will be a relative path, so account for that by 149 // finding the relative URL to this page. This will allow us to find links on the page 150 // leading back to this page. 151 var pathParts = pagePath.split('/'); 152 var relativePagePathParts = []; 153 var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3; 154 for (var i = 0; i < upDirs; i++) { 155 relativePagePathParts.push('..'); 156 } 157 for (var i = 0; i < upDirs; i++) { 158 relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]); 159 } 160 relativePagePathParts.push(pathParts[pathParts.length - 1]); 161 pagePath = relativePagePathParts.join('/'); 162 } else { 163 // Otherwise the page path is already an absolute URL 164 } 165 166 // Highlight the header tabs... 167 // highlight Design tab 168 if ($("body").hasClass("design")) { 169 $("#header li.design a").addClass("selected"); 170 171 // highlight Develop tab 172 } else if ($("body").hasClass("develop") || $("body").hasClass("google")) { 173 $("#header li.develop a").addClass("selected"); 174 // In Develop docs, also highlight appropriate sub-tab 175 var rootDir = pagePathOriginal.substring(1,pagePathOriginal.indexOf('/', 1)); 176 if (rootDir == "training") { 177 $("#nav-x li.training a").addClass("selected"); 178 } else if (rootDir == "guide") { 179 $("#nav-x li.guide a").addClass("selected"); 180 } else if (rootDir == "reference") { 181 // If the root is reference, but page is also part of Google Services, select Google 182 if ($("body").hasClass("google")) { 183 $("#nav-x li.google a").addClass("selected"); 184 } else { 185 $("#nav-x li.reference a").addClass("selected"); 186 } 187 } else if ((rootDir == "tools") || (rootDir == "sdk")) { 188 $("#nav-x li.tools a").addClass("selected"); 189 } else if ($("body").hasClass("google")) { 190 $("#nav-x li.google a").addClass("selected"); 191 } 192 193 // highlight Distribute tab 194 } else if ($("body").hasClass("distribute")) { 195 $("#header li.distribute a").addClass("selected"); 196 } 197 198 // set global variable so we can highlight the sidenav a bit later (such as for google reference) 199 // and highlight the sidenav 200 mPagePath = pagePath; 201 highlightSidenav(); 202 203 // set up prev/next links if they exist 204 var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]'); 205 var $selListItem; 206 if ($selNavLink.length) { 207 $selListItem = $selNavLink.closest('li'); 208 209 // set up prev links 210 var $prevLink = []; 211 var $prevListItem = $selListItem.prev('li'); 212 213 var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true : 214false; // navigate across topic boundaries only in design docs 215 if ($prevListItem.length) { 216 if ($prevListItem.hasClass('nav-section')) { 217 // jump to last topic of previous section 218 $prevLink = $prevListItem.find('a:last'); 219 } else if (!$selListItem.hasClass('nav-section')) { 220 // jump to previous topic in this section 221 $prevLink = $prevListItem.find('a:eq(0)'); 222 } 223 } else { 224 // jump to this section's index page (if it exists) 225 var $parentListItem = $selListItem.parents('li'); 226 $prevLink = $selListItem.parents('li').find('a'); 227 228 // except if cross boundaries aren't allowed, and we're at the top of a section already 229 // (and there's another parent) 230 if (!crossBoundaries && $parentListItem.hasClass('nav-section') 231 && $selListItem.hasClass('nav-section')) { 232 $prevLink = []; 233 } 234 } 235 236 // set up next links 237 var $nextLink = []; 238 var startClass = false; 239 var training = $(".next-class-link").length; // decides whether to provide "next class" link 240 var isCrossingBoundary = false; 241 242 if ($selListItem.hasClass('nav-section')) { 243 // we're on an index page, jump to the first topic 244 $nextLink = $selListItem.find('ul:eq(0)').find('a:eq(0)'); 245 246 // if there aren't any children, go to the next section (required for About pages) 247 if($nextLink.length == 0) { 248 $nextLink = $selListItem.next('li').find('a'); 249 } else if ($('.topic-start-link').length) { 250 // as long as there's a child link and there is a "topic start link" (we're on a landing) 251 // then set the landing page "start link" text to be the first doc title 252 $('.topic-start-link').text($nextLink.text().toUpperCase()); 253 } 254 255 // If the selected page has a description, then it's a class or article homepage 256 if ($selListItem.find('a[description]').length) { 257 // this means we're on a class landing page 258 startClass = true; 259 } 260 } else { 261 // jump to the next topic in this section (if it exists) 262 $nextLink = $selListItem.next('li').find('a:eq(0)'); 263 if (!$nextLink.length) { 264 isCrossingBoundary = true; 265 // no more topics in this section, jump to the first topic in the next section 266 $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)'); 267 if (!$nextLink.length) { // Go up another layer to look for next page (lesson > class > course) 268 $nextLink = $selListItem.parents('li:eq(1)').next('li.nav-section').find('a:eq(0)'); 269 } 270 } 271 } 272 273 if (startClass) { 274 $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide"); 275 276 // if there's no training bar (below the start button), 277 // then we need to add a bottom border to button 278 if (!$("#tb").length) { 279 $('.start-class-link').css({'border-bottom':'1px solid #DADADA'}); 280 } 281 } else if (isCrossingBoundary && !$('body.design').length) { // Design always crosses boundaries 282 $('.content-footer.next-class').show(); 283 $('.next-page-link').attr('href','') 284 .removeClass("hide").addClass("disabled") 285 .click(function() { return false; }); 286 287 $('.next-class-link').attr('href',$nextLink.attr('href')) 288 .removeClass("hide").append($nextLink.html()); 289 $('.next-class-link').find('.new').empty(); 290 } else { 291 $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide"); 292 } 293 294 if (!startClass && $prevLink.length) { 295 var prevHref = $prevLink.attr('href'); 296 if (prevHref == SITE_ROOT + 'index.html') { 297 // Don't show Previous when it leads to the homepage 298 } else { 299 $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide"); 300 } 301 } 302 303 // If this is a training 'article', there should be no prev/next nav 304 // ... if the grandparent is the "nav" ... and it has no child list items... 305 if (training && $selListItem.parents('ul').eq(1).is('[id="nav"]') && 306 !$selListItem.find('li').length) { 307 $('.next-page-link,.prev-page-link').attr('href','').addClass("disabled") 308 .click(function() { return false; }); 309 } 310 311 } 312 313 314 315 // Set up the course landing pages for Training with class names and descriptions 316 if ($('body.trainingcourse').length) { 317 var $classLinks = $selListItem.find('ul li a').not('#nav .nav-section .nav-section ul a'); 318 var $classDescriptions = $classLinks.attr('description'); 319 320 var $olClasses = $('<ol class="class-list"></ol>'); 321 var $liClass; 322 var $imgIcon; 323 var $h2Title; 324 var $pSummary; 325 var $olLessons; 326 var $liLesson; 327 $classLinks.each(function(index) { 328 $liClass = $('<li></li>'); 329 $h2Title = $('<a class="title" href="'+$(this).attr('href')+'"><h2>' + $(this).html()+'</h2><span></span></a>'); 330 $pSummary = $('<p class="description">' + $(this).attr('description') + '</p>'); 331 332 $olLessons = $('<ol class="lesson-list"></ol>'); 333 334 $lessons = $(this).closest('li').find('ul li a'); 335 336 if ($lessons.length) { 337 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-tutorial.png" alt=""/>'); 338 $lessons.each(function(index) { 339 $olLessons.append('<li><a href="'+$(this).attr('href')+'">' + $(this).html()+'</a></li>'); 340 }); 341 } else { 342 $imgIcon = $('<img src="'+toRoot+'assets/images/resource-article.png" alt=""/>'); 343 $pSummary.addClass('article'); 344 } 345 346 $liClass.append($h2Title).append($imgIcon).append($pSummary).append($olLessons); 347 $olClasses.append($liClass); 348 }); 349 $('.jd-descr').append($olClasses); 350 } 351 352 353 354 355 // Set up expand/collapse behavior 356 $('#nav li.nav-section .nav-section-header').click(function() { 357 var section = $(this).closest('li.nav-section'); 358 if (section.hasClass('expanded')) { 359 /* hide me */ 360 // if (section.hasClass('selected') || section.find('li').hasClass('selected')) { 361 // /* but not if myself or my descendents are selected */ 362 // return; 363 // } 364 section.children('ul').slideUp(250, function() { 365 section.closest('li').removeClass('expanded'); 366 resizeNav(); 367 }); 368 } else { 369 /* show me */ 370 // first hide all other siblings 371 var $others = $('li.nav-section.expanded', $(this).closest('ul')); 372 $others.removeClass('expanded').children('ul').slideUp(250); 373 374 // now expand me 375 section.closest('li').addClass('expanded'); 376 section.children('ul').slideDown(250, function() { 377 resizeNav(); 378 }); 379 } 380 }); 381 382 $(".scroll-pane").scroll(function(event) { 383 event.preventDefault(); 384 return false; 385 }); 386 387 /* Resize nav height when window height changes */ 388 $(window).resize(function() { 389 if ($('#side-nav').length == 0) return; 390 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]'); 391 setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed 392 // make sidenav behave when resizing the window and side-scolling is a concern 393 if (navBarIsFixed) { 394 if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) { 395 updateSideNavPosition(); 396 } else { 397 updateSidenavFullscreenWidth(); 398 } 399 } 400 resizeNav(); 401 }); 402 403 404 // Set up fixed navbar 405 var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll 406 $(window).scroll(function(event) { 407 if ($('#side-nav').length == 0) return; 408 if (event.target.nodeName == "DIV") { 409 // Dump scroll event if the target is a DIV, because that means the event is coming 410 // from a scrollable div and so there's no need to make adjustments to our layout 411 return; 412 } 413 var scrollTop = $(window).scrollTop(); 414 var headerHeight = $('#header').outerHeight(); 415 var subheaderHeight = $('#nav-x').outerHeight(); 416 var searchResultHeight = $('#searchResults').is(":visible") ? 417 $('#searchResults').outerHeight() : 0; 418 var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight; 419 // we set the navbar fixed when the scroll position is beyond the height of the site header... 420 var navBarShouldBeFixed = scrollTop > totalHeaderHeight; 421 // ... except if the document content is shorter than the sidenav height. 422 // (this is necessary to avoid crazy behavior on OSX Lion due to overscroll bouncing) 423 if ($("#doc-col").height() < $("#side-nav").height()) { 424 navBarShouldBeFixed = false; 425 } 426 427 var scrollLeft = $(window).scrollLeft(); 428 // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match 429 if (navBarIsFixed && (scrollLeft != prevScrollLeft)) { 430 updateSideNavPosition(); 431 prevScrollLeft = scrollLeft; 432 } 433 434 // Don't continue if the header is sufficently far away 435 // (to avoid intensive resizing that slows scrolling) 436 if (navBarIsFixed && navBarShouldBeFixed) { 437 return; 438 } 439 440 if (navBarIsFixed != navBarShouldBeFixed) { 441 if (navBarShouldBeFixed) { 442 // make it fixed 443 var width = $('#devdoc-nav').width(); 444 $('#devdoc-nav') 445 .addClass('fixed') 446 .css({'width':width+'px'}) 447 .prependTo('#body-content'); 448 // add neato "back to top" button 449 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'}); 450 451 // update the sidenaav position for side scrolling 452 updateSideNavPosition(); 453 } else { 454 // make it static again 455 $('#devdoc-nav') 456 .removeClass('fixed') 457 .css({'width':'auto','margin':''}) 458 .prependTo('#side-nav'); 459 $('#devdoc-nav a.totop').hide(); 460 } 461 navBarIsFixed = navBarShouldBeFixed; 462 } 463 464 resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance 465 }); 466 467 468 var navBarLeftPos; 469 if ($('#devdoc-nav').length) { 470 setNavBarLeftPos(); 471 } 472 473 474 // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away 475 // from the page) 476 $('.nav-section-header').find('a:eq(0)').click(function(evt) { 477 window.location.href = $(this).attr('href'); 478 return false; 479 }); 480 481 // Set up play-on-hover <video> tags. 482 $('video.play-on-hover').bind('click', function(){ 483 $(this).get(0).load(); // in case the video isn't seekable 484 $(this).get(0).play(); 485 }); 486 487 // Set up tooltips 488 var TOOLTIP_MARGIN = 10; 489 $('acronym,.tooltip-link').each(function() { 490 var $target = $(this); 491 var $tooltip = $('<div>') 492 .addClass('tooltip-box') 493 .append($target.attr('title')) 494 .hide() 495 .appendTo('body'); 496 $target.removeAttr('title'); 497 498 $target.hover(function() { 499 // in 500 var targetRect = $target.offset(); 501 targetRect.width = $target.width(); 502 targetRect.height = $target.height(); 503 504 $tooltip.css({ 505 left: targetRect.left, 506 top: targetRect.top + targetRect.height + TOOLTIP_MARGIN 507 }); 508 $tooltip.addClass('below'); 509 $tooltip.show(); 510 }, function() { 511 // out 512 $tooltip.hide(); 513 }); 514 }); 515 516 // Set up <h2> deeplinks 517 $('h2').click(function() { 518 var id = $(this).attr('id'); 519 if (id) { 520 document.location.hash = id; 521 } 522 }); 523 524 //Loads the +1 button 525 var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; 526 po.src = 'https://apis.google.com/js/plusone.js'; 527 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); 528 529 530 // Revise the sidenav widths to make room for the scrollbar 531 // which avoids the visible width from changing each time the bar appears 532 var $sidenav = $("#side-nav"); 533 var sidenav_width = parseInt($sidenav.innerWidth()); 534 535 $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width 536 537 538 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller 539 540 if ($(".scroll-pane").length > 1) { 541 // Check if there's a user preference for the panel heights 542 var cookieHeight = readCookie("reference_height"); 543 if (cookieHeight) { 544 restoreHeight(cookieHeight); 545 } 546 } 547 548 resizeNav(); 549 550 /* init the language selector based on user cookie for lang */ 551 loadLangPref(); 552 changeNavLang(getLangPref()); 553 554 /* setup event handlers to ensure the overflow menu is visible while picking lang */ 555 $("#language select") 556 .mousedown(function() { 557 $("div.morehover").addClass("hover"); }) 558 .blur(function() { 559 $("div.morehover").removeClass("hover"); }); 560 561 /* some global variable setup */ 562 resizePackagesNav = $("#resize-packages-nav"); 563 classesNav = $("#classes-nav"); 564 devdocNav = $("#devdoc-nav"); 565 566 var cookiePath = ""; 567 if (location.href.indexOf("/reference/") != -1) { 568 cookiePath = "reference_"; 569 } else if (location.href.indexOf("/guide/") != -1) { 570 cookiePath = "guide_"; 571 } else if (location.href.indexOf("/tools/") != -1) { 572 cookiePath = "tools_"; 573 } else if (location.href.indexOf("/training/") != -1) { 574 cookiePath = "training_"; 575 } else if (location.href.indexOf("/design/") != -1) { 576 cookiePath = "design_"; 577 } else if (location.href.indexOf("/distribute/") != -1) { 578 cookiePath = "distribute_"; 579 } 580 581}); 582// END of the onload event 583 584 585function highlightSidenav() { 586 // select current page in sidenav and header, and set up prev/next links if they exist 587 var $selNavLink = $('#nav').find('a[href="' + mPagePath + '"]'); 588 var $selListItem; 589 if ($selNavLink.length) { 590 591 // Find this page's <li> in sidenav and set selected 592 $selListItem = $selNavLink.closest('li'); 593 $selListItem.addClass('selected'); 594 595 // Traverse up the tree and expand all parent nav-sections 596 $selNavLink.parents('li.nav-section').each(function() { 597 $(this).addClass('expanded'); 598 $(this).children('ul').show(); 599 }); 600 } 601} 602 603 604function toggleFullscreen(enable) { 605 var delay = 20; 606 var enabled = true; 607 var stylesheet = $('link[rel="stylesheet"][class="fullscreen"]'); 608 if (enable) { 609 // Currently NOT USING fullscreen; enable fullscreen 610 stylesheet.removeAttr('disabled'); 611 $('#nav-swap .fullscreen').removeClass('disabled'); 612 $('#devdoc-nav').css({left:''}); 613 setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch 614 enabled = true; 615 } else { 616 // Currently USING fullscreen; disable fullscreen 617 stylesheet.attr('disabled', 'disabled'); 618 $('#nav-swap .fullscreen').addClass('disabled'); 619 setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch 620 enabled = false; 621 } 622 writeCookie("fullscreen", enabled, null, null); 623 setNavBarLeftPos(); 624 resizeNav(delay); 625 updateSideNavPosition(); 626 setTimeout(initSidenavHeightResize,delay); 627} 628 629 630function setNavBarLeftPos() { 631 navBarLeftPos = $('#body-content').offset().left; 632} 633 634 635function updateSideNavPosition() { 636 var newLeft = $(window).scrollLeft() - navBarLeftPos; 637 $('#devdoc-nav').css({left: -newLeft}); 638 $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))}); 639} 640 641 642 643 644 645 646 647 648// TODO: use $(document).ready instead 649function addLoadEvent(newfun) { 650 var current = window.onload; 651 if (typeof window.onload != 'function') { 652 window.onload = newfun; 653 } else { 654 window.onload = function() { 655 current(); 656 newfun(); 657 } 658 } 659} 660 661var agent = navigator['userAgent'].toLowerCase(); 662// If a mobile phone, set flag and do mobile setup 663if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod 664 (agent.indexOf("blackberry") != -1) || 665 (agent.indexOf("webos") != -1) || 666 (agent.indexOf("mini") != -1)) { // opera mini browsers 667 isMobile = true; 668} 669 670 671addLoadEvent( function() { 672 $("pre:not(.no-pretty-print)").addClass("prettyprint"); 673 prettyPrint(); 674} ); 675 676 677 678 679/* ######### RESIZE THE SIDENAV HEIGHT ########## */ 680 681function resizeNav(delay) { 682 var $nav = $("#devdoc-nav"); 683 var $window = $(window); 684 var navHeight; 685 686 // Get the height of entire window and the total header height. 687 // Then figure out based on scroll position whether the header is visible 688 var windowHeight = $window.height(); 689 var scrollTop = $window.scrollTop(); 690 var headerHeight = $('#header').outerHeight(); 691 var subheaderHeight = $('#nav-x').outerHeight(); 692 var headerVisible = (scrollTop < (headerHeight + subheaderHeight)); 693 694 // get the height of space between nav and top of window. 695 // Could be either margin or top position, depending on whether the nav is fixed. 696 var topMargin = (parseInt($nav.css('margin-top')) || parseInt($nav.css('top'))) + 1; 697 // add 1 for the #side-nav bottom margin 698 699 // Depending on whether the header is visible, set the side nav's height. 700 if (headerVisible) { 701 // The sidenav height grows as the header goes off screen 702 navHeight = windowHeight - (headerHeight + subheaderHeight - scrollTop) - topMargin; 703 } else { 704 // Once header is off screen, the nav height is almost full window height 705 navHeight = windowHeight - topMargin; 706 } 707 708 709 710 $scrollPanes = $(".scroll-pane"); 711 if ($scrollPanes.length > 1) { 712 // subtract the height of the api level widget and nav swapper from the available nav height 713 navHeight -= ($('#api-nav-header').outerHeight(true) + $('#nav-swap').outerHeight(true)); 714 715 $("#swapper").css({height:navHeight + "px"}); 716 if ($("#nav-tree").is(":visible")) { 717 $("#nav-tree").css({height:navHeight}); 718 } 719 720 var classesHeight = navHeight - parseInt($("#resize-packages-nav").css("height")) - 10 + "px"; 721 //subtract 10px to account for drag bar 722 723 // if the window becomes small enough to make the class panel height 0, 724 // then the package panel should begin to shrink 725 if (parseInt(classesHeight) <= 0) { 726 $("#resize-packages-nav").css({height:navHeight - 10}); //subtract 10px for drag bar 727 $("#packages-nav").css({height:navHeight - 10}); 728 } 729 730 $("#classes-nav").css({'height':classesHeight, 'margin-top':'10px'}); 731 $("#classes-nav .jspContainer").css({height:classesHeight}); 732 733 734 } else { 735 $nav.height(navHeight); 736 } 737 738 if (delay) { 739 updateFromResize = true; 740 delayedReInitScrollbars(delay); 741 } else { 742 reInitScrollbars(); 743 } 744 745} 746 747var updateScrollbars = false; 748var updateFromResize = false; 749 750/* Re-initialize the scrollbars to account for changed nav size. 751 * This method postpones the actual update by a 1/4 second in order to optimize the 752 * scroll performance while the header is still visible, because re-initializing the 753 * scroll panes is an intensive process. 754 */ 755function delayedReInitScrollbars(delay) { 756 // If we're scheduled for an update, but have received another resize request 757 // before the scheduled resize has occured, just ignore the new request 758 // (and wait for the scheduled one). 759 if (updateScrollbars && updateFromResize) { 760 updateFromResize = false; 761 return; 762 } 763 764 // We're scheduled for an update and the update request came from this method's setTimeout 765 if (updateScrollbars && !updateFromResize) { 766 reInitScrollbars(); 767 updateScrollbars = false; 768 } else { 769 updateScrollbars = true; 770 updateFromResize = false; 771 setTimeout('delayedReInitScrollbars()',delay); 772 } 773} 774 775/* Re-initialize the scrollbars to account for changed nav size. */ 776function reInitScrollbars() { 777 var pane = $(".scroll-pane").each(function(){ 778 var api = $(this).data('jsp'); 779 if (!api) { setTimeout(reInitScrollbars,300); return;} 780 api.reinitialise( {verticalGutter:0} ); 781 }); 782 $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller 783} 784 785 786/* Resize the height of the nav panels in the reference, 787 * and save the new size to a cookie */ 788function saveNavPanels() { 789 var basePath = getBaseUri(location.pathname); 790 var section = basePath.substring(1,basePath.indexOf("/",1)); 791 writeCookie("height", resizePackagesNav.css("height"), section, null); 792} 793 794 795 796function restoreHeight(packageHeight) { 797 $("#resize-packages-nav").height(packageHeight); 798 $("#packages-nav").height(packageHeight); 799 // var classesHeight = navHeight - packageHeight; 800 // $("#classes-nav").css({height:classesHeight}); 801 // $("#classes-nav .jspContainer").css({height:classesHeight}); 802} 803 804 805 806/* ######### END RESIZE THE SIDENAV HEIGHT ########## */ 807 808 809 810 811 812/** Scroll the jScrollPane to make the currently selected item visible 813 This is called when the page finished loading. */ 814function scrollIntoView(nav) { 815 var $nav = $("#"+nav); 816 var element = $nav.jScrollPane({/* ...settings... */}); 817 var api = element.data('jsp'); 818 819 if ($nav.is(':visible')) { 820 var $selected = $(".selected", $nav); 821 if ($selected.length == 0) return; 822 823 var selectedOffset = $selected.position().top; 824 if (selectedOffset + 90 > $nav.height()) { // add 90 so that we scroll up even 825 // if the current item is close to the bottom 826 api.scrollTo(0, selectedOffset - ($nav.height() / 4), false); // scroll the item into view 827 // to be 1/4 of the way from the top 828 } 829 } 830} 831 832 833 834 835 836 837/* Show popup dialogs */ 838function showDialog(id) { 839 $dialog = $("#"+id); 840 $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>'); 841 $dialog.wrapInner('<div/>'); 842 $dialog.removeClass("hide"); 843} 844 845 846 847 848 849/* ######### COOKIES! ########## */ 850 851function readCookie(cookie) { 852 var myCookie = cookie_namespace+"_"+cookie+"="; 853 if (document.cookie) { 854 var index = document.cookie.indexOf(myCookie); 855 if (index != -1) { 856 var valStart = index + myCookie.length; 857 var valEnd = document.cookie.indexOf(";", valStart); 858 if (valEnd == -1) { 859 valEnd = document.cookie.length; 860 } 861 var val = document.cookie.substring(valStart, valEnd); 862 return val; 863 } 864 } 865 return 0; 866} 867 868function writeCookie(cookie, val, section, expiration) { 869 if (val==undefined) return; 870 section = section == null ? "_" : "_"+section+"_"; 871 if (expiration == null) { 872 var date = new Date(); 873 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week 874 expiration = date.toGMTString(); 875 } 876 var cookieValue = cookie_namespace + section + cookie + "=" + val 877 + "; expires=" + expiration+"; path=/"; 878 document.cookie = cookieValue; 879} 880 881/* ######### END COOKIES! ########## */ 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907/* 908 909REMEMBER THE PREVIOUS PAGE FOR EACH TAB 910 911function loadLast(cookiePath) { 912 var location = window.location.href; 913 if (location.indexOf("/"+cookiePath+"/") != -1) { 914 return true; 915 } 916 var lastPage = readCookie(cookiePath + "_lastpage"); 917 if (lastPage) { 918 window.location = lastPage; 919 return false; 920 } 921 return true; 922} 923 924 925 926$(window).unload(function(){ 927 var path = getBaseUri(location.pathname); 928 if (path.indexOf("/reference/") != -1) { 929 writeCookie("lastpage", path, "reference", null); 930 } else if (path.indexOf("/guide/") != -1) { 931 writeCookie("lastpage", path, "guide", null); 932 } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) { 933 writeCookie("lastpage", path, "resources", null); 934 } 935}); 936 937*/ 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952function toggle(obj, slide) { 953 var ul = $("ul:first", obj); 954 var li = ul.parent(); 955 if (li.hasClass("closed")) { 956 if (slide) { 957 ul.slideDown("fast"); 958 } else { 959 ul.show(); 960 } 961 li.removeClass("closed"); 962 li.addClass("open"); 963 $(".toggle-img", li).attr("title", "hide pages"); 964 } else { 965 ul.slideUp("fast"); 966 li.removeClass("open"); 967 li.addClass("closed"); 968 $(".toggle-img", li).attr("title", "show pages"); 969 } 970} 971 972 973 974 975 976function buildToggleLists() { 977 $(".toggle-list").each( 978 function(i) { 979 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>"); 980 $(this).addClass("closed"); 981 }); 982} 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015/* REFERENCE NAV SWAP */ 1016 1017 1018function getNavPref() { 1019 var v = readCookie('reference_nav'); 1020 if (v != NAV_PREF_TREE) { 1021 v = NAV_PREF_PANELS; 1022 } 1023 return v; 1024} 1025 1026function chooseDefaultNav() { 1027 nav_pref = getNavPref(); 1028 if (nav_pref == NAV_PREF_TREE) { 1029 $("#nav-panels").toggle(); 1030 $("#panel-link").toggle(); 1031 $("#nav-tree").toggle(); 1032 $("#tree-link").toggle(); 1033 } 1034} 1035 1036function swapNav() { 1037 if (nav_pref == NAV_PREF_TREE) { 1038 nav_pref = NAV_PREF_PANELS; 1039 } else { 1040 nav_pref = NAV_PREF_TREE; 1041 init_default_navtree(toRoot); 1042 } 1043 var date = new Date(); 1044 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years 1045 writeCookie("nav", nav_pref, "reference", date.toGMTString()); 1046 1047 $("#nav-panels").toggle(); 1048 $("#panel-link").toggle(); 1049 $("#nav-tree").toggle(); 1050 $("#tree-link").toggle(); 1051 1052 resizeNav(); 1053 1054 // Gross nasty hack to make tree view show up upon first swap by setting height manually 1055 $("#nav-tree .jspContainer:visible") 1056 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'}); 1057 // Another nasty hack to make the scrollbar appear now that we have height 1058 resizeNav(); 1059 1060 if ($("#nav-tree").is(':visible')) { 1061 scrollIntoView("nav-tree"); 1062 } else { 1063 scrollIntoView("packages-nav"); 1064 scrollIntoView("classes-nav"); 1065 } 1066} 1067 1068 1069 1070/* ############################################ */ 1071/* ########## LOCALIZATION ############ */ 1072/* ############################################ */ 1073 1074function getBaseUri(uri) { 1075 var intlUrl = (uri.substring(0,6) == "/intl/"); 1076 if (intlUrl) { 1077 base = uri.substring(uri.indexOf('intl/')+5,uri.length); 1078 base = base.substring(base.indexOf('/')+1, base.length); 1079 //alert("intl, returning base url: /" + base); 1080 return ("/" + base); 1081 } else { 1082 //alert("not intl, returning uri as found."); 1083 return uri; 1084 } 1085} 1086 1087function requestAppendHL(uri) { 1088//append "?hl=<lang> to an outgoing request (such as to blog) 1089 var lang = getLangPref(); 1090 if (lang) { 1091 var q = 'hl=' + lang; 1092 uri += '?' + q; 1093 window.location = uri; 1094 return false; 1095 } else { 1096 return true; 1097 } 1098} 1099 1100 1101function changeNavLang(lang) { 1102 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]"); 1103 $links.each(function(i){ // for each link with a translation 1104 var $link = $(this); 1105 if (lang != "en") { // No need to worry about English, because a language change invokes new request 1106 // put the desired language from the attribute as the text 1107 $link.text($link.attr(lang+"-lang")) 1108 } 1109 }); 1110} 1111 1112function changeLangPref(lang, submit) { 1113 var date = new Date(); 1114 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); 1115 // keep this for 50 years 1116 //alert("expires: " + expires) 1117 writeCookie("pref_lang", lang, null, expires); 1118 1119 // ####### TODO: Remove this condition once we're stable on devsite ####### 1120 // This condition is only needed if we still need to support legacy GAE server 1121 if (devsite) { 1122 // Switch language when on Devsite server 1123 if (submit) { 1124 $("#setlang").submit(); 1125 } 1126 } else { 1127 // Switch language when on legacy GAE server 1128 if (submit) { 1129 window.location = getBaseUri(location.pathname); 1130 } 1131 } 1132} 1133 1134function loadLangPref() { 1135 var lang = readCookie("pref_lang"); 1136 if (lang != 0) { 1137 $("#language").find("option[value='"+lang+"']").attr("selected",true); 1138 } 1139} 1140 1141function getLangPref() { 1142 var lang = $("#language").find(":selected").attr("value"); 1143 if (!lang) { 1144 lang = readCookie("pref_lang"); 1145 } 1146 return (lang != 0) ? lang : 'en'; 1147} 1148 1149/* ########## END LOCALIZATION ############ */ 1150 1151 1152 1153 1154 1155 1156/* Used to hide and reveal supplemental content, such as long code samples. 1157 See the companion CSS in android-developer-docs.css */ 1158function toggleContent(obj) { 1159 var div = $(obj.parentNode.parentNode); 1160 var toggleMe = $(".toggle-content-toggleme",div); 1161 if (div.hasClass("closed")) { // if it's closed, open it 1162 toggleMe.slideDown(); 1163 $(".toggle-content-text", obj).toggle(); 1164 div.removeClass("closed").addClass("open"); 1165 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot 1166 + "assets/images/triangle-opened.png"); 1167 } else { // if it's open, close it 1168 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow 1169 $(".toggle-content-text", obj).toggle(); 1170 div.removeClass("open").addClass("closed"); 1171 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot 1172 + "assets/images/triangle-closed.png"); 1173 }); 1174 } 1175 return false; 1176} 1177 1178 1179/* New version of expandable content */ 1180function toggleExpandable(link,id) { 1181 if($(id).is(':visible')) { 1182 $(id).slideUp(); 1183 $(link).removeClass('expanded'); 1184 } else { 1185 $(id).slideDown(); 1186 $(link).addClass('expanded'); 1187 } 1188} 1189 1190function hideExpandable(ids) { 1191 $(ids).slideUp(); 1192 $(ids).prev('h4').find('a.expandable').removeClass('expanded'); 1193} 1194 1195 1196 1197 1198 1199/* 1200 * Slideshow 1.0 1201 * Used on /index.html and /develop/index.html for carousel 1202 * 1203 * Sample usage: 1204 * HTML - 1205 * <div class="slideshow-container"> 1206 * <a href="" class="slideshow-prev">Prev</a> 1207 * <a href="" class="slideshow-next">Next</a> 1208 * <ul> 1209 * <li class="item"><img src="images/marquee1.jpg"></li> 1210 * <li class="item"><img src="images/marquee2.jpg"></li> 1211 * <li class="item"><img src="images/marquee3.jpg"></li> 1212 * <li class="item"><img src="images/marquee4.jpg"></li> 1213 * </ul> 1214 * </div> 1215 * 1216 * <script type="text/javascript"> 1217 * $('.slideshow-container').dacSlideshow({ 1218 * auto: true, 1219 * btnPrev: '.slideshow-prev', 1220 * btnNext: '.slideshow-next' 1221 * }); 1222 * </script> 1223 * 1224 * Options: 1225 * btnPrev: optional identifier for previous button 1226 * btnNext: optional identifier for next button 1227 * btnPause: optional identifier for pause button 1228 * auto: whether or not to auto-proceed 1229 * speed: animation speed 1230 * autoTime: time between auto-rotation 1231 * easing: easing function for transition 1232 * start: item to select by default 1233 * scroll: direction to scroll in 1234 * pagination: whether or not to include dotted pagination 1235 * 1236 */ 1237 1238 (function($) { 1239 $.fn.dacSlideshow = function(o) { 1240 1241 //Options - see above 1242 o = $.extend({ 1243 btnPrev: null, 1244 btnNext: null, 1245 btnPause: null, 1246 auto: true, 1247 speed: 500, 1248 autoTime: 12000, 1249 easing: null, 1250 start: 0, 1251 scroll: 1, 1252 pagination: true 1253 1254 }, o || {}); 1255 1256 //Set up a carousel for each 1257 return this.each(function() { 1258 1259 var running = false; 1260 var animCss = o.vertical ? "top" : "left"; 1261 var sizeCss = o.vertical ? "height" : "width"; 1262 var div = $(this); 1263 var ul = $("ul", div); 1264 var tLi = $("li", ul); 1265 var tl = tLi.size(); 1266 var timer = null; 1267 1268 var li = $("li", ul); 1269 var itemLength = li.size(); 1270 var curr = o.start; 1271 1272 li.css({float: o.vertical ? "none" : "left"}); 1273 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"}); 1274 div.css({position: "relative", "z-index": "2", left: "0px"}); 1275 1276 var liSize = o.vertical ? height(li) : width(li); 1277 var ulSize = liSize * itemLength; 1278 var divSize = liSize; 1279 1280 li.css({width: li.width(), height: li.height()}); 1281 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize)); 1282 1283 div.css(sizeCss, divSize+"px"); 1284 1285 //Pagination 1286 if (o.pagination) { 1287 var pagination = $("<div class='pagination'></div>"); 1288 var pag_ul = $("<ul></ul>"); 1289 if (tl > 1) { 1290 for (var i=0;i<tl;i++) { 1291 var li = $("<li>"+i+"</li>"); 1292 pag_ul.append(li); 1293 if (i==o.start) li.addClass('active'); 1294 li.click(function() { 1295 go(parseInt($(this).text())); 1296 }) 1297 } 1298 pagination.append(pag_ul); 1299 div.append(pagination); 1300 } 1301 } 1302 1303 //Previous button 1304 if(o.btnPrev) 1305 $(o.btnPrev).click(function(e) { 1306 e.preventDefault(); 1307 return go(curr-o.scroll); 1308 }); 1309 1310 //Next button 1311 if(o.btnNext) 1312 $(o.btnNext).click(function(e) { 1313 e.preventDefault(); 1314 return go(curr+o.scroll); 1315 }); 1316 1317 //Pause button 1318 if(o.btnPause) 1319 $(o.btnPause).click(function(e) { 1320 e.preventDefault(); 1321 if ($(this).hasClass('paused')) { 1322 startRotateTimer(); 1323 } else { 1324 pauseRotateTimer(); 1325 } 1326 }); 1327 1328 //Auto rotation 1329 if(o.auto) startRotateTimer(); 1330 1331 function startRotateTimer() { 1332 clearInterval(timer); 1333 timer = setInterval(function() { 1334 if (curr == tl-1) { 1335 go(0); 1336 } else { 1337 go(curr+o.scroll); 1338 } 1339 }, o.autoTime); 1340 $(o.btnPause).removeClass('paused'); 1341 } 1342 1343 function pauseRotateTimer() { 1344 clearInterval(timer); 1345 $(o.btnPause).addClass('paused'); 1346 } 1347 1348 //Go to an item 1349 function go(to) { 1350 if(!running) { 1351 1352 if(to<0) { 1353 to = itemLength-1; 1354 } else if (to>itemLength-1) { 1355 to = 0; 1356 } 1357 curr = to; 1358 1359 running = true; 1360 1361 ul.animate( 1362 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing, 1363 function() { 1364 running = false; 1365 } 1366 ); 1367 1368 $(o.btnPrev + "," + o.btnNext).removeClass("disabled"); 1369 $( (curr-o.scroll<0 && o.btnPrev) 1370 || 1371 (curr+o.scroll > itemLength && o.btnNext) 1372 || 1373 [] 1374 ).addClass("disabled"); 1375 1376 1377 var nav_items = $('li', pagination); 1378 nav_items.removeClass('active'); 1379 nav_items.eq(to).addClass('active'); 1380 1381 1382 } 1383 if(o.auto) startRotateTimer(); 1384 return false; 1385 }; 1386 }); 1387 }; 1388 1389 function css(el, prop) { 1390 return parseInt($.css(el[0], prop)) || 0; 1391 }; 1392 function width(el) { 1393 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight'); 1394 }; 1395 function height(el) { 1396 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom'); 1397 }; 1398 1399 })(jQuery); 1400 1401 1402/* 1403 * dacSlideshow 1.0 1404 * Used on develop/index.html for side-sliding tabs 1405 * 1406 * Sample usage: 1407 * HTML - 1408 * <div class="slideshow-container"> 1409 * <a href="" class="slideshow-prev">Prev</a> 1410 * <a href="" class="slideshow-next">Next</a> 1411 * <ul> 1412 * <li class="item"><img src="images/marquee1.jpg"></li> 1413 * <li class="item"><img src="images/marquee2.jpg"></li> 1414 * <li class="item"><img src="images/marquee3.jpg"></li> 1415 * <li class="item"><img src="images/marquee4.jpg"></li> 1416 * </ul> 1417 * </div> 1418 * 1419 * <script type="text/javascript"> 1420 * $('.slideshow-container').dacSlideshow({ 1421 * auto: true, 1422 * btnPrev: '.slideshow-prev', 1423 * btnNext: '.slideshow-next' 1424 * }); 1425 * </script> 1426 * 1427 * Options: 1428 * btnPrev: optional identifier for previous button 1429 * btnNext: optional identifier for next button 1430 * auto: whether or not to auto-proceed 1431 * speed: animation speed 1432 * autoTime: time between auto-rotation 1433 * easing: easing function for transition 1434 * start: item to select by default 1435 * scroll: direction to scroll in 1436 * pagination: whether or not to include dotted pagination 1437 * 1438 */ 1439 (function($) { 1440 $.fn.dacTabbedList = function(o) { 1441 1442 //Options - see above 1443 o = $.extend({ 1444 speed : 250, 1445 easing: null, 1446 nav_id: null, 1447 frame_id: null 1448 }, o || {}); 1449 1450 //Set up a carousel for each 1451 return this.each(function() { 1452 1453 var curr = 0; 1454 var running = false; 1455 var animCss = "margin-left"; 1456 var sizeCss = "width"; 1457 var div = $(this); 1458 1459 var nav = $(o.nav_id, div); 1460 var nav_li = $("li", nav); 1461 var nav_size = nav_li.size(); 1462 var frame = div.find(o.frame_id); 1463 var content_width = $(frame).find('ul').width(); 1464 //Buttons 1465 $(nav_li).click(function(e) { 1466 go($(nav_li).index($(this))); 1467 }) 1468 1469 //Go to an item 1470 function go(to) { 1471 if(!running) { 1472 curr = to; 1473 running = true; 1474 1475 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing, 1476 function() { 1477 running = false; 1478 } 1479 ); 1480 1481 1482 nav_li.removeClass('active'); 1483 nav_li.eq(to).addClass('active'); 1484 1485 1486 } 1487 return false; 1488 }; 1489 }); 1490 }; 1491 1492 function css(el, prop) { 1493 return parseInt($.css(el[0], prop)) || 0; 1494 }; 1495 function width(el) { 1496 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight'); 1497 }; 1498 function height(el) { 1499 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom'); 1500 }; 1501 1502 })(jQuery); 1503 1504 1505 1506 1507 1508/* ######################################################## */ 1509/* ################ SEARCH SUGGESTIONS ################## */ 1510/* ######################################################## */ 1511 1512 1513 1514var gSelectedIndex = -1; // the index position of currently highlighted suggestion 1515var gSelectedColumn = -1; // which column of suggestion lists is currently focused 1516 1517var gMatches = new Array(); 1518var gLastText = ""; 1519var gInitialized = false; 1520var ROW_COUNT_FRAMEWORK = 20; // max number of results in list 1521var gListLength = 0; 1522 1523 1524var gGoogleMatches = new Array(); 1525var ROW_COUNT_GOOGLE = 15; // max number of results in list 1526var gGoogleListLength = 0; 1527 1528var gDocsMatches = new Array(); 1529var ROW_COUNT_DOCS = 100; // max number of results in list 1530var gDocsListLength = 0; 1531 1532function onSuggestionClick(link) { 1533 // When user clicks a suggested document, track it 1534 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(), 1535 'from: ' + $("#search_autocomplete").val()]); 1536} 1537 1538function set_item_selected($li, selected) 1539{ 1540 if (selected) { 1541 $li.attr('class','jd-autocomplete jd-selected'); 1542 } else { 1543 $li.attr('class','jd-autocomplete'); 1544 } 1545} 1546 1547function set_item_values(toroot, $li, match) 1548{ 1549 var $link = $('a',$li); 1550 $link.html(match.__hilabel || match.label); 1551 $link.attr('href',toroot + match.link); 1552} 1553 1554function new_suggestion($list) { 1555 var $li = $("<li class='jd-autocomplete'></li>"); 1556 $list.append($li); 1557 1558 $li.mousedown(function() { 1559 window.location = this.firstChild.getAttribute("href"); 1560 }); 1561 $li.mouseover(function() { 1562 $('.search_filtered_wrapper li').removeClass('jd-selected'); 1563 $(this).addClass('jd-selected'); 1564 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered')); 1565 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this); 1566 }); 1567 $li.append("<a onclick='onSuggestionClick(this)'></a>"); 1568 $li.attr('class','show-item'); 1569 return $li; 1570} 1571 1572function sync_selection_table(toroot) 1573{ 1574 var $li; //list item jquery object 1575 var i; //list item iterator 1576 1577 // if there are NO results at all, hide all columns 1578 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) { 1579 $('.suggest-card').hide(300); 1580 return; 1581 } 1582 1583 // if there are api results 1584 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) { 1585 // reveal suggestion list 1586 $('.suggest-card.dummy').show(); 1587 $('.suggest-card.reference').show(); 1588 var listIndex = 0; // list index position 1589 1590 // reset the lists 1591 $(".search_filtered_wrapper.reference li").remove(); 1592 1593 // ########### ANDROID RESULTS ############# 1594 if (gMatches.length > 0) { 1595 1596 // determine android results to show 1597 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ? 1598 gMatches.length : ROW_COUNT_FRAMEWORK; 1599 for (i=0; i<gListLength; i++) { 1600 var $li = new_suggestion($(".suggest-card.reference ul")); 1601 set_item_values(toroot, $li, gMatches[i]); 1602 set_item_selected($li, i == gSelectedIndex); 1603 } 1604 } 1605 1606 // ########### GOOGLE RESULTS ############# 1607 if (gGoogleMatches.length > 0) { 1608 // show header for list 1609 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>"); 1610 1611 // determine google results to show 1612 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE; 1613 for (i=0; i<gGoogleListLength; i++) { 1614 var $li = new_suggestion($(".suggest-card.reference ul")); 1615 set_item_values(toroot, $li, gGoogleMatches[i]); 1616 set_item_selected($li, i == gSelectedIndex); 1617 } 1618 } 1619 } else { 1620 $('.suggest-card.reference').hide(); 1621 $('.suggest-card.dummy').hide(); 1622 } 1623 1624 // ########### JD DOC RESULTS ############# 1625 if (gDocsMatches.length > 0) { 1626 // reset the lists 1627 $(".search_filtered_wrapper.docs li").remove(); 1628 1629 // determine google results to show 1630 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS; 1631 for (i=0; i<gDocsListLength; i++) { 1632 var sugg = gDocsMatches[i]; 1633 var $li; 1634 if (sugg.type == "design") { 1635 $li = new_suggestion($(".suggest-card.design ul")); 1636 } else 1637 if (sugg.type == "distribute") { 1638 $li = new_suggestion($(".suggest-card.distribute ul")); 1639 } else 1640 if (sugg.type == "training") { 1641 $li = new_suggestion($(".suggest-card.develop .child-card.training")); 1642 } else 1643 if (sugg.type == "guide"||"google") { 1644 $li = new_suggestion($(".suggest-card.develop .child-card.guides")); 1645 } else { 1646 continue; 1647 } 1648 1649 set_item_values(toroot, $li, sugg); 1650 set_item_selected($li, i == gSelectedIndex); 1651 } 1652 1653 // add heading and show or hide card 1654 if ($(".suggest-card.design li").length > 0) { 1655 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>"); 1656 $(".suggest-card.design").show(300); 1657 } else { 1658 $('.suggest-card.design').hide(300); 1659 } 1660 if ($(".suggest-card.distribute li").length > 0) { 1661 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>"); 1662 $(".suggest-card.distribute").show(300); 1663 } else { 1664 $('.suggest-card.distribute').hide(300); 1665 } 1666 if ($(".child-card.guides li").length > 0) { 1667 $(".child-card.guides").prepend("<li class='header'>Guides:</li>"); 1668 $(".child-card.guides li").appendTo(".suggest-card.develop ul"); 1669 } 1670 if ($(".child-card.training li").length > 0) { 1671 $(".child-card.training").prepend("<li class='header'>Training:</li>"); 1672 $(".child-card.training li").appendTo(".suggest-card.develop ul"); 1673 } 1674 1675 if ($(".suggest-card.develop li").length > 0) { 1676 $(".suggest-card.develop").show(300); 1677 } else { 1678 $('.suggest-card.develop').hide(300); 1679 } 1680 1681 } else { 1682 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300); 1683 } 1684} 1685 1686/** Called by the search input's onkeydown and onkeyup events. 1687 * Handles navigation with keyboard arrows, Enter key to invoke search, 1688 * otherwise invokes search suggestions on key-up event. 1689 * @param e The JS event 1690 * @param kd True if the event is key-down 1691 * @param toroot A string for the site's root path 1692 * @returns True if the event should bubble up 1693 */ 1694function search_changed(e, kd, toroot) 1695{ 1696 var search = document.getElementById("search_autocomplete"); 1697 var text = search.value.replace(/(^ +)|( +$)/g, ''); 1698 // get the ul hosting the currently selected item 1699 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0; 1700 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible"); 1701 var $selectedUl = $columns[gSelectedColumn]; 1702 1703 // show/hide the close button 1704 if (text != '') { 1705 $(".search .close").removeClass("hide"); 1706 } else { 1707 $(".search .close").addClass("hide"); 1708 } 1709 // 27 = esc 1710 if (e.keyCode == 27) { 1711 // close all search results 1712 if (kd) $('.search .close').trigger('click'); 1713 return true; 1714 } 1715 // 13 = enter 1716 else if (e.keyCode == 13) { 1717 if (gSelectedIndex < 0) { 1718 $('.suggest-card').hide(); 1719 if ($("#searchResults").is(":hidden") && (search.value != "")) { 1720 // if results aren't showing (and text not empty), return true to allow search to execute 1721 return true; 1722 } else { 1723 // otherwise, results are already showing, so allow ajax to auto refresh the results 1724 // and ignore this Enter press to avoid the reload. 1725 return false; 1726 } 1727 } else if (kd && gSelectedIndex >= 0) { 1728 // click the link corresponding to selected item 1729 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click(); 1730 return false; 1731 } 1732 } 1733 // Stop here if Google results are showing 1734 else if ($("#searchResults").is(":visible")) { 1735 return true; 1736 } 1737 // 38 UP ARROW 1738 else if (kd && (e.keyCode == 38)) { 1739 // if the next item is a header, skip it 1740 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) { 1741 gSelectedIndex--; 1742 } 1743 if (gSelectedIndex >= 0) { 1744 $('li', $selectedUl).removeClass('jd-selected'); 1745 gSelectedIndex--; 1746 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1747 // If user reaches top, reset selected column 1748 if (gSelectedIndex < 0) { 1749 gSelectedColumn = -1; 1750 } 1751 } 1752 return false; 1753 } 1754 // 40 DOWN ARROW 1755 else if (kd && (e.keyCode == 40)) { 1756 // if the next item is a header, skip it 1757 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) { 1758 gSelectedIndex++; 1759 } 1760 if ((gSelectedIndex < $("li", $selectedUl).length-1) || 1761 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) { 1762 $('li', $selectedUl).removeClass('jd-selected'); 1763 gSelectedIndex++; 1764 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1765 } 1766 return false; 1767 } 1768 // Consider left/right arrow navigation 1769 // NOTE: Order of suggest columns are reverse order (index position 0 is on right) 1770 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) { 1771 // 37 LEFT ARROW 1772 // go left only if current column is not left-most column (last column) 1773 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) { 1774 $('li', $selectedUl).removeClass('jd-selected'); 1775 gSelectedColumn++; 1776 $selectedUl = $columns[gSelectedColumn]; 1777 // keep or reset the selected item to last item as appropriate 1778 gSelectedIndex = gSelectedIndex > 1779 $("li", $selectedUl).length-1 ? 1780 $("li", $selectedUl).length-1 : gSelectedIndex; 1781 // if the corresponding item is a header, move down 1782 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) { 1783 gSelectedIndex++; 1784 } 1785 // set item selected 1786 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1787 return false; 1788 } 1789 // 39 RIGHT ARROW 1790 // go right only if current column is not the right-most column (first column) 1791 else if (e.keyCode == 39 && gSelectedColumn > 0) { 1792 $('li', $selectedUl).removeClass('jd-selected'); 1793 gSelectedColumn--; 1794 $selectedUl = $columns[gSelectedColumn]; 1795 // keep or reset the selected item to last item as appropriate 1796 gSelectedIndex = gSelectedIndex > 1797 $("li", $selectedUl).length-1 ? 1798 $("li", $selectedUl).length-1 : gSelectedIndex; 1799 // if the corresponding item is a header, move down 1800 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) { 1801 gSelectedIndex++; 1802 } 1803 // set item selected 1804 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1805 return false; 1806 } 1807 } 1808 1809 // if key-up event and not arrow down/up, 1810 // read the search query and add suggestsions to gMatches 1811 else if (!kd && (e.keyCode != 40) 1812 && (e.keyCode != 38) 1813 && (e.keyCode != 37) 1814 && (e.keyCode != 39)) { 1815 gSelectedIndex = -1; 1816 gMatches = new Array(); 1817 matchedCount = 0; 1818 gGoogleMatches = new Array(); 1819 matchedCountGoogle = 0; 1820 gDocsMatches = new Array(); 1821 matchedCountDocs = 0; 1822 1823 // Search for Android matches 1824 for (var i=0; i<DATA.length; i++) { 1825 var s = DATA[i]; 1826 if (text.length != 0 && 1827 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) { 1828 gMatches[matchedCount] = s; 1829 matchedCount++; 1830 } 1831 } 1832 rank_autocomplete_api_results(text, gMatches); 1833 for (var i=0; i<gMatches.length; i++) { 1834 var s = gMatches[i]; 1835 } 1836 1837 1838 // Search for Google matches 1839 for (var i=0; i<GOOGLE_DATA.length; i++) { 1840 var s = GOOGLE_DATA[i]; 1841 if (text.length != 0 && 1842 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) { 1843 gGoogleMatches[matchedCountGoogle] = s; 1844 matchedCountGoogle++; 1845 } 1846 } 1847 rank_autocomplete_api_results(text, gGoogleMatches); 1848 for (var i=0; i<gGoogleMatches.length; i++) { 1849 var s = gGoogleMatches[i]; 1850 } 1851 1852 highlight_autocomplete_result_labels(text); 1853 1854 1855 1856 // Search for JD docs 1857 if (text.length >= 3) { 1858 for (var i=0; i<JD_DATA.length; i++) { 1859 // Regex to match only the beginning of a word 1860 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g"); 1861 // current search comparison, with counters for tag and title, 1862 // used later to improve ranking 1863 var s = JD_DATA[i]; 1864 s.matched_tag = 0; 1865 s.matched_title = 0; 1866 var matched = false; 1867 1868 // Check if query matches any tags; work backwards toward 1 to assist ranking 1869 for (var j = s.tags.length - 1; j >= 0; j--) { 1870 // it matches a tag 1871 if (s.tags[j].toLowerCase().match(textRegex)) { 1872 matched = true; 1873 s.matched_tag = j + 1; // add 1 to index position 1874 } 1875 } 1876 // Don't consider doc title for lessons (only for class landing pages) 1877 // ...it is not a training lesson (or is but has matched a tag) 1878 if (!(s.type == "training" && s.link.indexOf("index.html") == -1) || matched) { 1879 // it matches the doc title 1880 if (s.label.toLowerCase().match(textRegex)) { 1881 matched = true; 1882 s.matched_title = 1; 1883 } 1884 } 1885 if (matched) { 1886 gDocsMatches[matchedCountDocs] = s; 1887 matchedCountDocs++; 1888 } 1889 } 1890 rank_autocomplete_doc_results(text, gDocsMatches); 1891 } 1892 1893 // draw the suggestions 1894 sync_selection_table(toroot); 1895 return true; // allow the event to bubble up to the search api 1896 } 1897} 1898 1899/* Order the jd doc result list based on match quality */ 1900function rank_autocomplete_doc_results(query, matches) { 1901 query = query || ''; 1902 if (!matches || !matches.length) 1903 return; 1904 1905 var _resultScoreFn = function(match) { 1906 var score = 1.0; 1907 1908 // if the query matched a tag 1909 if (match.matched_tag > 0) { 1910 // multiply score by factor relative to position in tags list (max of 3) 1911 score *= 3 / match.matched_tag; 1912 1913 // if it also matched the title 1914 if (match.matched_title > 0) { 1915 score *= 2; 1916 } 1917 } else if (match.matched_title > 0) { 1918 score *= 3; 1919 } 1920 1921 return score; 1922 }; 1923 1924 for (var i=0; i<matches.length; i++) { 1925 matches[i].__resultScore = _resultScoreFn(matches[i]); 1926 } 1927 1928 matches.sort(function(a,b){ 1929 var n = b.__resultScore - a.__resultScore; 1930 if (n == 0) // lexicographical sort if scores are the same 1931 n = (a.label < b.label) ? -1 : 1; 1932 return n; 1933 }); 1934} 1935 1936/* Order the result list based on match quality */ 1937function rank_autocomplete_api_results(query, matches) { 1938 query = query || ''; 1939 if (!matches || !matches.length) 1940 return; 1941 1942 // helper function that gets the last occurence index of the given regex 1943 // in the given string, or -1 if not found 1944 var _lastSearch = function(s, re) { 1945 if (s == '') 1946 return -1; 1947 var l = -1; 1948 var tmp; 1949 while ((tmp = s.search(re)) >= 0) { 1950 if (l < 0) l = 0; 1951 l += tmp; 1952 s = s.substr(tmp + 1); 1953 } 1954 return l; 1955 }; 1956 1957 // helper function that counts the occurrences of a given character in 1958 // a given string 1959 var _countChar = function(s, c) { 1960 var n = 0; 1961 for (var i=0; i<s.length; i++) 1962 if (s.charAt(i) == c) ++n; 1963 return n; 1964 }; 1965 1966 var queryLower = query.toLowerCase(); 1967 var queryAlnum = (queryLower.match(/\w+/) || [''])[0]; 1968 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum); 1969 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b'); 1970 1971 var _resultScoreFn = function(result) { 1972 // scores are calculated based on exact and prefix matches, 1973 // and then number of path separators (dots) from the last 1974 // match (i.e. favoring classes and deep package names) 1975 var score = 1.0; 1976 var labelLower = result.label.toLowerCase(); 1977 var t; 1978 t = _lastSearch(labelLower, partExactAlnumRE); 1979 if (t >= 0) { 1980 // exact part match 1981 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 1982 score *= 200 / (partsAfter + 1); 1983 } else { 1984 t = _lastSearch(labelLower, partPrefixAlnumRE); 1985 if (t >= 0) { 1986 // part prefix match 1987 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 1988 score *= 20 / (partsAfter + 1); 1989 } 1990 } 1991 1992 return score; 1993 }; 1994 1995 for (var i=0; i<matches.length; i++) { 1996 // if the API is deprecated, default score is 0; otherwise, perform scoring 1997 if (matches[i].deprecated == "true") { 1998 matches[i].__resultScore = 0; 1999 } else { 2000 matches[i].__resultScore = _resultScoreFn(matches[i]); 2001 } 2002 } 2003 2004 matches.sort(function(a,b){ 2005 var n = b.__resultScore - a.__resultScore; 2006 if (n == 0) // lexicographical sort if scores are the same 2007 n = (a.label < b.label) ? -1 : 1; 2008 return n; 2009 }); 2010} 2011 2012/* Add emphasis to part of string that matches query */ 2013function highlight_autocomplete_result_labels(query) { 2014 query = query || ''; 2015 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length)) 2016 return; 2017 2018 var queryLower = query.toLowerCase(); 2019 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0]; 2020 var queryRE = new RegExp( 2021 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig'); 2022 for (var i=0; i<gMatches.length; i++) { 2023 gMatches[i].__hilabel = gMatches[i].label.replace( 2024 queryRE, '<b>$1</b>'); 2025 } 2026 for (var i=0; i<gGoogleMatches.length; i++) { 2027 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace( 2028 queryRE, '<b>$1</b>'); 2029 } 2030} 2031 2032function search_focus_changed(obj, focused) 2033{ 2034 if (!focused) { 2035 if(obj.value == ""){ 2036 $(".search .close").addClass("hide"); 2037 } 2038 $(".suggest-card").hide(); 2039 } 2040} 2041 2042function submit_search() { 2043 var query = document.getElementById('search_autocomplete').value; 2044 location.hash = 'q=' + query; 2045 loadSearchResults(); 2046 $("#searchResults").slideDown('slow'); 2047 return false; 2048} 2049 2050 2051function hideResults() { 2052 $("#searchResults").slideUp(); 2053 $(".search .close").addClass("hide"); 2054 location.hash = ''; 2055 2056 $("#search_autocomplete").val("").blur(); 2057 2058 // reset the ajax search callback to nothing, so results don't appear unless ENTER 2059 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {}); 2060 2061 // forcefully regain key-up event control (previously jacked by search api) 2062 $("#search_autocomplete").keyup(function(event) { 2063 return search_changed(event, false, toRoot); 2064 }); 2065 2066 return false; 2067} 2068 2069 2070 2071/* ########################################################## */ 2072/* ################ CUSTOM SEARCH ENGINE ################## */ 2073/* ########################################################## */ 2074 2075var searchControl; 2076google.load('search', '1', {"callback" : function() { 2077 searchControl = new google.search.SearchControl(); 2078 } }); 2079 2080function loadSearchResults() { 2081 document.getElementById("search_autocomplete").style.color = "#000"; 2082 2083 searchControl = new google.search.SearchControl(); 2084 2085 // use our existing search form and use tabs when multiple searchers are used 2086 drawOptions = new google.search.DrawOptions(); 2087 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED); 2088 drawOptions.setInput(document.getElementById("search_autocomplete")); 2089 2090 // configure search result options 2091 searchOptions = new google.search.SearcherOptions(); 2092 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN); 2093 2094 // configure each of the searchers, for each tab 2095 devSiteSearcher = new google.search.WebSearch(); 2096 devSiteSearcher.setUserDefinedLabel("All"); 2097 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u"); 2098 2099 designSearcher = new google.search.WebSearch(); 2100 designSearcher.setUserDefinedLabel("Design"); 2101 designSearcher.setSiteRestriction("http://developer.android.com/design/"); 2102 2103 trainingSearcher = new google.search.WebSearch(); 2104 trainingSearcher.setUserDefinedLabel("Training"); 2105 trainingSearcher.setSiteRestriction("http://developer.android.com/training/"); 2106 2107 guidesSearcher = new google.search.WebSearch(); 2108 guidesSearcher.setUserDefinedLabel("Guides"); 2109 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/"); 2110 2111 referenceSearcher = new google.search.WebSearch(); 2112 referenceSearcher.setUserDefinedLabel("Reference"); 2113 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/"); 2114 2115 googleSearcher = new google.search.WebSearch(); 2116 googleSearcher.setUserDefinedLabel("Google Services"); 2117 googleSearcher.setSiteRestriction("http://developer.android.com/google/"); 2118 2119 blogSearcher = new google.search.WebSearch(); 2120 blogSearcher.setUserDefinedLabel("Blog"); 2121 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com"); 2122 2123 // add each searcher to the search control 2124 searchControl.addSearcher(devSiteSearcher, searchOptions); 2125 searchControl.addSearcher(designSearcher, searchOptions); 2126 searchControl.addSearcher(trainingSearcher, searchOptions); 2127 searchControl.addSearcher(guidesSearcher, searchOptions); 2128 searchControl.addSearcher(referenceSearcher, searchOptions); 2129 searchControl.addSearcher(googleSearcher, searchOptions); 2130 searchControl.addSearcher(blogSearcher, searchOptions); 2131 2132 // configure result options 2133 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET); 2134 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF); 2135 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT); 2136 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING); 2137 2138 // upon ajax search, refresh the url and search title 2139 searchControl.setSearchStartingCallback(this, function(control, searcher, query) { 2140 updateResultTitle(query); 2141 var query = document.getElementById('search_autocomplete').value; 2142 location.hash = 'q=' + query; 2143 }); 2144 2145 // once search results load, set up click listeners 2146 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) { 2147 addResultClickListeners(); 2148 }); 2149 2150 // draw the search results box 2151 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions); 2152 2153 // get query and execute the search 2154 searchControl.execute(decodeURI(getQuery(location.hash))); 2155 2156 document.getElementById("search_autocomplete").focus(); 2157 addTabListeners(); 2158} 2159// End of loadSearchResults 2160 2161 2162google.setOnLoadCallback(function(){ 2163 if (location.hash.indexOf("q=") == -1) { 2164 // if there's no query in the url, don't search and make sure results are hidden 2165 $('#searchResults').hide(); 2166 return; 2167 } else { 2168 // first time loading search results for this page 2169 $('#searchResults').slideDown('slow'); 2170 $(".search .close").removeClass("hide"); 2171 loadSearchResults(); 2172 } 2173}, true); 2174 2175// when an event on the browser history occurs (back, forward, load) requery hash and do search 2176$(window).hashchange( function(){ 2177 // Exit if the hash isn't a search query or there's an error in the query 2178 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) { 2179 // If the results pane is open, close it. 2180 if (!$("#searchResults").is(":hidden")) { 2181 hideResults(); 2182 } 2183 return; 2184 } 2185 2186 // Otherwise, we have a search to do 2187 var query = decodeURI(getQuery(location.hash)); 2188 searchControl.execute(query); 2189 $('#searchResults').slideDown('slow'); 2190 $("#search_autocomplete").focus(); 2191 $(".search .close").removeClass("hide"); 2192 2193 updateResultTitle(query); 2194}); 2195 2196function updateResultTitle(query) { 2197 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>"); 2198} 2199 2200// forcefully regain key-up event control (previously jacked by search api) 2201$("#search_autocomplete").keyup(function(event) { 2202 return search_changed(event, false, toRoot); 2203}); 2204 2205// add event listeners to each tab so we can track the browser history 2206function addTabListeners() { 2207 var tabHeaders = $(".gsc-tabHeader"); 2208 for (var i = 0; i < tabHeaders.length; i++) { 2209 $(tabHeaders[i]).attr("id",i).click(function() { 2210 /* 2211 // make a copy of the page numbers for the search left pane 2212 setTimeout(function() { 2213 // remove any residual page numbers 2214 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove(); 2215 // move the page numbers to the left position; make a clone, 2216 // because the element is drawn to the DOM only once 2217 // and because we're going to remove it (previous line), 2218 // we need it to be available to move again as the user navigates 2219 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible') 2220 .clone().appendTo('#searchResults .gsc-tabsArea'); 2221 }, 200); 2222 */ 2223 }); 2224 } 2225 setTimeout(function(){$(tabHeaders[0]).click()},200); 2226} 2227 2228// add analytics tracking events to each result link 2229function addResultClickListeners() { 2230 $("#searchResults a.gs-title").each(function(index, link) { 2231 // When user clicks enter for Google search results, track it 2232 $(link).click(function() { 2233 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(), 2234 'from: ' + $("#search_autocomplete").val()]); 2235 }); 2236 }); 2237} 2238 2239 2240function getQuery(hash) { 2241 var queryParts = hash.split('='); 2242 return queryParts[1]; 2243} 2244 2245/* returns the given string with all HTML brackets converted to entities 2246 TODO: move this to the site's JS library */ 2247function escapeHTML(string) { 2248 return string.replace(/</g,"<") 2249 .replace(/>/g,">"); 2250} 2251 2252 2253 2254 2255 2256 2257 2258/* ######################################################## */ 2259/* ################# JAVADOC REFERENCE ################### */ 2260/* ######################################################## */ 2261 2262/* Initialize some droiddoc stuff, but only if we're in the reference */ 2263if (location.pathname.indexOf("/reference")) { 2264 if(!location.pathname.indexOf("/reference-gms/packages.html") 2265 && !location.pathname.indexOf("/reference-gcm/packages.html") 2266 && !location.pathname.indexOf("/reference/com/google") == 0) { 2267 $(document).ready(function() { 2268 // init available apis based on user pref 2269 changeApiLevel(); 2270 initSidenavHeightResize() 2271 }); 2272 } 2273} 2274 2275var API_LEVEL_COOKIE = "api_level"; 2276var minLevel = 1; 2277var maxLevel = 1; 2278 2279/******* SIDENAV DIMENSIONS ************/ 2280 2281 function initSidenavHeightResize() { 2282 // Change the drag bar size to nicely fit the scrollbar positions 2283 var $dragBar = $(".ui-resizable-s"); 2284 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"}); 2285 2286 $( "#resize-packages-nav" ).resizable({ 2287 containment: "#nav-panels", 2288 handles: "s", 2289 alsoResize: "#packages-nav", 2290 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */ 2291 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */ 2292 }); 2293 2294 } 2295 2296function updateSidenavFixedWidth() { 2297 if (!navBarIsFixed) return; 2298 $('#devdoc-nav').css({ 2299 'width' : $('#side-nav').css('width'), 2300 'margin' : $('#side-nav').css('margin') 2301 }); 2302 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'}); 2303 2304 initSidenavHeightResize(); 2305} 2306 2307function updateSidenavFullscreenWidth() { 2308 if (!navBarIsFixed) return; 2309 $('#devdoc-nav').css({ 2310 'width' : $('#side-nav').css('width'), 2311 'margin' : $('#side-nav').css('margin') 2312 }); 2313 $('#devdoc-nav .totop').css({'left': 'inherit'}); 2314 2315 initSidenavHeightResize(); 2316} 2317 2318function buildApiLevelSelector() { 2319 maxLevel = SINCE_DATA.length; 2320 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE)); 2321 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default 2322 2323 minLevel = parseInt($("#doc-api-level").attr("class")); 2324 // Handle provisional api levels; the provisional level will always be the highest possible level 2325 // Provisional api levels will also have a length; other stuff that's just missing a level won't, 2326 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class) 2327 if (isNaN(minLevel) && minLevel.length) { 2328 minLevel = maxLevel; 2329 } 2330 var select = $("#apiLevelSelector").html("").change(changeApiLevel); 2331 for (var i = maxLevel-1; i >= 0; i--) { 2332 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]); 2333 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames) 2334 select.append(option); 2335 } 2336 2337 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true) 2338 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0); 2339 selectedLevelItem.setAttribute('selected',true); 2340} 2341 2342function changeApiLevel() { 2343 maxLevel = SINCE_DATA.length; 2344 var selectedLevel = maxLevel; 2345 2346 selectedLevel = parseInt($("#apiLevelSelector option:selected").val()); 2347 toggleVisisbleApis(selectedLevel, "body"); 2348 2349 var date = new Date(); 2350 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years 2351 var expiration = date.toGMTString(); 2352 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration); 2353 2354 if (selectedLevel < minLevel) { 2355 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class"; 2356 $("#naMessage").show().html("<div><p><strong>This " + thing 2357 + " requires API level " + minLevel + " or higher.</strong></p>" 2358 + "<p>This document is hidden because your selected API level for the documentation is " 2359 + selectedLevel + ". You can change the documentation API level with the selector " 2360 + "above the left navigation.</p>" 2361 + "<p>For more information about specifying the API level your app requires, " 2362 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'" 2363 + ">Supporting Different Platform Versions</a>.</p>" 2364 + "<input type='button' value='OK, make this page visible' " 2365 + "title='Change the API level to " + minLevel + "' " 2366 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />" 2367 + "</div>"); 2368 } else { 2369 $("#naMessage").hide(); 2370 } 2371} 2372 2373function toggleVisisbleApis(selectedLevel, context) { 2374 var apis = $(".api",context); 2375 apis.each(function(i) { 2376 var obj = $(this); 2377 var className = obj.attr("class"); 2378 var apiLevelIndex = className.lastIndexOf("-")+1; 2379 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex); 2380 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length; 2381 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex); 2382 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail 2383 return; 2384 } 2385 apiLevel = parseInt(apiLevel); 2386 2387 // Handle provisional api levels; if this item's level is the provisional one, set it to the max 2388 var selectedLevelNum = parseInt(selectedLevel) 2389 var apiLevelNum = parseInt(apiLevel); 2390 if (isNaN(apiLevelNum)) { 2391 apiLevelNum = maxLevel; 2392 } 2393 2394 // Grey things out that aren't available and give a tooltip title 2395 if (apiLevelNum > selectedLevelNum) { 2396 obj.addClass("absent").attr("title","Requires API Level \"" 2397 + apiLevel + "\" or higher"); 2398 } 2399 else obj.removeClass("absent").removeAttr("title"); 2400 }); 2401} 2402 2403 2404 2405 2406/* ################# SIDENAV TREE VIEW ################### */ 2407 2408function new_node(me, mom, text, link, children_data, api_level) 2409{ 2410 var node = new Object(); 2411 node.children = Array(); 2412 node.children_data = children_data; 2413 node.depth = mom.depth + 1; 2414 2415 node.li = document.createElement("li"); 2416 mom.get_children_ul().appendChild(node.li); 2417 2418 node.label_div = document.createElement("div"); 2419 node.label_div.className = "label"; 2420 if (api_level != null) { 2421 $(node.label_div).addClass("api"); 2422 $(node.label_div).addClass("api-level-"+api_level); 2423 } 2424 node.li.appendChild(node.label_div); 2425 2426 if (children_data != null) { 2427 node.expand_toggle = document.createElement("a"); 2428 node.expand_toggle.href = "javascript:void(0)"; 2429 node.expand_toggle.onclick = function() { 2430 if (node.expanded) { 2431 $(node.get_children_ul()).slideUp("fast"); 2432 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png"; 2433 node.expanded = false; 2434 } else { 2435 expand_node(me, node); 2436 } 2437 }; 2438 node.label_div.appendChild(node.expand_toggle); 2439 2440 node.plus_img = document.createElement("img"); 2441 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png"; 2442 node.plus_img.className = "plus"; 2443 node.plus_img.width = "8"; 2444 node.plus_img.border = "0"; 2445 node.expand_toggle.appendChild(node.plus_img); 2446 2447 node.expanded = false; 2448 } 2449 2450 var a = document.createElement("a"); 2451 node.label_div.appendChild(a); 2452 node.label = document.createTextNode(text); 2453 a.appendChild(node.label); 2454 if (link) { 2455 a.href = me.toroot + link; 2456 } else { 2457 if (children_data != null) { 2458 a.className = "nolink"; 2459 a.href = "javascript:void(0)"; 2460 a.onclick = node.expand_toggle.onclick; 2461 // This next line shouldn't be necessary. I'll buy a beer for the first 2462 // person who figures out how to remove this line and have the link 2463 // toggle shut on the first try. --joeo@android.com 2464 node.expanded = false; 2465 } 2466 } 2467 2468 2469 node.children_ul = null; 2470 node.get_children_ul = function() { 2471 if (!node.children_ul) { 2472 node.children_ul = document.createElement("ul"); 2473 node.children_ul.className = "children_ul"; 2474 node.children_ul.style.display = "none"; 2475 node.li.appendChild(node.children_ul); 2476 } 2477 return node.children_ul; 2478 }; 2479 2480 return node; 2481} 2482 2483 2484 2485 2486function expand_node(me, node) 2487{ 2488 if (node.children_data && !node.expanded) { 2489 if (node.children_visited) { 2490 $(node.get_children_ul()).slideDown("fast"); 2491 } else { 2492 get_node(me, node); 2493 if ($(node.label_div).hasClass("absent")) { 2494 $(node.get_children_ul()).addClass("absent"); 2495 } 2496 $(node.get_children_ul()).slideDown("fast"); 2497 } 2498 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png"; 2499 node.expanded = true; 2500 2501 // perform api level toggling because new nodes are new to the DOM 2502 var selectedLevel = $("#apiLevelSelector option:selected").val(); 2503 toggleVisisbleApis(selectedLevel, "#side-nav"); 2504 } 2505} 2506 2507function get_node(me, mom) 2508{ 2509 mom.children_visited = true; 2510 for (var i in mom.children_data) { 2511 var node_data = mom.children_data[i]; 2512 mom.children[i] = new_node(me, mom, node_data[0], node_data[1], 2513 node_data[2], node_data[3]); 2514 } 2515} 2516 2517function this_page_relative(toroot) 2518{ 2519 var full = document.location.pathname; 2520 var file = ""; 2521 if (toroot.substr(0, 1) == "/") { 2522 if (full.substr(0, toroot.length) == toroot) { 2523 return full.substr(toroot.length); 2524 } else { 2525 // the file isn't under toroot. Fail. 2526 return null; 2527 } 2528 } else { 2529 if (toroot != "./") { 2530 toroot = "./" + toroot; 2531 } 2532 do { 2533 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") { 2534 var pos = full.lastIndexOf("/"); 2535 file = full.substr(pos) + file; 2536 full = full.substr(0, pos); 2537 toroot = toroot.substr(0, toroot.length-3); 2538 } 2539 } while (toroot != "" && toroot != "/"); 2540 return file.substr(1); 2541 } 2542} 2543 2544function find_page(url, data) 2545{ 2546 var nodes = data; 2547 var result = null; 2548 for (var i in nodes) { 2549 var d = nodes[i]; 2550 if (d[1] == url) { 2551 return new Array(i); 2552 } 2553 else if (d[2] != null) { 2554 result = find_page(url, d[2]); 2555 if (result != null) { 2556 return (new Array(i).concat(result)); 2557 } 2558 } 2559 } 2560 return null; 2561} 2562 2563function init_default_navtree(toroot) { 2564 // load json file for navtree data 2565 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) { 2566 // when the file is loaded, initialize the tree 2567 if(jqxhr.status === 200) { 2568 init_navtree("tree-list", toroot, NAVTREE_DATA); 2569 } 2570 }); 2571 2572 // perform api level toggling because because the whole tree is new to the DOM 2573 var selectedLevel = $("#apiLevelSelector option:selected").val(); 2574 toggleVisisbleApis(selectedLevel, "#side-nav"); 2575} 2576 2577function init_navtree(navtree_id, toroot, root_nodes) 2578{ 2579 var me = new Object(); 2580 me.toroot = toroot; 2581 me.node = new Object(); 2582 2583 me.node.li = document.getElementById(navtree_id); 2584 me.node.children_data = root_nodes; 2585 me.node.children = new Array(); 2586 me.node.children_ul = document.createElement("ul"); 2587 me.node.get_children_ul = function() { return me.node.children_ul; }; 2588 //me.node.children_ul.className = "children_ul"; 2589 me.node.li.appendChild(me.node.children_ul); 2590 me.node.depth = 0; 2591 2592 get_node(me, me.node); 2593 2594 me.this_page = this_page_relative(toroot); 2595 me.breadcrumbs = find_page(me.this_page, root_nodes); 2596 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) { 2597 var mom = me.node; 2598 for (var i in me.breadcrumbs) { 2599 var j = me.breadcrumbs[i]; 2600 mom = mom.children[j]; 2601 expand_node(me, mom); 2602 } 2603 mom.label_div.className = mom.label_div.className + " selected"; 2604 addLoadEvent(function() { 2605 scrollIntoView("nav-tree"); 2606 }); 2607 } 2608} 2609 2610/* TODO: eliminate redundancy with non-google functions */ 2611function init_google_navtree(navtree_id, toroot, root_nodes) 2612{ 2613 var me = new Object(); 2614 me.toroot = toroot; 2615 me.node = new Object(); 2616 2617 me.node.li = document.getElementById(navtree_id); 2618 me.node.children_data = root_nodes; 2619 me.node.children = new Array(); 2620 me.node.children_ul = document.createElement("ul"); 2621 me.node.get_children_ul = function() { return me.node.children_ul; }; 2622 //me.node.children_ul.className = "children_ul"; 2623 me.node.li.appendChild(me.node.children_ul); 2624 me.node.depth = 0; 2625 2626 get_google_node(me, me.node); 2627} 2628 2629function new_google_node(me, mom, text, link, children_data, api_level) 2630{ 2631 var node = new Object(); 2632 var child; 2633 node.children = Array(); 2634 node.children_data = children_data; 2635 node.depth = mom.depth + 1; 2636 node.get_children_ul = function() { 2637 if (!node.children_ul) { 2638 node.children_ul = document.createElement("ul"); 2639 node.children_ul.className = "tree-list-children"; 2640 node.li.appendChild(node.children_ul); 2641 } 2642 return node.children_ul; 2643 }; 2644 node.li = document.createElement("li"); 2645 2646 mom.get_children_ul().appendChild(node.li); 2647 2648 2649 if(link) { 2650 child = document.createElement("a"); 2651 2652 } 2653 else { 2654 child = document.createElement("span"); 2655 child.className = "tree-list-subtitle"; 2656 2657 } 2658 if (children_data != null) { 2659 node.li.className="nav-section"; 2660 node.label_div = document.createElement("div"); 2661 node.label_div.className = "nav-section-header-ref"; 2662 node.li.appendChild(node.label_div); 2663 get_google_node(me, node); 2664 node.label_div.appendChild(child); 2665 } 2666 else { 2667 node.li.appendChild(child); 2668 } 2669 if(link) { 2670 child.href = me.toroot + link; 2671 } 2672 node.label = document.createTextNode(text); 2673 child.appendChild(node.label); 2674 2675 node.children_ul = null; 2676 2677 return node; 2678} 2679 2680function get_google_node(me, mom) 2681{ 2682 mom.children_visited = true; 2683 var linkText; 2684 for (var i in mom.children_data) { 2685 var node_data = mom.children_data[i]; 2686 linkText = node_data[0]; 2687 2688 if(linkText.match("^"+"com.google.android")=="com.google.android"){ 2689 linkText = linkText.substr(19, linkText.length); 2690 } 2691 mom.children[i] = new_google_node(me, mom, linkText, node_data[1], 2692 node_data[2], node_data[3]); 2693 } 2694} 2695function showGoogleRefTree() { 2696 init_default_google_navtree(toRoot); 2697 init_default_gcm_navtree(toRoot); 2698} 2699 2700function init_default_google_navtree(toroot) { 2701 // load json file for navtree data 2702 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) { 2703 // when the file is loaded, initialize the tree 2704 if(jqxhr.status === 200) { 2705 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA); 2706 highlightSidenav(); 2707 resizeNav(); 2708 } 2709 }); 2710} 2711 2712function init_default_gcm_navtree(toroot) { 2713 // load json file for navtree data 2714 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) { 2715 // when the file is loaded, initialize the tree 2716 if(jqxhr.status === 200) { 2717 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA); 2718 highlightSidenav(); 2719 resizeNav(); 2720 } 2721 }); 2722} 2723 2724/* TOGGLE INHERITED MEMBERS */ 2725 2726/* Toggle an inherited class (arrow toggle) 2727 * @param linkObj The link that was clicked. 2728 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed. 2729 * 'null' to simply toggle. 2730 */ 2731function toggleInherited(linkObj, expand) { 2732 var base = linkObj.getAttribute("id"); 2733 var list = document.getElementById(base + "-list"); 2734 var summary = document.getElementById(base + "-summary"); 2735 var trigger = document.getElementById(base + "-trigger"); 2736 var a = $(linkObj); 2737 if ( (expand == null && a.hasClass("closed")) || expand ) { 2738 list.style.display = "none"; 2739 summary.style.display = "block"; 2740 trigger.src = toRoot + "assets/images/triangle-opened.png"; 2741 a.removeClass("closed"); 2742 a.addClass("opened"); 2743 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) { 2744 list.style.display = "block"; 2745 summary.style.display = "none"; 2746 trigger.src = toRoot + "assets/images/triangle-closed.png"; 2747 a.removeClass("opened"); 2748 a.addClass("closed"); 2749 } 2750 return false; 2751} 2752 2753/* Toggle all inherited classes in a single table (e.g. all inherited methods) 2754 * @param linkObj The link that was clicked. 2755 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed. 2756 * 'null' to simply toggle. 2757 */ 2758function toggleAllInherited(linkObj, expand) { 2759 var a = $(linkObj); 2760 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody 2761 var expandos = $(".jd-expando-trigger", table); 2762 if ( (expand == null && a.text() == "[Expand]") || expand ) { 2763 expandos.each(function(i) { 2764 toggleInherited(this, true); 2765 }); 2766 a.text("[Collapse]"); 2767 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) { 2768 expandos.each(function(i) { 2769 toggleInherited(this, false); 2770 }); 2771 a.text("[Expand]"); 2772 } 2773 return false; 2774} 2775 2776/* Toggle all inherited members in the class (link in the class title) 2777 */ 2778function toggleAllClassInherited() { 2779 var a = $("#toggleAllClassInherited"); // get toggle link from class title 2780 var toggles = $(".toggle-all", $("#body-content")); 2781 if (a.text() == "[Expand All]") { 2782 toggles.each(function(i) { 2783 toggleAllInherited(this, true); 2784 }); 2785 a.text("[Collapse All]"); 2786 } else { 2787 toggles.each(function(i) { 2788 toggleAllInherited(this, false); 2789 }); 2790 a.text("[Expand All]"); 2791 } 2792 return false; 2793} 2794 2795/* Expand all inherited members in the class. Used when initiating page search */ 2796function ensureAllInheritedExpanded() { 2797 var toggles = $(".toggle-all", $("#body-content")); 2798 toggles.each(function(i) { 2799 toggleAllInherited(this, true); 2800 }); 2801 $("#toggleAllClassInherited").text("[Collapse All]"); 2802} 2803 2804 2805/* HANDLE KEY EVENTS 2806 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search) 2807 */ 2808var agent = navigator['userAgent'].toLowerCase(); 2809var mac = agent.indexOf("macintosh") != -1; 2810 2811$(document).keydown( function(e) { 2812var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key 2813 if (control && e.which == 70) { // 70 is "F" 2814 ensureAllInheritedExpanded(); 2815 } 2816}); 2817