1 var resizePackagesNav; 2 var classesNav; 3 var devdocNav; 4 var sidenav; 5 var content; 6 var HEADER_HEIGHT = 117; 7 var cookie_namespace = 'android_developer'; 8 var NAV_PREF_TREE = "tree"; 9 var NAV_PREF_PANELS = "panels"; 10 var nav_pref; 11 var toRoot; 12 var isMobile = false; // true if mobile, so we can adjust some layout 13 var isIE6 = false; // true if IE6 14 15 // TODO: use $(document).ready instead 16 function addLoadEvent(newfun) { 17 var current = window.onload; 18 if (typeof window.onload != 'function') { 19 window.onload = newfun; 20 } else { 21 window.onload = function() { 22 current(); 23 newfun(); 24 } 25 } 26 } 27 28 var agent = navigator['userAgent'].toLowerCase(); 29 // If a mobile phone, set flag and do mobile setup 30 if ((agent.indexOf("mobile") != -1) || // android, iphone, ipod 31 (agent.indexOf("blackberry") != -1) || 32 (agent.indexOf("webos") != -1) || 33 (agent.indexOf("mini") != -1)) { // opera mini browsers 34 isMobile = true; 35 addLoadEvent(mobileSetup); 36 // If not a mobile browser, set the onresize event for IE6, and others 37 } else if (agent.indexOf("msie 6") != -1) { 38 isIE6 = true; 39 addLoadEvent(function() { 40 window.onresize = resizeAll; 41 }); 42 } else { 43 addLoadEvent(function() { 44 window.onresize = resizeHeight; 45 }); 46 } 47 48 function mobileSetup() { 49 $("body").css({'overflow':'auto'}); 50 $("html").css({'overflow':'auto'}); 51 $("#body-content").css({'position':'relative', 'top':'0'}); 52 $("#doc-content").css({'overflow':'visible', 'border-left':'3px solid #DDD'}); 53 $("#side-nav").css({'padding':'0'}); 54 $("#nav-tree").css({'overflow-y': 'auto'}); 55 } 56 57 /* loads the lists.js file to the page. 58 Loading this in the head was slowing page load time */ 59 addLoadEvent( function() { 60 var lists = document.createElement("script"); 61 lists.setAttribute("type","text/javascript"); 62 lists.setAttribute("src", toRoot+"reference/lists.js"); 63 document.getElementsByTagName("head")[0].appendChild(lists); 64 } ); 65 66 addLoadEvent( function() { 67 $("pre:not(.no-pretty-print)").addClass("prettyprint"); 68 prettyPrint(); 69 } ); 70 71 function setToRoot(root) { 72 toRoot = root; 73 // note: toRoot also used by carousel.js 74 } 75 76 function restoreWidth(navWidth) { 77 var windowWidth = $(window).width() + "px"; 78 content.css({marginLeft:parseInt(navWidth) + 6 + "px"}); //account for 6px-wide handle-bar 79 80 if (isIE6) { 81 content.css({width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"}); // necessary in order for scrollbars to be visible 82 } 83 84 sidenav.css({width:navWidth}); 85 resizePackagesNav.css({width:navWidth}); 86 classesNav.css({width:navWidth}); 87 $("#packages-nav").css({width:navWidth}); 88 } 89 90 function restoreHeight(packageHeight) { 91 var windowHeight = ($(window).height() - HEADER_HEIGHT); 92 var swapperHeight = windowHeight - 13; 93 $("#swapper").css({height:swapperHeight + "px"}); 94 sidenav.css({height:windowHeight + "px"}); 95 content.css({height:windowHeight + "px"}); 96 resizePackagesNav.css({maxHeight:swapperHeight + "px", height:packageHeight}); 97 classesNav.css({height:swapperHeight - parseInt(packageHeight) + "px"}); 98 $("#packages-nav").css({height:parseInt(packageHeight) - 6 + "px"}); //move 6px to give space for the resize handle 99 devdocNav.css({height:sidenav.css("height")}); 100 $("#nav-tree").css({height:swapperHeight + "px"}); 101 } 102 103 function readCookie(cookie) { 104 var myCookie = cookie_namespace+"_"+cookie+"="; 105 if (document.cookie) { 106 var index = document.cookie.indexOf(myCookie); 107 if (index != -1) { 108 var valStart = index + myCookie.length; 109 var valEnd = document.cookie.indexOf(";", valStart); 110 if (valEnd == -1) { 111 valEnd = document.cookie.length; 112 } 113 var val = document.cookie.substring(valStart, valEnd); 114 return val; 115 } 116 } 117 return 0; 118 } 119 120 function writeCookie(cookie, val, section, expiration) { 121 if (val==undefined) return; 122 section = section == null ? "_" : "_"+section+"_"; 123 if (expiration == null) { 124 var date = new Date(); 125 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week 126 expiration = date.toGMTString(); 127 } 128 document.cookie = cookie_namespace + section + cookie + "=" + val + "; expires=" + expiration+"; path=/"; 129 } 130 131 function init() { 132 $("#side-nav").css({position:"absolute",left:0}); 133 content = $("#doc-content"); 134 resizePackagesNav = $("#resize-packages-nav"); 135 classesNav = $("#classes-nav"); 136 sidenav = $("#side-nav"); 137 devdocNav = $("#devdoc-nav"); 138 139 var cookiePath = ""; 140 if (location.href.indexOf("/reference/") != -1) { 141 cookiePath = "reference_"; 142 } else if (location.href.indexOf("/guide/") != -1) { 143 cookiePath = "guide_"; 144 } else if (location.href.indexOf("/sdk/") != -1) { 145 cookiePath = "sdk_"; 146 } else if ((location.href.indexOf("/resources/") != -1) || 147 (location.href.indexOf("/training/") != -1)) { 148 cookiePath = "resources_"; 149 } 150 151 if (!isMobile) { 152 $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizePackagesHeight(); } }); 153 $("#side-nav").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } }); 154 var cookieWidth = readCookie(cookiePath+'width'); 155 var cookieHeight = readCookie(cookiePath+'height'); 156 if (cookieWidth) { 157 restoreWidth(cookieWidth); 158 } else if ($("#side-nav").length) { 159 resizeWidth(); 160 } 161 if (cookieHeight) { 162 restoreHeight(cookieHeight); 163 } else { 164 resizeHeight(); 165 } 166 } 167 168 if (devdocNav.length) { // only dev guide, resources, and sdk 169 tryPopulateResourcesNav(); 170 highlightNav(location.href); 171 } 172 } 173 174 function tryPopulateResourcesNav() { 175 var sampleList = $('#devdoc-nav-sample-list'); 176 var articleList = $('#devdoc-nav-article-list'); 177 var tutorialList = $('#devdoc-nav-tutorial-list'); 178 var topicList = $('#devdoc-nav-topic-list'); 179 180 if (!topicList.length || !ANDROID_TAGS || !ANDROID_RESOURCES) 181 return; 182 183 var topics = []; 184 for (var topic in ANDROID_TAGS['topic']) { 185 topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]}); 186 } 187 topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; }); 188 for (var i = 0; i < topics.length; i++) { 189 topicList.append( 190 $('<li>').append( 191 $('<a>') 192 .attr('href', toRoot + "resources/browser.html?tag=" + topics[i].name) 193 .append($('<span>') 194 .addClass('en') 195 .html(topics[i].title) 196 ) 197 ) 198 ); 199 } 200 201 var _renderResourceList = function(tag, listNode) { 202 var resources = []; 203 var tags; 204 var resource; 205 var i, j; 206 for (i = 0; i < ANDROID_RESOURCES.length; i++) { 207 resource = ANDROID_RESOURCES[i]; 208 tags = resource.tags || []; 209 var hasTag = false; 210 for (j = 0; j < tags.length; j++) 211 if (tags[j] == tag) { 212 hasTag = true; 213 break; 214 } 215 if (!hasTag) 216 continue; 217 resources.push(resource); 218 } 219 //resources.sort(function(x,y){ return (x.title.en < y.title.en) ? -1 : 1; }); 220 for (i = 0; i < resources.length; i++) { 221 resource = resources[i]; 222 var listItemNode = $('<li>').append( 223 $('<a>') 224 .attr('href', toRoot + "resources/" + resource.path) 225 .append($('<span>') 226 .addClass('en') 227 .html(resource.title.en) 228 ) 229 ); 230 tags = resource.tags || []; 231 for (j = 0; j < tags.length; j++) { 232 if (tags[j] == 'new') { 233 listItemNode.get(0).innerHTML += ' <span class="new">new!</span>'; 234 break; 235 } else if (tags[j] == 'updated') { 236 listItemNode.get(0).innerHTML += ' <span class="new">updated!</span>'; 237 break; 238 } 239 } 240 listNode.append(listItemNode); 241 } 242 }; 243 244 _renderResourceList('sample', sampleList); 245 _renderResourceList('article', articleList); 246 _renderResourceList('tutorial', tutorialList); 247 } 248 249 function highlightNav(fullPageName) { 250 var lastSlashPos = fullPageName.lastIndexOf("/"); 251 var firstSlashPos; 252 if (fullPageName.indexOf("/guide/") != -1) { 253 firstSlashPos = fullPageName.indexOf("/guide/"); 254 } else if (fullPageName.indexOf("/sdk/") != -1) { 255 firstSlashPos = fullPageName.indexOf("/sdk/"); 256 } else if (fullPageName.indexOf("/resources/") != -1) { 257 firstSlashPos = fullPageName.indexOf("/resources/"); 258 } else if (fullPageName.indexOf("/training/") != -1) { 259 firstSlashPos = fullPageName.indexOf("/training/"); 260 } 261 if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html') 262 fullPageName = fullPageName + "index.html"; 263 } 264 265 // get the path and page name from the URL (such as 'guide/topics/graphics/index.html') 266 var htmlPos = fullPageName.indexOf(".html"); 267 var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5); // +5 advances past ".html" 268 // find instances of the page name in the side nav 269 var link = $("#devdoc-nav a[href$='"+ pathPageName+"']"); 270 // if there's no match, then let's backstep through the directory until we find an index.html 271 // page that matches our ancestor directories (only for dev guide and resources) 272 if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || 273 (fullPageName.indexOf("/resources/") != -1))) { 274 lastBackstep = pathPageName.lastIndexOf("/"); 275 while (link.length == 0) { 276 backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep); 277 link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 278 1)+"index.html']"); 279 lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1); 280 if (lastBackstep == 0) break; 281 } 282 } 283 284 // add 'selected' to the <li> or <div> that wraps this <a> 285 link.parent().addClass('selected'); 286 287 // if we're in a toggleable root link (<li class=toggle-list><div><a>) 288 if (link.parent().parent().hasClass('toggle-list')) { 289 toggle(link.parent().parent(), false); // open our own list 290 // then also check if we're in a third-level nested list that's toggleable 291 if (link.parent().parent().parent().is(':hidden')) { 292 toggle(link.parent().parent().parent().parent(), false); // open the super parent list 293 } 294 } 295 // if we're in a normal nav link (<li><a>) and the parent <ul> is hidden 296 else if (link.parent().parent().is(':hidden')) { 297 toggle(link.parent().parent().parent(), false); // open the parent list 298 // then also check if the parent list is also nested in a hidden list 299 if (link.parent().parent().parent().parent().is(':hidden')) { 300 toggle(link.parent().parent().parent().parent().parent(), false); // open the super parent list 301 } 302 } 303 } 304 305 /* Resize the height of the nav panels in the reference, 306 * and save the new size to a cookie */ 307 function resizePackagesHeight() { 308 var windowHeight = ($(window).height() - HEADER_HEIGHT); 309 var swapperHeight = windowHeight - 13; // move 13px for swapper link at the bottom 310 resizePackagesNav.css({maxHeight:swapperHeight + "px"}); 311 classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); 312 313 $("#swapper").css({height:swapperHeight + "px"}); 314 $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle 315 316 var basePath = getBaseUri(location.pathname); 317 var section = basePath.substring(1,basePath.indexOf("/",1)); 318 writeCookie("height", resizePackagesNav.css("height"), section, null); 319 } 320 321 /* Resize the height of the side-nav and doc-content divs, 322 * which creates the frame effect */ 323 function resizeHeight() { 324 var docContent = $("#doc-content"); 325 326 // Get the window height and always resize the doc-content and side-nav divs 327 var windowHeight = ($(window).height() - HEADER_HEIGHT); 328 docContent.css({height:windowHeight + "px"}); 329 $("#side-nav").css({height:windowHeight + "px"}); 330 331 var href = location.href; 332 // If in the reference docs, also resize the "swapper", "classes-nav", and "nav-tree" divs 333 if (href.indexOf("/reference/") != -1) { 334 var swapperHeight = windowHeight - 13; 335 $("#swapper").css({height:swapperHeight + "px"}); 336 $("#classes-nav").css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"}); 337 $("#nav-tree").css({height:swapperHeight + "px"}); 338 339 // Also resize the "devdoc-nav" div 340 } else if ($("#devdoc-nav").length) { 341 $("#devdoc-nav").css({height:sidenav.css("height")}); 342 } 343 344 // Hide the "Go to top" link if there's no vertical scroll 345 if ( parseInt($("#jd-content").css("height")) <= parseInt(docContent.css("height")) ) { 346 $("a[href='#top']").css({'display':'none'}); 347 } else { 348 $("a[href='#top']").css({'display':'inline'}); 349 } 350 } 351 352 /* Resize the width of the "side-nav" and the left margin of the "doc-content" div, 353 * which creates the resizable side bar */ 354 function resizeWidth() { 355 var windowWidth = $(window).width() + "px"; 356 var sidenav = $("#side-nav"); 357 if (sidenav.length) { 358 var sidenavWidth = sidenav.css("width"); 359 } else { 360 var sidenavWidth = 0; 361 } 362 content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px"}); //account for 6px-wide handle-bar 363 364 if (isIE6) { 365 content.css({width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"}); // necessary in order to for scrollbars to be visible 366 } 367 368 resizePackagesNav.css({width:sidenavWidth}); 369 classesNav.css({width:sidenavWidth}); 370 $("#packages-nav").css({width:sidenavWidth}); 371 372 if (sidenav.length) { // Must check if the nav exists because IE6 calls resizeWidth() from resizeAll() for all pages 373 var basePath = getBaseUri(location.pathname); 374 var section = basePath.substring(1,basePath.indexOf("/",1)); 375 writeCookie("width", sidenavWidth, section, null); 376 } 377 } 378 379 /* For IE6 only, 380 * because it can't properly perform auto width for "doc-content" div, 381 * avoiding this for all browsers provides better performance */ 382 function resizeAll() { 383 resizeHeight(); 384 resizeWidth(); 385 } 386 387 function getBaseUri(uri) { 388 var intlUrl = (uri.substring(0,6) == "/intl/"); 389 if (intlUrl) { 390 base = uri.substring(uri.indexOf('intl/')+5,uri.length); 391 base = base.substring(base.indexOf('/')+1, base.length); 392 //alert("intl, returning base url: /" + base); 393 return ("/" + base); 394 } else { 395 //alert("not intl, returning uri as found."); 396 return uri; 397 } 398 } 399 400 function requestAppendHL(uri) { 401 //append "?hl=<lang> to an outgoing request (such as to blog) 402 var lang = getLangPref(); 403 if (lang) { 404 var q = 'hl=' + lang; 405 uri += '?' + q; 406 window.location = uri; 407 return false; 408 } else { 409 return true; 410 } 411 } 412 413 function loadLast(cookiePath) { 414 var location = window.location.href; 415 if (location.indexOf("/"+cookiePath+"/") != -1) { 416 return true; 417 } 418 var lastPage = readCookie(cookiePath + "_lastpage"); 419 if (lastPage) { 420 window.location = lastPage; 421 return false; 422 } 423 return true; 424 } 425 426 $(window).unload(function(){ 427 var path = getBaseUri(location.pathname); 428 if (path.indexOf("/reference/") != -1) { 429 writeCookie("lastpage", path, "reference", null); 430 } else if (path.indexOf("/guide/") != -1) { 431 writeCookie("lastpage", path, "guide", null); 432 } else if ((path.indexOf("/resources/") != -1) || (path.indexOf("/training/") != -1)) { 433 writeCookie("lastpage", path, "resources", null); 434 } 435 }); 436 437 function toggle(obj, slide) { 438 var ul = $("ul:first", obj); 439 var li = ul.parent(); 440 if (li.hasClass("closed")) { 441 if (slide) { 442 ul.slideDown("fast"); 443 } else { 444 ul.show(); 445 } 446 li.removeClass("closed"); 447 li.addClass("open"); 448 $(".toggle-img", li).attr("title", "hide pages"); 449 } else { 450 ul.slideUp("fast"); 451 li.removeClass("open"); 452 li.addClass("closed"); 453 $(".toggle-img", li).attr("title", "show pages"); 454 } 455 } 456 457 function buildToggleLists() { 458 $(".toggle-list").each( 459 function(i) { 460 $("div:first", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>"); 461 $(this).addClass("closed"); 462 }); 463 } 464 465 function getNavPref() { 466 var v = readCookie('reference_nav'); 467 if (v != NAV_PREF_TREE) { 468 v = NAV_PREF_PANELS; 469 } 470 return v; 471 } 472 473 function chooseDefaultNav() { 474 nav_pref = getNavPref(); 475 if (nav_pref == NAV_PREF_TREE) { 476 $("#nav-panels").toggle(); 477 $("#panel-link").toggle(); 478 $("#nav-tree").toggle(); 479 $("#tree-link").toggle(); 480 } 481 } 482 483 function swapNav() { 484 if (nav_pref == NAV_PREF_TREE) { 485 nav_pref = NAV_PREF_PANELS; 486 } else { 487 nav_pref = NAV_PREF_TREE; 488 init_default_navtree(toRoot); 489 } 490 var date = new Date(); 491 date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years 492 writeCookie("nav", nav_pref, "reference", date.toGMTString()); 493 494 $("#nav-panels").toggle(); 495 $("#panel-link").toggle(); 496 $("#nav-tree").toggle(); 497 $("#tree-link").toggle(); 498 499 if ($("#nav-tree").is(':visible')) scrollIntoView("nav-tree"); 500 else { 501 scrollIntoView("packages-nav"); 502 scrollIntoView("classes-nav"); 503 } 504 } 505 506 function scrollIntoView(nav) { 507 var navObj = $("#"+nav); 508 if (navObj.is(':visible')) { 509 var selected = $(".selected", navObj); 510 if (selected.length == 0) return; 511 if (selected.is("div")) selected = selected.parent(); // when the selected item is a parent 512 513 var scrolling = document.getElementById(nav); 514 var navHeight = navObj.height(); 515 var offsetTop = selected.position().top; 516 517 // handle nested items 518 if (selected.parent().parent().is(".toggle-list")) { 519 selected = selected.parent().parent(); 520 // handle second level nested items 521 if (selected.parent().parent().is(".toggle-list")) { 522 selected = selected.parent().parent(); 523 } 524 offsetTop += selected.position().top; 525 } 526 527 // 180px from the bottom of the list is the threshold 528 if(offsetTop > navHeight - 180) { 529 scrolling.scrollTop = offsetTop - navHeight + 180; 530 } 531 } 532 } 533 534 function changeTabLang(lang) { 535 var nodes = $("#header-tabs").find("."+lang); 536 for (i=0; i < nodes.length; i++) { // for each node in this language 537 var node = $(nodes[i]); 538 node.siblings().css("display","none"); // hide all siblings 539 if (node.not(":empty").length != 0) { //if this languages node has a translation, show it 540 node.css("display","inline"); 541 } else { //otherwise, show English instead 542 node.css("display","none"); 543 node.siblings().filter(".en").css("display","inline"); 544 } 545 } 546 } 547 548 function changeNavLang(lang) { 549 var nodes = $("#side-nav").find("."+lang); 550 for (i=0; i < nodes.length; i++) { // for each node in this language 551 var node = $(nodes[i]); 552 node.siblings().css("display","none"); // hide all siblings 553 if (node.not(":empty").length != 0) { // if this languages node has a translation, show it 554 node.css("display","inline"); 555 } else { // otherwise, show English instead 556 node.css("display","none"); 557 node.siblings().filter(".en").css("display","inline"); 558 } 559 } 560 } 561 562 function changeDocLang(lang) { 563 changeTabLang(lang); 564 changeNavLang(lang); 565 } 566 567 function changeLangPref(lang, refresh) { 568 var date = new Date(); 569 expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years 570 //alert("expires: " + expires) 571 writeCookie("pref_lang", lang, null, expires); 572 //changeDocLang(lang); 573 if (refresh) { 574 l = getBaseUri(location.pathname); 575 window.location = l; 576 } 577 } 578 579 function loadLangPref() { 580 var lang = readCookie("pref_lang"); 581 if (lang != 0) { 582 $("#language").find("option[value='"+lang+"']").attr("selected",true); 583 } 584 } 585 586 function getLangPref() { 587 var lang = $("#language").find(":selected").attr("value"); 588 if (!lang) { 589 lang = readCookie("pref_lang"); 590 } 591 return (lang != 0) ? lang : 'en'; 592 } 593 594 595 /* Used to hide and reveal supplemental content, such as long code samples. 596 See the companion CSS in android-developer-docs.css */ 597 function toggleContent(obj) { 598 var div = $(obj.parentNode.parentNode); 599 var toggleMe = $(".toggle-content-toggleme",div); 600 if (div.hasClass("closed")) { // if it's closed, open it 601 toggleMe.slideDown(); 602 $(".toggle-content-text", obj).toggle(); 603 div.removeClass("closed").addClass("open"); 604 $(".toggle-content-img", div).attr("title", "hide").attr("src", toRoot + "assets/images/triangle-opened.png"); 605 } else { // if it's open, close it 606 toggleMe.slideUp('fast', function() { // Wait until the animation is done before closing arrow 607 $(".toggle-content-text", obj).toggle(); 608 div.removeClass("open").addClass("closed"); 609 $(".toggle-content-img", div).attr("title", "show").attr("src", toRoot + "assets/images/triangle-closed.png"); 610 }); 611 } 612 return false; 613 } 614