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