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