1 var classesNav; 2 var devdocNav; 3 var sidenav; 4 var cookie_namespace = 'android_developer'; 5 var NAV_PREF_TREE = "tree"; 6 var NAV_PREF_PANELS = "panels"; 7 var nav_pref; 8 var isMobile = false; // true if mobile, so we can adjust some layout 9 var mPagePath; // initialized in ready() function 10 11 var basePath = getBaseUri(location.pathname); 12 var SITE_ROOT = toRoot + basePath.substring(1,basePath.indexOf("/",1)); 13 var 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 22 var 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 : 214 false; // 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 585 function 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 604 function 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 630 function setNavBarLeftPos() { 631 navBarLeftPos = $('#body-content').offset().left; 632 } 633 634 635 function 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 649 function 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 661 var agent = navigator['userAgent'].toLowerCase(); 662 // If a mobile phone, set flag and do mobile setup 663 if ((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 671 addLoadEvent( function() { 672 $("pre:not(.no-pretty-print)").addClass("prettyprint"); 673 prettyPrint(); 674 } ); 675 676 677 678 679 /* ######### RESIZE THE SIDENAV HEIGHT ########## */ 680 681 function 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 747 var updateScrollbars = false; 748 var 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 */ 755 function 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. */ 776 function 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 */ 788 function 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 796 function 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. */ 814 function 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 */ 838 function 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 851 function 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 868 function 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 /* MISC LIBRARY FUNCTIONS */ 902 903 904 905 906 907 function toggle(obj, slide) { 908 var ul = $("ul:first", obj); 909 var li = ul.parent(); 910 if (li.hasClass("closed")) { 911 if (slide) { 912 ul.slideDown("fast"); 913 } else { 914 ul.show(); 915 } 916 li.removeClass("closed"); 917 li.addClass("open"); 918 $(".toggle-img", li).attr("title", "hide pages"); 919 } else { 920 ul.slideUp("fast"); 921 li.removeClass("open"); 922 li.addClass("closed"); 923 $(".toggle-img", li).attr("title", "show pages"); 924 } 925 } 926 927 928 function buildToggleLists() { 929 $(".toggle-list").each( 930 function(i) { 931 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>"); 932 $(this).addClass("closed"); 933 }); 934 } 935 936 937 938 function hideNestedItems(list, toggle) { 939 $list = $(list); 940 // hide nested lists 941 if($list.hasClass('showing')) { 942 $("li ol", $list).hide('fast'); 943 $list.removeClass('showing'); 944 // show nested lists 945 } else { 946 $("li ol", $list).show('fast'); 947 $list.addClass('showing'); 948 } 949 $(".more,.less",$(toggle)).toggle(); 950 } 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 /* REFERENCE NAV SWAP */ 980 981 982 function getNavPref() { 983 var v = readCookie('reference_nav'); 984 if (v != NAV_PREF_TREE) { 985 v = NAV_PREF_PANELS; 986 } 987 return v; 988 } 989 990 function chooseDefaultNav() { 991 nav_pref = getNavPref(); 992 if (nav_pref == NAV_PREF_TREE) { 993 $("#nav-panels").toggle(); 994 $("#panel-link").toggle(); 995 $("#nav-tree").toggle(); 996 $("#tree-link").toggle(); 997 } 998 } 999 1000 function swapNav() { 1001 if (nav_pref == NAV_PREF_TREE) { 1002 nav_pref = NAV_PREF_PANELS; 1003 } else { 1004 nav_pref = NAV_PREF_TREE; 1005 init_default_navtree(toRoot); 1006 } 1007 var date = new Date(); 1008 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years 1009 writeCookie("nav", nav_pref, "reference", date.toGMTString()); 1010 1011 $("#nav-panels").toggle(); 1012 $("#panel-link").toggle(); 1013 $("#nav-tree").toggle(); 1014 $("#tree-link").toggle(); 1015 1016 resizeNav(); 1017 1018 // Gross nasty hack to make tree view show up upon first swap by setting height manually 1019 $("#nav-tree .jspContainer:visible") 1020 .css({'height':$("#nav-tree .jspContainer .jspPane").height() +'px'}); 1021 // Another nasty hack to make the scrollbar appear now that we have height 1022 resizeNav(); 1023 1024 if ($("#nav-tree").is(':visible')) { 1025 scrollIntoView("nav-tree"); 1026 } else { 1027 scrollIntoView("packages-nav"); 1028 scrollIntoView("classes-nav"); 1029 } 1030 } 1031 1032 1033 1034 /* ############################################ */ 1035 /* ########## LOCALIZATION ############ */ 1036 /* ############################################ */ 1037 1038 function getBaseUri(uri) { 1039 var intlUrl = (uri.substring(0,6) == "/intl/"); 1040 if (intlUrl) { 1041 base = uri.substring(uri.indexOf('intl/')+5,uri.length); 1042 base = base.substring(base.indexOf('/')+1, base.length); 1043 //alert("intl, returning base url: /" + base); 1044 return ("/" + base); 1045 } else { 1046 //alert("not intl, returning uri as found."); 1047 return uri; 1048 } 1049 } 1050 1051 function requestAppendHL(uri) { 1052 //append "?hl=<lang> to an outgoing request (such as to blog) 1053 var lang = getLangPref(); 1054 if (lang) { 1055 var q = 'hl=' + lang; 1056 uri += '?' + q; 1057 window.location = uri; 1058 return false; 1059 } else { 1060 return true; 1061 } 1062 } 1063 1064 1065 function changeNavLang(lang) { 1066 var $links = $("#devdoc-nav,#header,#nav-x,.training-nav-top,.content-footer").find("a["+lang+"-lang]"); 1067 $links.each(function(i){ // for each link with a translation 1068 var $link = $(this); 1069 if (lang != "en") { // No need to worry about English, because a language change invokes new request 1070 // put the desired language from the attribute as the text 1071 $link.text($link.attr(lang+"-lang")) 1072 } 1073 }); 1074 } 1075 1076 function changeLangPref(lang, submit) { 1077 var date = new Date(); 1078 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); 1079 // keep this for 50 years 1080 //alert("expires: " + expires) 1081 writeCookie("pref_lang", lang, null, expires); 1082 1083 // ####### TODO: Remove this condition once we're stable on devsite ####### 1084 // This condition is only needed if we still need to support legacy GAE server 1085 if (devsite) { 1086 // Switch language when on Devsite server 1087 if (submit) { 1088 $("#setlang").submit(); 1089 } 1090 } else { 1091 // Switch language when on legacy GAE server 1092 if (submit) { 1093 window.location = getBaseUri(location.pathname); 1094 } 1095 } 1096 } 1097 1098 function loadLangPref() { 1099 var lang = readCookie("pref_lang"); 1100 if (lang != 0) { 1101 $("#language").find("option[value='"+lang+"']").attr("selected",true); 1102 } 1103 } 1104 1105 function getLangPref() { 1106 var lang = $("#language").find(":selected").attr("value"); 1107 if (!lang) { 1108 lang = readCookie("pref_lang"); 1109 } 1110 return (lang != 0) ? lang : 'en'; 1111 } 1112 1113 /* ########## END LOCALIZATION ############ */ 1114 1115 1116 1117 1118 1119 1120 /* Used to hide and reveal supplemental content, such as long code samples. 1121 See the companion CSS in android-developer-docs.css */ 1122 function toggleContent(obj) { 1123 var div = $(obj.parentNode.parentNode); 1124 var toggleMe = $(".toggle-content-toggleme",div); 1125 if (div.hasClass("closed")) { // if it's closed, open it 1126 toggleMe.slideDown(); 1127 $(".toggle-content-text", obj).toggle(); 1128 div.removeClass("closed").addClass("open"); 1129 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot 1130 + "assets/images/triangle-opened.png"); 1131 } else { // if it's open, close it 1132 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow 1133 $(".toggle-content-text", obj).toggle(); 1134 div.removeClass("open").addClass("closed"); 1135 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot 1136 + "assets/images/triangle-closed.png"); 1137 }); 1138 } 1139 return false; 1140 } 1141 1142 1143 /* New version of expandable content */ 1144 function toggleExpandable(link,id) { 1145 if($(id).is(':visible')) { 1146 $(id).slideUp(); 1147 $(link).removeClass('expanded'); 1148 } else { 1149 $(id).slideDown(); 1150 $(link).addClass('expanded'); 1151 } 1152 } 1153 1154 function hideExpandable(ids) { 1155 $(ids).slideUp(); 1156 $(ids).prev('h4').find('a.expandable').removeClass('expanded'); 1157 } 1158 1159 1160 1161 1162 1163 /* 1164 * Slideshow 1.0 1165 * Used on /index.html and /develop/index.html for carousel 1166 * 1167 * Sample usage: 1168 * HTML - 1169 * <div class="slideshow-container"> 1170 * <a href="" class="slideshow-prev">Prev</a> 1171 * <a href="" class="slideshow-next">Next</a> 1172 * <ul> 1173 * <li class="item"><img src="images/marquee1.jpg"></li> 1174 * <li class="item"><img src="images/marquee2.jpg"></li> 1175 * <li class="item"><img src="images/marquee3.jpg"></li> 1176 * <li class="item"><img src="images/marquee4.jpg"></li> 1177 * </ul> 1178 * </div> 1179 * 1180 * <script type="text/javascript"> 1181 * $('.slideshow-container').dacSlideshow({ 1182 * auto: true, 1183 * btnPrev: '.slideshow-prev', 1184 * btnNext: '.slideshow-next' 1185 * }); 1186 * </script> 1187 * 1188 * Options: 1189 * btnPrev: optional identifier for previous button 1190 * btnNext: optional identifier for next button 1191 * btnPause: optional identifier for pause button 1192 * auto: whether or not to auto-proceed 1193 * speed: animation speed 1194 * autoTime: time between auto-rotation 1195 * easing: easing function for transition 1196 * start: item to select by default 1197 * scroll: direction to scroll in 1198 * pagination: whether or not to include dotted pagination 1199 * 1200 */ 1201 1202 (function($) { 1203 $.fn.dacSlideshow = function(o) { 1204 1205 //Options - see above 1206 o = $.extend({ 1207 btnPrev: null, 1208 btnNext: null, 1209 btnPause: null, 1210 auto: true, 1211 speed: 500, 1212 autoTime: 12000, 1213 easing: null, 1214 start: 0, 1215 scroll: 1, 1216 pagination: true 1217 1218 }, o || {}); 1219 1220 //Set up a carousel for each 1221 return this.each(function() { 1222 1223 var running = false; 1224 var animCss = o.vertical ? "top" : "left"; 1225 var sizeCss = o.vertical ? "height" : "width"; 1226 var div = $(this); 1227 var ul = $("ul", div); 1228 var tLi = $("li", ul); 1229 var tl = tLi.size(); 1230 var timer = null; 1231 1232 var li = $("li", ul); 1233 var itemLength = li.size(); 1234 var curr = o.start; 1235 1236 li.css({float: o.vertical ? "none" : "left"}); 1237 ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"}); 1238 div.css({position: "relative", "z-index": "2", left: "0px"}); 1239 1240 var liSize = o.vertical ? height(li) : width(li); 1241 var ulSize = liSize * itemLength; 1242 var divSize = liSize; 1243 1244 li.css({width: li.width(), height: li.height()}); 1245 ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize)); 1246 1247 div.css(sizeCss, divSize+"px"); 1248 1249 //Pagination 1250 if (o.pagination) { 1251 var pagination = $("<div class='pagination'></div>"); 1252 var pag_ul = $("<ul></ul>"); 1253 if (tl > 1) { 1254 for (var i=0;i<tl;i++) { 1255 var li = $("<li>"+i+"</li>"); 1256 pag_ul.append(li); 1257 if (i==o.start) li.addClass('active'); 1258 li.click(function() { 1259 go(parseInt($(this).text())); 1260 }) 1261 } 1262 pagination.append(pag_ul); 1263 div.append(pagination); 1264 } 1265 } 1266 1267 //Previous button 1268 if(o.btnPrev) 1269 $(o.btnPrev).click(function(e) { 1270 e.preventDefault(); 1271 return go(curr-o.scroll); 1272 }); 1273 1274 //Next button 1275 if(o.btnNext) 1276 $(o.btnNext).click(function(e) { 1277 e.preventDefault(); 1278 return go(curr+o.scroll); 1279 }); 1280 1281 //Pause button 1282 if(o.btnPause) 1283 $(o.btnPause).click(function(e) { 1284 e.preventDefault(); 1285 if ($(this).hasClass('paused')) { 1286 startRotateTimer(); 1287 } else { 1288 pauseRotateTimer(); 1289 } 1290 }); 1291 1292 //Auto rotation 1293 if(o.auto) startRotateTimer(); 1294 1295 function startRotateTimer() { 1296 clearInterval(timer); 1297 timer = setInterval(function() { 1298 if (curr == tl-1) { 1299 go(0); 1300 } else { 1301 go(curr+o.scroll); 1302 } 1303 }, o.autoTime); 1304 $(o.btnPause).removeClass('paused'); 1305 } 1306 1307 function pauseRotateTimer() { 1308 clearInterval(timer); 1309 $(o.btnPause).addClass('paused'); 1310 } 1311 1312 //Go to an item 1313 function go(to) { 1314 if(!running) { 1315 1316 if(to<0) { 1317 to = itemLength-1; 1318 } else if (to>itemLength-1) { 1319 to = 0; 1320 } 1321 curr = to; 1322 1323 running = true; 1324 1325 ul.animate( 1326 animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing, 1327 function() { 1328 running = false; 1329 } 1330 ); 1331 1332 $(o.btnPrev + "," + o.btnNext).removeClass("disabled"); 1333 $( (curr-o.scroll<0 && o.btnPrev) 1334 || 1335 (curr+o.scroll > itemLength && o.btnNext) 1336 || 1337 [] 1338 ).addClass("disabled"); 1339 1340 1341 var nav_items = $('li', pagination); 1342 nav_items.removeClass('active'); 1343 nav_items.eq(to).addClass('active'); 1344 1345 1346 } 1347 if(o.auto) startRotateTimer(); 1348 return false; 1349 }; 1350 }); 1351 }; 1352 1353 function css(el, prop) { 1354 return parseInt($.css(el[0], prop)) || 0; 1355 }; 1356 function width(el) { 1357 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight'); 1358 }; 1359 function height(el) { 1360 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom'); 1361 }; 1362 1363 })(jQuery); 1364 1365 1366 /* 1367 * dacSlideshow 1.0 1368 * Used on develop/index.html for side-sliding tabs 1369 * 1370 * Sample usage: 1371 * HTML - 1372 * <div class="slideshow-container"> 1373 * <a href="" class="slideshow-prev">Prev</a> 1374 * <a href="" class="slideshow-next">Next</a> 1375 * <ul> 1376 * <li class="item"><img src="images/marquee1.jpg"></li> 1377 * <li class="item"><img src="images/marquee2.jpg"></li> 1378 * <li class="item"><img src="images/marquee3.jpg"></li> 1379 * <li class="item"><img src="images/marquee4.jpg"></li> 1380 * </ul> 1381 * </div> 1382 * 1383 * <script type="text/javascript"> 1384 * $('.slideshow-container').dacSlideshow({ 1385 * auto: true, 1386 * btnPrev: '.slideshow-prev', 1387 * btnNext: '.slideshow-next' 1388 * }); 1389 * </script> 1390 * 1391 * Options: 1392 * btnPrev: optional identifier for previous button 1393 * btnNext: optional identifier for next button 1394 * auto: whether or not to auto-proceed 1395 * speed: animation speed 1396 * autoTime: time between auto-rotation 1397 * easing: easing function for transition 1398 * start: item to select by default 1399 * scroll: direction to scroll in 1400 * pagination: whether or not to include dotted pagination 1401 * 1402 */ 1403 (function($) { 1404 $.fn.dacTabbedList = function(o) { 1405 1406 //Options - see above 1407 o = $.extend({ 1408 speed : 250, 1409 easing: null, 1410 nav_id: null, 1411 frame_id: null 1412 }, o || {}); 1413 1414 //Set up a carousel for each 1415 return this.each(function() { 1416 1417 var curr = 0; 1418 var running = false; 1419 var animCss = "margin-left"; 1420 var sizeCss = "width"; 1421 var div = $(this); 1422 1423 var nav = $(o.nav_id, div); 1424 var nav_li = $("li", nav); 1425 var nav_size = nav_li.size(); 1426 var frame = div.find(o.frame_id); 1427 var content_width = $(frame).find('ul').width(); 1428 //Buttons 1429 $(nav_li).click(function(e) { 1430 go($(nav_li).index($(this))); 1431 }) 1432 1433 //Go to an item 1434 function go(to) { 1435 if(!running) { 1436 curr = to; 1437 running = true; 1438 1439 frame.animate({ 'margin-left' : -(curr*content_width) }, o.speed, o.easing, 1440 function() { 1441 running = false; 1442 } 1443 ); 1444 1445 1446 nav_li.removeClass('active'); 1447 nav_li.eq(to).addClass('active'); 1448 1449 1450 } 1451 return false; 1452 }; 1453 }); 1454 }; 1455 1456 function css(el, prop) { 1457 return parseInt($.css(el[0], prop)) || 0; 1458 }; 1459 function width(el) { 1460 return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight'); 1461 }; 1462 function height(el) { 1463 return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom'); 1464 }; 1465 1466 })(jQuery); 1467 1468 1469 1470 1471 1472 /* ######################################################## */ 1473 /* ################ SEARCH SUGGESTIONS ################## */ 1474 /* ######################################################## */ 1475 1476 1477 1478 var gSelectedIndex = -1; // the index position of currently highlighted suggestion 1479 var gSelectedColumn = -1; // which column of suggestion lists is currently focused 1480 1481 var gMatches = new Array(); 1482 var gLastText = ""; 1483 var gInitialized = false; 1484 var ROW_COUNT_FRAMEWORK = 20; // max number of results in list 1485 var gListLength = 0; 1486 1487 1488 var gGoogleMatches = new Array(); 1489 var ROW_COUNT_GOOGLE = 15; // max number of results in list 1490 var gGoogleListLength = 0; 1491 1492 var gDocsMatches = new Array(); 1493 var ROW_COUNT_DOCS = 100; // max number of results in list 1494 var gDocsListLength = 0; 1495 1496 function onSuggestionClick(link) { 1497 // When user clicks a suggested document, track it 1498 _gaq.push(['_trackEvent', 'Suggestion Click', 'clicked: ' + $(link).text(), 1499 'from: ' + $("#search_autocomplete").val()]); 1500 } 1501 1502 function set_item_selected($li, selected) 1503 { 1504 if (selected) { 1505 $li.attr('class','jd-autocomplete jd-selected'); 1506 } else { 1507 $li.attr('class','jd-autocomplete'); 1508 } 1509 } 1510 1511 function set_item_values(toroot, $li, match) 1512 { 1513 var $link = $('a',$li); 1514 $link.html(match.__hilabel || match.label); 1515 $link.attr('href',toroot + match.link); 1516 } 1517 1518 function new_suggestion($list) { 1519 var $li = $("<li class='jd-autocomplete'></li>"); 1520 $list.append($li); 1521 1522 $li.mousedown(function() { 1523 window.location = this.firstChild.getAttribute("href"); 1524 }); 1525 $li.mouseover(function() { 1526 $('.search_filtered_wrapper li').removeClass('jd-selected'); 1527 $(this).addClass('jd-selected'); 1528 gSelectedColumn = $(".search_filtered:visible").index($(this).closest('.search_filtered')); 1529 gSelectedIndex = $("li", $(".search_filtered:visible")[gSelectedColumn]).index(this); 1530 }); 1531 $li.append("<a onclick='onSuggestionClick(this)'></a>"); 1532 $li.attr('class','show-item'); 1533 return $li; 1534 } 1535 1536 function sync_selection_table(toroot) 1537 { 1538 var $li; //list item jquery object 1539 var i; //list item iterator 1540 1541 // if there are NO results at all, hide all columns 1542 if (!(gMatches.length > 0) && !(gGoogleMatches.length > 0) && !(gDocsMatches.length > 0)) { 1543 $('.suggest-card').hide(300); 1544 return; 1545 } 1546 1547 // if there are api results 1548 if ((gMatches.length > 0) || (gGoogleMatches.length > 0)) { 1549 // reveal suggestion list 1550 $('.suggest-card.dummy').show(); 1551 $('.suggest-card.reference').show(); 1552 var listIndex = 0; // list index position 1553 1554 // reset the lists 1555 $(".search_filtered_wrapper.reference li").remove(); 1556 1557 // ########### ANDROID RESULTS ############# 1558 if (gMatches.length > 0) { 1559 1560 // determine android results to show 1561 gListLength = gMatches.length < ROW_COUNT_FRAMEWORK ? 1562 gMatches.length : ROW_COUNT_FRAMEWORK; 1563 for (i=0; i<gListLength; i++) { 1564 var $li = new_suggestion($(".suggest-card.reference ul")); 1565 set_item_values(toroot, $li, gMatches[i]); 1566 set_item_selected($li, i == gSelectedIndex); 1567 } 1568 } 1569 1570 // ########### GOOGLE RESULTS ############# 1571 if (gGoogleMatches.length > 0) { 1572 // show header for list 1573 $(".suggest-card.reference ul").append("<li class='header'>in Google Services:</li>"); 1574 1575 // determine google results to show 1576 gGoogleListLength = gGoogleMatches.length < ROW_COUNT_GOOGLE ? gGoogleMatches.length : ROW_COUNT_GOOGLE; 1577 for (i=0; i<gGoogleListLength; i++) { 1578 var $li = new_suggestion($(".suggest-card.reference ul")); 1579 set_item_values(toroot, $li, gGoogleMatches[i]); 1580 set_item_selected($li, i == gSelectedIndex); 1581 } 1582 } 1583 } else { 1584 $('.suggest-card.reference').hide(); 1585 $('.suggest-card.dummy').hide(); 1586 } 1587 1588 // ########### JD DOC RESULTS ############# 1589 if (gDocsMatches.length > 0) { 1590 // reset the lists 1591 $(".search_filtered_wrapper.docs li").remove(); 1592 1593 // determine google results to show 1594 gDocsListLength = gDocsMatches.length < ROW_COUNT_DOCS ? gDocsMatches.length : ROW_COUNT_DOCS; 1595 for (i=0; i<gDocsListLength; i++) { 1596 var sugg = gDocsMatches[i]; 1597 var $li; 1598 if (sugg.type == "design") { 1599 $li = new_suggestion($(".suggest-card.design ul")); 1600 } else 1601 if (sugg.type == "distribute") { 1602 $li = new_suggestion($(".suggest-card.distribute ul")); 1603 } else 1604 if (sugg.type == "training") { 1605 $li = new_suggestion($(".suggest-card.develop .child-card.training")); 1606 } else 1607 if (sugg.type == "guide"||"google") { 1608 $li = new_suggestion($(".suggest-card.develop .child-card.guides")); 1609 } else { 1610 continue; 1611 } 1612 1613 set_item_values(toroot, $li, sugg); 1614 set_item_selected($li, i == gSelectedIndex); 1615 } 1616 1617 // add heading and show or hide card 1618 if ($(".suggest-card.design li").length > 0) { 1619 $(".suggest-card.design ul").prepend("<li class='header'>Design:</li>"); 1620 $(".suggest-card.design").show(300); 1621 } else { 1622 $('.suggest-card.design').hide(300); 1623 } 1624 if ($(".suggest-card.distribute li").length > 0) { 1625 $(".suggest-card.distribute ul").prepend("<li class='header'>Distribute:</li>"); 1626 $(".suggest-card.distribute").show(300); 1627 } else { 1628 $('.suggest-card.distribute').hide(300); 1629 } 1630 if ($(".child-card.guides li").length > 0) { 1631 $(".child-card.guides").prepend("<li class='header'>Guides:</li>"); 1632 $(".child-card.guides li").appendTo(".suggest-card.develop ul"); 1633 } 1634 if ($(".child-card.training li").length > 0) { 1635 $(".child-card.training").prepend("<li class='header'>Training:</li>"); 1636 $(".child-card.training li").appendTo(".suggest-card.develop ul"); 1637 } 1638 1639 if ($(".suggest-card.develop li").length > 0) { 1640 $(".suggest-card.develop").show(300); 1641 } else { 1642 $('.suggest-card.develop').hide(300); 1643 } 1644 1645 } else { 1646 $('.search_filtered_wrapper.docs .suggest-card:not(.dummy)').hide(300); 1647 } 1648 } 1649 1650 /** Called by the search input's onkeydown and onkeyup events. 1651 * Handles navigation with keyboard arrows, Enter key to invoke search, 1652 * otherwise invokes search suggestions on key-up event. 1653 * @param e The JS event 1654 * @param kd True if the event is key-down 1655 * @param toroot A string for the site's root path 1656 * @returns True if the event should bubble up 1657 */ 1658 function search_changed(e, kd, toroot) 1659 { 1660 var search = document.getElementById("search_autocomplete"); 1661 var text = search.value.replace(/(^ +)|( +$)/g, ''); 1662 // get the ul hosting the currently selected item 1663 gSelectedColumn = gSelectedColumn >= 0 ? gSelectedColumn : 0; 1664 var $columns = $(".search_filtered_wrapper").find(".search_filtered:visible"); 1665 var $selectedUl = $columns[gSelectedColumn]; 1666 1667 // show/hide the close button 1668 if (text != '') { 1669 $(".search .close").removeClass("hide"); 1670 } else { 1671 $(".search .close").addClass("hide"); 1672 } 1673 // 27 = esc 1674 if (e.keyCode == 27) { 1675 // close all search results 1676 if (kd) $('.search .close').trigger('click'); 1677 return true; 1678 } 1679 // 13 = enter 1680 else if (e.keyCode == 13) { 1681 if (gSelectedIndex < 0) { 1682 $('.suggest-card').hide(); 1683 if ($("#searchResults").is(":hidden") && (search.value != "")) { 1684 // if results aren't showing (and text not empty), return true to allow search to execute 1685 return true; 1686 } else { 1687 // otherwise, results are already showing, so allow ajax to auto refresh the results 1688 // and ignore this Enter press to avoid the reload. 1689 return false; 1690 } 1691 } else if (kd && gSelectedIndex >= 0) { 1692 // click the link corresponding to selected item 1693 $("a",$("li",$selectedUl)[gSelectedIndex]).get()[0].click(); 1694 return false; 1695 } 1696 } 1697 // Stop here if Google results are showing 1698 else if ($("#searchResults").is(":visible")) { 1699 return true; 1700 } 1701 // 38 UP ARROW 1702 else if (kd && (e.keyCode == 38)) { 1703 // if the next item is a header, skip it 1704 if ($($("li", $selectedUl)[gSelectedIndex-1]).hasClass("header")) { 1705 gSelectedIndex--; 1706 } 1707 if (gSelectedIndex >= 0) { 1708 $('li', $selectedUl).removeClass('jd-selected'); 1709 gSelectedIndex--; 1710 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1711 // If user reaches top, reset selected column 1712 if (gSelectedIndex < 0) { 1713 gSelectedColumn = -1; 1714 } 1715 } 1716 return false; 1717 } 1718 // 40 DOWN ARROW 1719 else if (kd && (e.keyCode == 40)) { 1720 // if the next item is a header, skip it 1721 if ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header")) { 1722 gSelectedIndex++; 1723 } 1724 if ((gSelectedIndex < $("li", $selectedUl).length-1) || 1725 ($($("li", $selectedUl)[gSelectedIndex+1]).hasClass("header"))) { 1726 $('li', $selectedUl).removeClass('jd-selected'); 1727 gSelectedIndex++; 1728 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1729 } 1730 return false; 1731 } 1732 // Consider left/right arrow navigation 1733 // NOTE: Order of suggest columns are reverse order (index position 0 is on right) 1734 else if (kd && $columns.length > 1 && gSelectedColumn >= 0) { 1735 // 37 LEFT ARROW 1736 // go left only if current column is not left-most column (last column) 1737 if (e.keyCode == 37 && gSelectedColumn < $columns.length - 1) { 1738 $('li', $selectedUl).removeClass('jd-selected'); 1739 gSelectedColumn++; 1740 $selectedUl = $columns[gSelectedColumn]; 1741 // keep or reset the selected item to last item as appropriate 1742 gSelectedIndex = gSelectedIndex > 1743 $("li", $selectedUl).length-1 ? 1744 $("li", $selectedUl).length-1 : gSelectedIndex; 1745 // if the corresponding item is a header, move down 1746 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) { 1747 gSelectedIndex++; 1748 } 1749 // set item selected 1750 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1751 return false; 1752 } 1753 // 39 RIGHT ARROW 1754 // go right only if current column is not the right-most column (first column) 1755 else if (e.keyCode == 39 && gSelectedColumn > 0) { 1756 $('li', $selectedUl).removeClass('jd-selected'); 1757 gSelectedColumn--; 1758 $selectedUl = $columns[gSelectedColumn]; 1759 // keep or reset the selected item to last item as appropriate 1760 gSelectedIndex = gSelectedIndex > 1761 $("li", $selectedUl).length-1 ? 1762 $("li", $selectedUl).length-1 : gSelectedIndex; 1763 // if the corresponding item is a header, move down 1764 if ($($("li", $selectedUl)[gSelectedIndex]).hasClass("header")) { 1765 gSelectedIndex++; 1766 } 1767 // set item selected 1768 $('li:nth-child('+(gSelectedIndex+1)+')', $selectedUl).addClass('jd-selected'); 1769 return false; 1770 } 1771 } 1772 1773 // if key-up event and not arrow down/up, 1774 // read the search query and add suggestsions to gMatches 1775 else if (!kd && (e.keyCode != 40) 1776 && (e.keyCode != 38) 1777 && (e.keyCode != 37) 1778 && (e.keyCode != 39)) { 1779 gSelectedIndex = -1; 1780 gMatches = new Array(); 1781 matchedCount = 0; 1782 gGoogleMatches = new Array(); 1783 matchedCountGoogle = 0; 1784 gDocsMatches = new Array(); 1785 matchedCountDocs = 0; 1786 1787 // Search for Android matches 1788 for (var i=0; i<DATA.length; i++) { 1789 var s = DATA[i]; 1790 if (text.length != 0 && 1791 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) { 1792 gMatches[matchedCount] = s; 1793 matchedCount++; 1794 } 1795 } 1796 rank_autocomplete_api_results(text, gMatches); 1797 for (var i=0; i<gMatches.length; i++) { 1798 var s = gMatches[i]; 1799 } 1800 1801 1802 // Search for Google matches 1803 for (var i=0; i<GOOGLE_DATA.length; i++) { 1804 var s = GOOGLE_DATA[i]; 1805 if (text.length != 0 && 1806 s.label.toLowerCase().indexOf(text.toLowerCase()) != -1) { 1807 gGoogleMatches[matchedCountGoogle] = s; 1808 matchedCountGoogle++; 1809 } 1810 } 1811 rank_autocomplete_api_results(text, gGoogleMatches); 1812 for (var i=0; i<gGoogleMatches.length; i++) { 1813 var s = gGoogleMatches[i]; 1814 } 1815 1816 highlight_autocomplete_result_labels(text); 1817 1818 1819 1820 // Search for JD docs 1821 if (text.length >= 3) { 1822 for (var i=0; i<JD_DATA.length; i++) { 1823 // Regex to match only the beginning of a word 1824 var textRegex = new RegExp("\\b" + text.toLowerCase(), "g"); 1825 // current search comparison, with counters for tag and title, 1826 // used later to improve ranking 1827 var s = JD_DATA[i]; 1828 s.matched_tag = 0; 1829 s.matched_title = 0; 1830 var matched = false; 1831 1832 // Check if query matches any tags; work backwards toward 1 to assist ranking 1833 for (var j = s.tags.length - 1; j >= 0; j--) { 1834 // it matches a tag 1835 if (s.tags[j].toLowerCase().match(textRegex)) { 1836 matched = true; 1837 s.matched_tag = j + 1; // add 1 to index position 1838 } 1839 } 1840 // Don't consider doc title for lessons (only for class landing pages) 1841 // ...it is not a training lesson (or is but has matched a tag) 1842 if (!(s.type == "training" && s.link.indexOf("index.html") == -1) || matched) { 1843 // it matches the doc title 1844 if (s.label.toLowerCase().match(textRegex)) { 1845 matched = true; 1846 s.matched_title = 1; 1847 } 1848 } 1849 if (matched) { 1850 gDocsMatches[matchedCountDocs] = s; 1851 matchedCountDocs++; 1852 } 1853 } 1854 rank_autocomplete_doc_results(text, gDocsMatches); 1855 } 1856 1857 // draw the suggestions 1858 sync_selection_table(toroot); 1859 return true; // allow the event to bubble up to the search api 1860 } 1861 } 1862 1863 /* Order the jd doc result list based on match quality */ 1864 function rank_autocomplete_doc_results(query, matches) { 1865 query = query || ''; 1866 if (!matches || !matches.length) 1867 return; 1868 1869 var _resultScoreFn = function(match) { 1870 var score = 1.0; 1871 1872 // if the query matched a tag 1873 if (match.matched_tag > 0) { 1874 // multiply score by factor relative to position in tags list (max of 3) 1875 score *= 3 / match.matched_tag; 1876 1877 // if it also matched the title 1878 if (match.matched_title > 0) { 1879 score *= 2; 1880 } 1881 } else if (match.matched_title > 0) { 1882 score *= 3; 1883 } 1884 1885 return score; 1886 }; 1887 1888 for (var i=0; i<matches.length; i++) { 1889 matches[i].__resultScore = _resultScoreFn(matches[i]); 1890 } 1891 1892 matches.sort(function(a,b){ 1893 var n = b.__resultScore - a.__resultScore; 1894 if (n == 0) // lexicographical sort if scores are the same 1895 n = (a.label < b.label) ? -1 : 1; 1896 return n; 1897 }); 1898 } 1899 1900 /* Order the result list based on match quality */ 1901 function rank_autocomplete_api_results(query, matches) { 1902 query = query || ''; 1903 if (!matches || !matches.length) 1904 return; 1905 1906 // helper function that gets the last occurence index of the given regex 1907 // in the given string, or -1 if not found 1908 var _lastSearch = function(s, re) { 1909 if (s == '') 1910 return -1; 1911 var l = -1; 1912 var tmp; 1913 while ((tmp = s.search(re)) >= 0) { 1914 if (l < 0) l = 0; 1915 l += tmp; 1916 s = s.substr(tmp + 1); 1917 } 1918 return l; 1919 }; 1920 1921 // helper function that counts the occurrences of a given character in 1922 // a given string 1923 var _countChar = function(s, c) { 1924 var n = 0; 1925 for (var i=0; i<s.length; i++) 1926 if (s.charAt(i) == c) ++n; 1927 return n; 1928 }; 1929 1930 var queryLower = query.toLowerCase(); 1931 var queryAlnum = (queryLower.match(/\w+/) || [''])[0]; 1932 var partPrefixAlnumRE = new RegExp('\\b' + queryAlnum); 1933 var partExactAlnumRE = new RegExp('\\b' + queryAlnum + '\\b'); 1934 1935 var _resultScoreFn = function(result) { 1936 // scores are calculated based on exact and prefix matches, 1937 // and then number of path separators (dots) from the last 1938 // match (i.e. favoring classes and deep package names) 1939 var score = 1.0; 1940 var labelLower = result.label.toLowerCase(); 1941 var t; 1942 t = _lastSearch(labelLower, partExactAlnumRE); 1943 if (t >= 0) { 1944 // exact part match 1945 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 1946 score *= 200 / (partsAfter + 1); 1947 } else { 1948 t = _lastSearch(labelLower, partPrefixAlnumRE); 1949 if (t >= 0) { 1950 // part prefix match 1951 var partsAfter = _countChar(labelLower.substr(t + 1), '.'); 1952 score *= 20 / (partsAfter + 1); 1953 } 1954 } 1955 1956 return score; 1957 }; 1958 1959 for (var i=0; i<matches.length; i++) { 1960 // if the API is deprecated, default score is 0; otherwise, perform scoring 1961 if (matches[i].deprecated == "true") { 1962 matches[i].__resultScore = 0; 1963 } else { 1964 matches[i].__resultScore = _resultScoreFn(matches[i]); 1965 } 1966 } 1967 1968 matches.sort(function(a,b){ 1969 var n = b.__resultScore - a.__resultScore; 1970 if (n == 0) // lexicographical sort if scores are the same 1971 n = (a.label < b.label) ? -1 : 1; 1972 return n; 1973 }); 1974 } 1975 1976 /* Add emphasis to part of string that matches query */ 1977 function highlight_autocomplete_result_labels(query) { 1978 query = query || ''; 1979 if ((!gMatches || !gMatches.length) && (!gGoogleMatches || !gGoogleMatches.length)) 1980 return; 1981 1982 var queryLower = query.toLowerCase(); 1983 var queryAlnumDot = (queryLower.match(/[\w\.]+/) || [''])[0]; 1984 var queryRE = new RegExp( 1985 '(' + queryAlnumDot.replace(/\./g, '\\.') + ')', 'ig'); 1986 for (var i=0; i<gMatches.length; i++) { 1987 gMatches[i].__hilabel = gMatches[i].label.replace( 1988 queryRE, '<b>$1</b>'); 1989 } 1990 for (var i=0; i<gGoogleMatches.length; i++) { 1991 gGoogleMatches[i].__hilabel = gGoogleMatches[i].label.replace( 1992 queryRE, '<b>$1</b>'); 1993 } 1994 } 1995 1996 function search_focus_changed(obj, focused) 1997 { 1998 if (!focused) { 1999 if(obj.value == ""){ 2000 $(".search .close").addClass("hide"); 2001 } 2002 $(".suggest-card").hide(); 2003 } 2004 } 2005 2006 function submit_search() { 2007 var query = document.getElementById('search_autocomplete').value; 2008 location.hash = 'q=' + query; 2009 loadSearchResults(); 2010 $("#searchResults").slideDown('slow'); 2011 return false; 2012 } 2013 2014 2015 function hideResults() { 2016 $("#searchResults").slideUp(); 2017 $(".search .close").addClass("hide"); 2018 location.hash = ''; 2019 2020 $("#search_autocomplete").val("").blur(); 2021 2022 // reset the ajax search callback to nothing, so results don't appear unless ENTER 2023 searchControl.setSearchStartingCallback(this, function(control, searcher, query) {}); 2024 2025 // forcefully regain key-up event control (previously jacked by search api) 2026 $("#search_autocomplete").keyup(function(event) { 2027 return search_changed(event, false, toRoot); 2028 }); 2029 2030 return false; 2031 } 2032 2033 2034 2035 /* ########################################################## */ 2036 /* ################ CUSTOM SEARCH ENGINE ################## */ 2037 /* ########################################################## */ 2038 2039 var searchControl; 2040 google.load('search', '1', {"callback" : function() { 2041 searchControl = new google.search.SearchControl(); 2042 } }); 2043 2044 function loadSearchResults() { 2045 document.getElementById("search_autocomplete").style.color = "#000"; 2046 2047 searchControl = new google.search.SearchControl(); 2048 2049 // use our existing search form and use tabs when multiple searchers are used 2050 drawOptions = new google.search.DrawOptions(); 2051 drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED); 2052 drawOptions.setInput(document.getElementById("search_autocomplete")); 2053 2054 // configure search result options 2055 searchOptions = new google.search.SearcherOptions(); 2056 searchOptions.setExpandMode(GSearchControl.EXPAND_MODE_OPEN); 2057 2058 // configure each of the searchers, for each tab 2059 devSiteSearcher = new google.search.WebSearch(); 2060 devSiteSearcher.setUserDefinedLabel("All"); 2061 devSiteSearcher.setSiteRestriction("001482626316274216503:zu90b7s047u"); 2062 2063 designSearcher = new google.search.WebSearch(); 2064 designSearcher.setUserDefinedLabel("Design"); 2065 designSearcher.setSiteRestriction("http://developer.android.com/design/"); 2066 2067 trainingSearcher = new google.search.WebSearch(); 2068 trainingSearcher.setUserDefinedLabel("Training"); 2069 trainingSearcher.setSiteRestriction("http://developer.android.com/training/"); 2070 2071 guidesSearcher = new google.search.WebSearch(); 2072 guidesSearcher.setUserDefinedLabel("Guides"); 2073 guidesSearcher.setSiteRestriction("http://developer.android.com/guide/"); 2074 2075 referenceSearcher = new google.search.WebSearch(); 2076 referenceSearcher.setUserDefinedLabel("Reference"); 2077 referenceSearcher.setSiteRestriction("http://developer.android.com/reference/"); 2078 2079 googleSearcher = new google.search.WebSearch(); 2080 googleSearcher.setUserDefinedLabel("Google Services"); 2081 googleSearcher.setSiteRestriction("http://developer.android.com/google/"); 2082 2083 blogSearcher = new google.search.WebSearch(); 2084 blogSearcher.setUserDefinedLabel("Blog"); 2085 blogSearcher.setSiteRestriction("http://android-developers.blogspot.com"); 2086 2087 // add each searcher to the search control 2088 searchControl.addSearcher(devSiteSearcher, searchOptions); 2089 searchControl.addSearcher(designSearcher, searchOptions); 2090 searchControl.addSearcher(trainingSearcher, searchOptions); 2091 searchControl.addSearcher(guidesSearcher, searchOptions); 2092 searchControl.addSearcher(referenceSearcher, searchOptions); 2093 searchControl.addSearcher(googleSearcher, searchOptions); 2094 searchControl.addSearcher(blogSearcher, searchOptions); 2095 2096 // configure result options 2097 searchControl.setResultSetSize(google.search.Search.LARGE_RESULTSET); 2098 searchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF); 2099 searchControl.setTimeoutInterval(google.search.SearchControl.TIMEOUT_SHORT); 2100 searchControl.setNoResultsString(google.search.SearchControl.NO_RESULTS_DEFAULT_STRING); 2101 2102 // upon ajax search, refresh the url and search title 2103 searchControl.setSearchStartingCallback(this, function(control, searcher, query) { 2104 updateResultTitle(query); 2105 var query = document.getElementById('search_autocomplete').value; 2106 location.hash = 'q=' + query; 2107 }); 2108 2109 // once search results load, set up click listeners 2110 searchControl.setSearchCompleteCallback(this, function(control, searcher, query) { 2111 addResultClickListeners(); 2112 }); 2113 2114 // draw the search results box 2115 searchControl.draw(document.getElementById("leftSearchControl"), drawOptions); 2116 2117 // get query and execute the search 2118 searchControl.execute(decodeURI(getQuery(location.hash))); 2119 2120 document.getElementById("search_autocomplete").focus(); 2121 addTabListeners(); 2122 } 2123 // End of loadSearchResults 2124 2125 2126 google.setOnLoadCallback(function(){ 2127 if (location.hash.indexOf("q=") == -1) { 2128 // if there's no query in the url, don't search and make sure results are hidden 2129 $('#searchResults').hide(); 2130 return; 2131 } else { 2132 // first time loading search results for this page 2133 $('#searchResults').slideDown('slow'); 2134 $(".search .close").removeClass("hide"); 2135 loadSearchResults(); 2136 } 2137 }, true); 2138 2139 // when an event on the browser history occurs (back, forward, load) requery hash and do search 2140 $(window).hashchange( function(){ 2141 // Exit if the hash isn't a search query or there's an error in the query 2142 if ((location.hash.indexOf("q=") == -1) || (query == "undefined")) { 2143 // If the results pane is open, close it. 2144 if (!$("#searchResults").is(":hidden")) { 2145 hideResults(); 2146 } 2147 return; 2148 } 2149 2150 // Otherwise, we have a search to do 2151 var query = decodeURI(getQuery(location.hash)); 2152 searchControl.execute(query); 2153 $('#searchResults').slideDown('slow'); 2154 $("#search_autocomplete").focus(); 2155 $(".search .close").removeClass("hide"); 2156 2157 updateResultTitle(query); 2158 }); 2159 2160 function updateResultTitle(query) { 2161 $("#searchTitle").html("Results for <em>" + escapeHTML(query) + "</em>"); 2162 } 2163 2164 // forcefully regain key-up event control (previously jacked by search api) 2165 $("#search_autocomplete").keyup(function(event) { 2166 return search_changed(event, false, toRoot); 2167 }); 2168 2169 // add event listeners to each tab so we can track the browser history 2170 function addTabListeners() { 2171 var tabHeaders = $(".gsc-tabHeader"); 2172 for (var i = 0; i < tabHeaders.length; i++) { 2173 $(tabHeaders[i]).attr("id",i).click(function() { 2174 /* 2175 // make a copy of the page numbers for the search left pane 2176 setTimeout(function() { 2177 // remove any residual page numbers 2178 $('#searchResults .gsc-tabsArea .gsc-cursor-box.gs-bidi-start-align').remove(); 2179 // move the page numbers to the left position; make a clone, 2180 // because the element is drawn to the DOM only once 2181 // and because we're going to remove it (previous line), 2182 // we need it to be available to move again as the user navigates 2183 $('#searchResults .gsc-webResult .gsc-cursor-box.gs-bidi-start-align:visible') 2184 .clone().appendTo('#searchResults .gsc-tabsArea'); 2185 }, 200); 2186 */ 2187 }); 2188 } 2189 setTimeout(function(){$(tabHeaders[0]).click()},200); 2190 } 2191 2192 // add analytics tracking events to each result link 2193 function addResultClickListeners() { 2194 $("#searchResults a.gs-title").each(function(index, link) { 2195 // When user clicks enter for Google search results, track it 2196 $(link).click(function() { 2197 _gaq.push(['_trackEvent', 'Google Click', 'clicked: ' + $(this).text(), 2198 'from: ' + $("#search_autocomplete").val()]); 2199 }); 2200 }); 2201 } 2202 2203 2204 function getQuery(hash) { 2205 var queryParts = hash.split('='); 2206 return queryParts[1]; 2207 } 2208 2209 /* returns the given string with all HTML brackets converted to entities 2210 TODO: move this to the site's JS library */ 2211 function escapeHTML(string) { 2212 return string.replace(/</g,"<") 2213 .replace(/>/g,">"); 2214 } 2215 2216 2217 2218 2219 2220 2221 2222 /* ######################################################## */ 2223 /* ################# JAVADOC REFERENCE ################### */ 2224 /* ######################################################## */ 2225 2226 /* Initialize some droiddoc stuff, but only if we're in the reference */ 2227 if (location.pathname.indexOf("/reference")) { 2228 if(!location.pathname.indexOf("/reference-gms/packages.html") 2229 && !location.pathname.indexOf("/reference-gcm/packages.html") 2230 && !location.pathname.indexOf("/reference/com/google") == 0) { 2231 $(document).ready(function() { 2232 // init available apis based on user pref 2233 changeApiLevel(); 2234 initSidenavHeightResize() 2235 }); 2236 } 2237 } 2238 2239 var API_LEVEL_COOKIE = "api_level"; 2240 var minLevel = 1; 2241 var maxLevel = 1; 2242 2243 /******* SIDENAV DIMENSIONS ************/ 2244 2245 function initSidenavHeightResize() { 2246 // Change the drag bar size to nicely fit the scrollbar positions 2247 var $dragBar = $(".ui-resizable-s"); 2248 $dragBar.css({'width': $dragBar.parent().width() - 5 + "px"}); 2249 2250 $( "#resize-packages-nav" ).resizable({ 2251 containment: "#nav-panels", 2252 handles: "s", 2253 alsoResize: "#packages-nav", 2254 resize: function(event, ui) { resizeNav(); }, /* resize the nav while dragging */ 2255 stop: function(event, ui) { saveNavPanels(); } /* once stopped, save the sizes to cookie */ 2256 }); 2257 2258 } 2259 2260 function updateSidenavFixedWidth() { 2261 if (!navBarIsFixed) return; 2262 $('#devdoc-nav').css({ 2263 'width' : $('#side-nav').css('width'), 2264 'margin' : $('#side-nav').css('margin') 2265 }); 2266 $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'}); 2267 2268 initSidenavHeightResize(); 2269 } 2270 2271 function updateSidenavFullscreenWidth() { 2272 if (!navBarIsFixed) return; 2273 $('#devdoc-nav').css({ 2274 'width' : $('#side-nav').css('width'), 2275 'margin' : $('#side-nav').css('margin') 2276 }); 2277 $('#devdoc-nav .totop').css({'left': 'inherit'}); 2278 2279 initSidenavHeightResize(); 2280 } 2281 2282 function buildApiLevelSelector() { 2283 maxLevel = SINCE_DATA.length; 2284 var userApiLevel = parseInt(readCookie(API_LEVEL_COOKIE)); 2285 userApiLevel = userApiLevel == 0 ? maxLevel : userApiLevel; // If there's no cookie (zero), use the max by default 2286 2287 minLevel = parseInt($("#doc-api-level").attr("class")); 2288 // Handle provisional api levels; the provisional level will always be the highest possible level 2289 // Provisional api levels will also have a length; other stuff that's just missing a level won't, 2290 // so leave those kinds of entities at the default level of 1 (for example, the R.styleable class) 2291 if (isNaN(minLevel) && minLevel.length) { 2292 minLevel = maxLevel; 2293 } 2294 var select = $("#apiLevelSelector").html("").change(changeApiLevel); 2295 for (var i = maxLevel-1; i >= 0; i--) { 2296 var option = $("<option />").attr("value",""+SINCE_DATA[i]).append(""+SINCE_DATA[i]); 2297 // if (SINCE_DATA[i] < minLevel) option.addClass("absent"); // always false for strings (codenames) 2298 select.append(option); 2299 } 2300 2301 // get the DOM element and use setAttribute cuz IE6 fails when using jquery .attr('selected',true) 2302 var selectedLevelItem = $("#apiLevelSelector option[value='"+userApiLevel+"']").get(0); 2303 selectedLevelItem.setAttribute('selected',true); 2304 } 2305 2306 function changeApiLevel() { 2307 maxLevel = SINCE_DATA.length; 2308 var selectedLevel = maxLevel; 2309 2310 selectedLevel = parseInt($("#apiLevelSelector option:selected").val()); 2311 toggleVisisbleApis(selectedLevel, "body"); 2312 2313 var date = new Date(); 2314 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years 2315 var expiration = date.toGMTString(); 2316 writeCookie(API_LEVEL_COOKIE, selectedLevel, null, expiration); 2317 2318 if (selectedLevel < minLevel) { 2319 var thing = ($("#jd-header").html().indexOf("package") != -1) ? "package" : "class"; 2320 $("#naMessage").show().html("<div><p><strong>This " + thing 2321 + " requires API level " + minLevel + " or higher.</strong></p>" 2322 + "<p>This document is hidden because your selected API level for the documentation is " 2323 + selectedLevel + ". You can change the documentation API level with the selector " 2324 + "above the left navigation.</p>" 2325 + "<p>For more information about specifying the API level your app requires, " 2326 + "read <a href='" + toRoot + "training/basics/supporting-devices/platforms.html'" 2327 + ">Supporting Different Platform Versions</a>.</p>" 2328 + "<input type='button' value='OK, make this page visible' " 2329 + "title='Change the API level to " + minLevel + "' " 2330 + "onclick='$(\"#apiLevelSelector\").val(\"" + minLevel + "\");changeApiLevel();' />" 2331 + "</div>"); 2332 } else { 2333 $("#naMessage").hide(); 2334 } 2335 } 2336 2337 function toggleVisisbleApis(selectedLevel, context) { 2338 var apis = $(".api",context); 2339 apis.each(function(i) { 2340 var obj = $(this); 2341 var className = obj.attr("class"); 2342 var apiLevelIndex = className.lastIndexOf("-")+1; 2343 var apiLevelEndIndex = className.indexOf(" ", apiLevelIndex); 2344 apiLevelEndIndex = apiLevelEndIndex != -1 ? apiLevelEndIndex : className.length; 2345 var apiLevel = className.substring(apiLevelIndex, apiLevelEndIndex); 2346 if (apiLevel.length == 0) { // for odd cases when the since data is actually missing, just bail 2347 return; 2348 } 2349 apiLevel = parseInt(apiLevel); 2350 2351 // Handle provisional api levels; if this item's level is the provisional one, set it to the max 2352 var selectedLevelNum = parseInt(selectedLevel) 2353 var apiLevelNum = parseInt(apiLevel); 2354 if (isNaN(apiLevelNum)) { 2355 apiLevelNum = maxLevel; 2356 } 2357 2358 // Grey things out that aren't available and give a tooltip title 2359 if (apiLevelNum > selectedLevelNum) { 2360 obj.addClass("absent").attr("title","Requires API Level \"" 2361 + apiLevel + "\" or higher"); 2362 } 2363 else obj.removeClass("absent").removeAttr("title"); 2364 }); 2365 } 2366 2367 2368 2369 2370 /* ################# SIDENAV TREE VIEW ################### */ 2371 2372 function new_node(me, mom, text, link, children_data, api_level) 2373 { 2374 var node = new Object(); 2375 node.children = Array(); 2376 node.children_data = children_data; 2377 node.depth = mom.depth + 1; 2378 2379 node.li = document.createElement("li"); 2380 mom.get_children_ul().appendChild(node.li); 2381 2382 node.label_div = document.createElement("div"); 2383 node.label_div.className = "label"; 2384 if (api_level != null) { 2385 $(node.label_div).addClass("api"); 2386 $(node.label_div).addClass("api-level-"+api_level); 2387 } 2388 node.li.appendChild(node.label_div); 2389 2390 if (children_data != null) { 2391 node.expand_toggle = document.createElement("a"); 2392 node.expand_toggle.href = "javascript:void(0)"; 2393 node.expand_toggle.onclick = function() { 2394 if (node.expanded) { 2395 $(node.get_children_ul()).slideUp("fast"); 2396 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png"; 2397 node.expanded = false; 2398 } else { 2399 expand_node(me, node); 2400 } 2401 }; 2402 node.label_div.appendChild(node.expand_toggle); 2403 2404 node.plus_img = document.createElement("img"); 2405 node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png"; 2406 node.plus_img.className = "plus"; 2407 node.plus_img.width = "8"; 2408 node.plus_img.border = "0"; 2409 node.expand_toggle.appendChild(node.plus_img); 2410 2411 node.expanded = false; 2412 } 2413 2414 var a = document.createElement("a"); 2415 node.label_div.appendChild(a); 2416 node.label = document.createTextNode(text); 2417 a.appendChild(node.label); 2418 if (link) { 2419 a.href = me.toroot + link; 2420 } else { 2421 if (children_data != null) { 2422 a.className = "nolink"; 2423 a.href = "javascript:void(0)"; 2424 a.onclick = node.expand_toggle.onclick; 2425 // This next line shouldn't be necessary. I'll buy a beer for the first 2426 // person who figures out how to remove this line and have the link 2427 // toggle shut on the first try. --joeo (a] android.com 2428 node.expanded = false; 2429 } 2430 } 2431 2432 2433 node.children_ul = null; 2434 node.get_children_ul = function() { 2435 if (!node.children_ul) { 2436 node.children_ul = document.createElement("ul"); 2437 node.children_ul.className = "children_ul"; 2438 node.children_ul.style.display = "none"; 2439 node.li.appendChild(node.children_ul); 2440 } 2441 return node.children_ul; 2442 }; 2443 2444 return node; 2445 } 2446 2447 2448 2449 2450 function expand_node(me, node) 2451 { 2452 if (node.children_data && !node.expanded) { 2453 if (node.children_visited) { 2454 $(node.get_children_ul()).slideDown("fast"); 2455 } else { 2456 get_node(me, node); 2457 if ($(node.label_div).hasClass("absent")) { 2458 $(node.get_children_ul()).addClass("absent"); 2459 } 2460 $(node.get_children_ul()).slideDown("fast"); 2461 } 2462 node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png"; 2463 node.expanded = true; 2464 2465 // perform api level toggling because new nodes are new to the DOM 2466 var selectedLevel = $("#apiLevelSelector option:selected").val(); 2467 toggleVisisbleApis(selectedLevel, "#side-nav"); 2468 } 2469 } 2470 2471 function get_node(me, mom) 2472 { 2473 mom.children_visited = true; 2474 for (var i in mom.children_data) { 2475 var node_data = mom.children_data[i]; 2476 mom.children[i] = new_node(me, mom, node_data[0], node_data[1], 2477 node_data[2], node_data[3]); 2478 } 2479 } 2480 2481 function this_page_relative(toroot) 2482 { 2483 var full = document.location.pathname; 2484 var file = ""; 2485 if (toroot.substr(0, 1) == "/") { 2486 if (full.substr(0, toroot.length) == toroot) { 2487 return full.substr(toroot.length); 2488 } else { 2489 // the file isn't under toroot. Fail. 2490 return null; 2491 } 2492 } else { 2493 if (toroot != "./") { 2494 toroot = "./" + toroot; 2495 } 2496 do { 2497 if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") { 2498 var pos = full.lastIndexOf("/"); 2499 file = full.substr(pos) + file; 2500 full = full.substr(0, pos); 2501 toroot = toroot.substr(0, toroot.length-3); 2502 } 2503 } while (toroot != "" && toroot != "/"); 2504 return file.substr(1); 2505 } 2506 } 2507 2508 function find_page(url, data) 2509 { 2510 var nodes = data; 2511 var result = null; 2512 for (var i in nodes) { 2513 var d = nodes[i]; 2514 if (d[1] == url) { 2515 return new Array(i); 2516 } 2517 else if (d[2] != null) { 2518 result = find_page(url, d[2]); 2519 if (result != null) { 2520 return (new Array(i).concat(result)); 2521 } 2522 } 2523 } 2524 return null; 2525 } 2526 2527 function init_default_navtree(toroot) { 2528 // load json file for navtree data 2529 $.getScript(toRoot + 'navtree_data.js', function(data, textStatus, jqxhr) { 2530 // when the file is loaded, initialize the tree 2531 if(jqxhr.status === 200) { 2532 init_navtree("tree-list", toroot, NAVTREE_DATA); 2533 } 2534 }); 2535 2536 // perform api level toggling because because the whole tree is new to the DOM 2537 var selectedLevel = $("#apiLevelSelector option:selected").val(); 2538 toggleVisisbleApis(selectedLevel, "#side-nav"); 2539 } 2540 2541 function init_navtree(navtree_id, toroot, root_nodes) 2542 { 2543 var me = new Object(); 2544 me.toroot = toroot; 2545 me.node = new Object(); 2546 2547 me.node.li = document.getElementById(navtree_id); 2548 me.node.children_data = root_nodes; 2549 me.node.children = new Array(); 2550 me.node.children_ul = document.createElement("ul"); 2551 me.node.get_children_ul = function() { return me.node.children_ul; }; 2552 //me.node.children_ul.className = "children_ul"; 2553 me.node.li.appendChild(me.node.children_ul); 2554 me.node.depth = 0; 2555 2556 get_node(me, me.node); 2557 2558 me.this_page = this_page_relative(toroot); 2559 me.breadcrumbs = find_page(me.this_page, root_nodes); 2560 if (me.breadcrumbs != null && me.breadcrumbs.length != 0) { 2561 var mom = me.node; 2562 for (var i in me.breadcrumbs) { 2563 var j = me.breadcrumbs[i]; 2564 mom = mom.children[j]; 2565 expand_node(me, mom); 2566 } 2567 mom.label_div.className = mom.label_div.className + " selected"; 2568 addLoadEvent(function() { 2569 scrollIntoView("nav-tree"); 2570 }); 2571 } 2572 } 2573 2574 /* TODO: eliminate redundancy with non-google functions */ 2575 function init_google_navtree(navtree_id, toroot, root_nodes) 2576 { 2577 var me = new Object(); 2578 me.toroot = toroot; 2579 me.node = new Object(); 2580 2581 me.node.li = document.getElementById(navtree_id); 2582 me.node.children_data = root_nodes; 2583 me.node.children = new Array(); 2584 me.node.children_ul = document.createElement("ul"); 2585 me.node.get_children_ul = function() { return me.node.children_ul; }; 2586 //me.node.children_ul.className = "children_ul"; 2587 me.node.li.appendChild(me.node.children_ul); 2588 me.node.depth = 0; 2589 2590 get_google_node(me, me.node); 2591 } 2592 2593 function new_google_node(me, mom, text, link, children_data, api_level) 2594 { 2595 var node = new Object(); 2596 var child; 2597 node.children = Array(); 2598 node.children_data = children_data; 2599 node.depth = mom.depth + 1; 2600 node.get_children_ul = function() { 2601 if (!node.children_ul) { 2602 node.children_ul = document.createElement("ul"); 2603 node.children_ul.className = "tree-list-children"; 2604 node.li.appendChild(node.children_ul); 2605 } 2606 return node.children_ul; 2607 }; 2608 node.li = document.createElement("li"); 2609 2610 mom.get_children_ul().appendChild(node.li); 2611 2612 2613 if(link) { 2614 child = document.createElement("a"); 2615 2616 } 2617 else { 2618 child = document.createElement("span"); 2619 child.className = "tree-list-subtitle"; 2620 2621 } 2622 if (children_data != null) { 2623 node.li.className="nav-section"; 2624 node.label_div = document.createElement("div"); 2625 node.label_div.className = "nav-section-header-ref"; 2626 node.li.appendChild(node.label_div); 2627 get_google_node(me, node); 2628 node.label_div.appendChild(child); 2629 } 2630 else { 2631 node.li.appendChild(child); 2632 } 2633 if(link) { 2634 child.href = me.toroot + link; 2635 } 2636 node.label = document.createTextNode(text); 2637 child.appendChild(node.label); 2638 2639 node.children_ul = null; 2640 2641 return node; 2642 } 2643 2644 function get_google_node(me, mom) 2645 { 2646 mom.children_visited = true; 2647 var linkText; 2648 for (var i in mom.children_data) { 2649 var node_data = mom.children_data[i]; 2650 linkText = node_data[0]; 2651 2652 if(linkText.match("^"+"com.google.android")=="com.google.android"){ 2653 linkText = linkText.substr(19, linkText.length); 2654 } 2655 mom.children[i] = new_google_node(me, mom, linkText, node_data[1], 2656 node_data[2], node_data[3]); 2657 } 2658 } 2659 function showGoogleRefTree() { 2660 init_default_google_navtree(toRoot); 2661 init_default_gcm_navtree(toRoot); 2662 } 2663 2664 function init_default_google_navtree(toroot) { 2665 // load json file for navtree data 2666 $.getScript(toRoot + 'gms_navtree_data.js', function(data, textStatus, jqxhr) { 2667 // when the file is loaded, initialize the tree 2668 if(jqxhr.status === 200) { 2669 init_google_navtree("gms-tree-list", toroot, GMS_NAVTREE_DATA); 2670 highlightSidenav(); 2671 resizeNav(); 2672 } 2673 }); 2674 } 2675 2676 function init_default_gcm_navtree(toroot) { 2677 // load json file for navtree data 2678 $.getScript(toRoot + 'gcm_navtree_data.js', function(data, textStatus, jqxhr) { 2679 // when the file is loaded, initialize the tree 2680 if(jqxhr.status === 200) { 2681 init_google_navtree("gcm-tree-list", toroot, GCM_NAVTREE_DATA); 2682 highlightSidenav(); 2683 resizeNav(); 2684 } 2685 }); 2686 } 2687 2688 /* TOGGLE INHERITED MEMBERS */ 2689 2690 /* Toggle an inherited class (arrow toggle) 2691 * @param linkObj The link that was clicked. 2692 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed. 2693 * 'null' to simply toggle. 2694 */ 2695 function toggleInherited(linkObj, expand) { 2696 var base = linkObj.getAttribute("id"); 2697 var list = document.getElementById(base + "-list"); 2698 var summary = document.getElementById(base + "-summary"); 2699 var trigger = document.getElementById(base + "-trigger"); 2700 var a = $(linkObj); 2701 if ( (expand == null && a.hasClass("closed")) || expand ) { 2702 list.style.display = "none"; 2703 summary.style.display = "block"; 2704 trigger.src = toRoot + "assets/images/triangle-opened.png"; 2705 a.removeClass("closed"); 2706 a.addClass("opened"); 2707 } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) { 2708 list.style.display = "block"; 2709 summary.style.display = "none"; 2710 trigger.src = toRoot + "assets/images/triangle-closed.png"; 2711 a.removeClass("opened"); 2712 a.addClass("closed"); 2713 } 2714 return false; 2715 } 2716 2717 /* Toggle all inherited classes in a single table (e.g. all inherited methods) 2718 * @param linkObj The link that was clicked. 2719 * @param expand 'true' to ensure it's expanded. 'false' to ensure it's closed. 2720 * 'null' to simply toggle. 2721 */ 2722 function toggleAllInherited(linkObj, expand) { 2723 var a = $(linkObj); 2724 var table = $(a.parent().parent().parent()); // ugly way to get table/tbody 2725 var expandos = $(".jd-expando-trigger", table); 2726 if ( (expand == null && a.text() == "[Expand]") || expand ) { 2727 expandos.each(function(i) { 2728 toggleInherited(this, true); 2729 }); 2730 a.text("[Collapse]"); 2731 } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) { 2732 expandos.each(function(i) { 2733 toggleInherited(this, false); 2734 }); 2735 a.text("[Expand]"); 2736 } 2737 return false; 2738 } 2739 2740 /* Toggle all inherited members in the class (link in the class title) 2741 */ 2742 function toggleAllClassInherited() { 2743 var a = $("#toggleAllClassInherited"); // get toggle link from class title 2744 var toggles = $(".toggle-all", $("#body-content")); 2745 if (a.text() == "[Expand All]") { 2746 toggles.each(function(i) { 2747 toggleAllInherited(this, true); 2748 }); 2749 a.text("[Collapse All]"); 2750 } else { 2751 toggles.each(function(i) { 2752 toggleAllInherited(this, false); 2753 }); 2754 a.text("[Expand All]"); 2755 } 2756 return false; 2757 } 2758 2759 /* Expand all inherited members in the class. Used when initiating page search */ 2760 function ensureAllInheritedExpanded() { 2761 var toggles = $(".toggle-all", $("#body-content")); 2762 toggles.each(function(i) { 2763 toggleAllInherited(this, true); 2764 }); 2765 $("#toggleAllClassInherited").text("[Collapse All]"); 2766 } 2767 2768 2769 /* HANDLE KEY EVENTS 2770 * - Listen for Ctrl+F (Cmd on Mac) and expand all inherited members (to aid page search) 2771 */ 2772 var agent = navigator['userAgent'].toLowerCase(); 2773 var mac = agent.indexOf("macintosh") != -1; 2774 2775 $(document).keydown( function(e) { 2776 var control = mac ? e.metaKey && !e.ctrlKey : e.ctrlKey; // get ctrl key 2777 if (control && e.which == 70) { // 70 is "F" 2778 ensureAllInheritedExpanded(); 2779 } 2780 }); 2781