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