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