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