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