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