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