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