Scott Main | e4d8f1b | 2012-06-21 18:03:05 -0700 | [diff] [blame^] | 1 | var navBarIsFixed = false; |
| 2 | $(document).ready(function() { |
| 3 | // init the fullscreen toggle click event |
| 4 | $('#nav-swap .fullscreen').click(function(){ |
| 5 | if ($(this).hasClass('disabled')) { |
| 6 | toggleFullscreen(true); |
| 7 | } else { |
| 8 | toggleFullscreen(false); |
| 9 | } |
| 10 | }); |
| 11 | |
| 12 | // initialize the divs with custom scrollbars |
| 13 | $('.scroll-pane').jScrollPane( {verticalGutter:0} ); |
| 14 | |
| 15 | // add HRs below all H2s (except for a few other h2 variants) |
| 16 | $('h2').not('#qv h2').not('#tb h2').not('#devdoc-nav h2').css({marginBottom:0}).after('<hr/>'); |
| 17 | |
| 18 | // set search's onkeyup handler here so we can show suggestions even while search results are visible |
| 19 | $("#search_autocomplete").keyup(function() {return search_changed(event, false, '/')}); |
| 20 | |
| 21 | // set up the search close button |
| 22 | $('.search .close').click(function() { |
| 23 | $searchInput = $('#search_autocomplete'); |
| 24 | $searchInput.attr('value', ''); |
| 25 | $(this).addClass("hide"); |
| 26 | $("#search-container").removeClass('active'); |
| 27 | $("#search_autocomplete").blur(); |
| 28 | search_focus_changed($searchInput.get(), false); // see search_autocomplete.js |
| 29 | hideResults(); // see search_autocomplete.js |
| 30 | }); |
| 31 | $('.search').click(function() { |
| 32 | if (!$('#search_autocomplete').is(":focused")) { |
| 33 | $('#search_autocomplete').focus(); |
| 34 | } |
| 35 | }); |
| 36 | |
| 37 | // Set up quicknav |
| 38 | var quicknav_open = false; |
| 39 | $("#btn-quicknav").click(function() { |
| 40 | if (quicknav_open) { |
| 41 | $(this).removeClass('active'); |
| 42 | quicknav_open = false; |
| 43 | collapse(); |
| 44 | } else { |
| 45 | $(this).addClass('active'); |
| 46 | quicknav_open = true; |
| 47 | expand(); |
| 48 | } |
| 49 | }) |
| 50 | |
| 51 | var expand = function() { |
| 52 | $('#header-wrap').addClass('quicknav'); |
| 53 | $('#quicknav').stop().show().animate({opacity:'1'}); |
| 54 | } |
| 55 | |
| 56 | var collapse = function() { |
| 57 | $('#quicknav').stop().animate({opacity:'0'}, 100, function() { |
| 58 | $(this).hide(); |
| 59 | $('#header-wrap').removeClass('quicknav'); |
| 60 | }); |
| 61 | } |
| 62 | |
| 63 | |
| 64 | //Set up search |
| 65 | $("#search_autocomplete").focus(function() { |
| 66 | $("#search-container").addClass('active'); |
| 67 | }) |
| 68 | $("#search-container").mouseover(function() { |
| 69 | $("#search-container").addClass('active'); |
| 70 | $("#search_autocomplete").focus(); |
| 71 | }) |
| 72 | $("#search-container").mouseout(function() { |
| 73 | if ($("#search_autocomplete").is(":focus")) return; |
| 74 | if ($("#search_autocomplete").val() == '') { |
| 75 | setTimeout(function(){ |
| 76 | $("#search-container").removeClass('active'); |
| 77 | $("#search_autocomplete").blur(); |
| 78 | },250); |
| 79 | } |
| 80 | }) |
| 81 | $("#search_autocomplete").blur(function() { |
| 82 | if ($("#search_autocomplete").val() == '') { |
| 83 | $("#search-container").removeClass('active'); |
| 84 | } |
| 85 | }) |
| 86 | |
| 87 | |
| 88 | // prep nav expandos |
| 89 | var pagePath = document.location.pathname; |
| 90 | // account for intl docs by removing the intl/*/ path |
| 91 | if (pagePath.indexOf("/intl/") == 0) { |
| 92 | pagePath = pagePath.substr(pagePath.indexOf("/",6)); // start after intl/ to get last / |
| 93 | } |
| 94 | |
| 95 | if (pagePath.indexOf(SITE_ROOT) == 0) { |
| 96 | if (pagePath == '' || pagePath.charAt(pagePath.length - 1) == '/') { |
| 97 | pagePath += 'index.html'; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | if (SITE_ROOT.match(/\.\.\//) || SITE_ROOT == '') { |
| 102 | // If running locally, SITE_ROOT will be a relative path, so account for that by |
| 103 | // finding the relative URL to this page. This will allow us to find links on the page |
| 104 | // leading back to this page. |
| 105 | var pathParts = pagePath.split('/'); |
| 106 | var relativePagePathParts = []; |
| 107 | var upDirs = (SITE_ROOT.match(/(\.\.\/)+/) || [''])[0].length / 3; |
| 108 | for (var i = 0; i < upDirs; i++) { |
| 109 | relativePagePathParts.push('..'); |
| 110 | } |
| 111 | for (var i = 0; i < upDirs; i++) { |
| 112 | relativePagePathParts.push(pathParts[pathParts.length - (upDirs - i) - 1]); |
| 113 | } |
| 114 | relativePagePathParts.push(pathParts[pathParts.length - 1]); |
| 115 | pagePath = relativePagePathParts.join('/'); |
| 116 | } else { |
| 117 | // Otherwise the page path is already an absolute URL |
| 118 | } |
| 119 | |
| 120 | // select current page in sidenav and set up prev/next links if they exist |
| 121 | var $selNavLink = $('#nav').find('a[href="' + pagePath + '"]'); |
| 122 | if ($selNavLink.length) { |
| 123 | $selListItem = $selNavLink.closest('li'); |
| 124 | |
| 125 | $selListItem.addClass('selected'); |
| 126 | $selListItem.closest('li.nav-section').addClass('expanded'); |
| 127 | $selListItem.closest('li.nav-section').children('ul').show(); |
| 128 | $selListItem.closest('li.nav-section').parent().closest('li.nav-section').addClass('expanded'); |
| 129 | $selListItem.closest('li.nav-section').parent().closest('ul').show(); |
| 130 | |
| 131 | |
| 132 | // $selListItem.closest('li.nav-section').closest('li.nav-section').addClass('expanded'); |
| 133 | // $selListItem.closest('li.nav-section').closest('li.nav-section').children('ul').show(); |
| 134 | |
| 135 | // set up prev links |
| 136 | var $prevLink = []; |
| 137 | var $prevListItem = $selListItem.prev('li'); |
| 138 | |
| 139 | var crossBoundaries = ($("body.design").length > 0) || ($("body.guide").length > 0) ? true : false; // navigate across topic boundaries only in design docs |
| 140 | if ($prevListItem.length) { |
| 141 | if ($prevListItem.hasClass('nav-section')) { |
| 142 | if (crossBoundaries) { |
| 143 | // jump to last topic of previous section |
| 144 | $prevLink = $prevListItem.find('a:last'); |
| 145 | } |
| 146 | } else { |
| 147 | // jump to previous topic in this section |
| 148 | $prevLink = $prevListItem.find('a:eq(0)'); |
| 149 | } |
| 150 | } else { |
| 151 | // jump to this section's index page (if it exists) |
| 152 | var $parentListItem = $selListItem.parents('li'); |
| 153 | $prevLink = $selListItem.parents('li').find('a'); |
| 154 | |
| 155 | // except if cross boundaries aren't allowed, and we're at the top of a section already (and there's another parent) |
| 156 | if (!crossBoundaries && $parentListItem.hasClass('nav-section') && $selListItem.hasClass('nav-section')) { |
| 157 | $prevLink = []; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | if ($prevLink.length) { |
| 162 | var prevHref = $prevLink.attr('href'); |
| 163 | if (prevHref == SITE_ROOT + 'index.html') { |
| 164 | // Don't show Previous when it leads to the homepage |
| 165 | } else { |
| 166 | $('.prev-page-link').attr('href', $prevLink.attr('href')).removeClass("hide"); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | // set up next links |
| 171 | var $nextLink = []; |
| 172 | var startCourse = false; |
| 173 | var startClass = false; |
| 174 | var training = $(".next-class-link").length; // decides whether to provide "next class" link |
| 175 | var isCrossingBoundary = false; |
| 176 | |
| 177 | if ($selListItem.hasClass('nav-section')) { |
| 178 | // we're on an index page, jump to the first topic |
| 179 | $nextLink = $selListItem.find('ul').find('a:eq(0)'); |
| 180 | |
| 181 | // if there aren't any children, go to the next section (required for About pages) |
| 182 | if($nextLink.length == 0) { |
| 183 | $nextLink = $selListItem.next('li').find('a'); |
| 184 | } |
| 185 | |
| 186 | // Handle some Training specialties |
| 187 | if ($selListItem.parent().is("#nav") && $(".start-course-link").length) { |
| 188 | // this means we're at the very top of the TOC hierarchy |
| 189 | startCourse = true; |
| 190 | } else if ($(".start-class-link").length) { |
| 191 | // this means this page has children but is not at the top (it's a class, not a course) |
| 192 | startClass = true; |
| 193 | } |
| 194 | } else { |
| 195 | // jump to the next topic in this section (if it exists) |
| 196 | $nextLink = $selListItem.next('li').find('a:eq(0)'); |
| 197 | if (!$nextLink.length) { |
| 198 | if (crossBoundaries || training) { |
| 199 | // no more topics in this section, jump to the first topic in the next section |
| 200 | $nextLink = $selListItem.parents('li:eq(0)').next('li.nav-section').find('a:eq(0)'); |
| 201 | isCrossingBoundary = true; |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | if ($nextLink.length) { |
| 206 | if (startCourse || startClass) { |
| 207 | if (startCourse) { |
| 208 | $('.start-course-link').attr('href', $nextLink.attr('href')).removeClass("hide"); |
| 209 | } else { |
| 210 | $('.start-class-link').attr('href', $nextLink.attr('href')).removeClass("hide"); |
| 211 | } |
| 212 | // if there's no training bar (below the start button), then we need to add a bottom border to button |
| 213 | if (!$("#tb").length) { |
| 214 | $('.start-course-link').css({'border-bottom':'1px solid #DADADA'}); |
| 215 | $('.start-class-link').css({'border-bottom':'1px solid #DADADA'}); |
| 216 | } |
| 217 | } else if (training && isCrossingBoundary) { |
| 218 | $('.content-footer.next-class').show(); |
| 219 | $('.next-page-link').attr('href','').removeClass("hide").addClass("disabled").click(function() { |
| 220 | return false; |
| 221 | }); |
| 222 | $('.next-class-link').attr('href',$nextLink.attr('href')).removeClass("hide").append($nextLink.html()); |
| 223 | $('.next-class-link').find('.new').empty(); |
| 224 | } else { |
| 225 | $('.next-page-link').attr('href', $nextLink.attr('href')).removeClass("hide"); |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | } |
| 230 | |
| 231 | |
| 232 | |
| 233 | // Set up expand/collapse behavior |
| 234 | $('#nav li.nav-section .nav-section-header').click(function() { |
| 235 | var section = $(this).closest('li.nav-section'); |
| 236 | if (section.hasClass('expanded')) { |
| 237 | /* hide me */ |
| 238 | // if (section.hasClass('selected') || section.find('li').hasClass('selected')) { |
| 239 | // /* but not if myself or my descendents are selected */ |
| 240 | // return; |
| 241 | // } |
| 242 | section.children('ul').slideUp(250, function() { |
| 243 | section.closest('li').removeClass('expanded'); |
| 244 | resizeNav(); |
| 245 | }); |
| 246 | } else { |
| 247 | /* show me */ |
| 248 | // first hide all other siblings |
| 249 | var $others = $('li.nav-section.expanded', $(this).closest('ul')); |
| 250 | $others.removeClass('expanded').children('ul').slideUp(250); |
| 251 | |
| 252 | // now expand me |
| 253 | section.closest('li').addClass('expanded'); |
| 254 | section.children('ul').slideDown(250, function() { |
| 255 | resizeNav(); |
| 256 | }); |
| 257 | } |
| 258 | }); |
| 259 | |
| 260 | $(".scroll-pane").scroll(function(event) { |
| 261 | event.preventDefault(); |
| 262 | return false; |
| 263 | }); |
| 264 | |
| 265 | /* Resize nav height when window height changes */ |
| 266 | $(window).resize(function() { |
| 267 | var stylesheet = $('link[rel="stylesheet"][title="fullscreen"]'); |
| 268 | setNavBarLeftPos(); // do this even if sidenav isn't fixed because it could become fixed |
| 269 | // make sidenav behave when resizing the window and side-scolling is a concern |
| 270 | if (navBarIsFixed) { |
| 271 | if ((stylesheet.attr("disabled") == "disabled") || stylesheet.length == 0) { |
| 272 | updateSideNavPosition(); |
| 273 | } else { |
| 274 | updateSidenavFullscreenWidth(); |
| 275 | } |
| 276 | } |
| 277 | resizeNav(); |
| 278 | }); |
| 279 | |
| 280 | |
| 281 | // Set up fixed navbar |
| 282 | var prevScrollLeft = 0; // used to compare current position to previous position of horiz scroll |
| 283 | $(window).scroll(function(event) { |
| 284 | if (event.target.nodeName == "DIV") { |
| 285 | // Dump scroll event if the target is a DIV, because that means the event is coming |
| 286 | // from a scrollable div and so there's no need to make adjustments to our layout |
| 287 | return; |
| 288 | } |
| 289 | var scrollTop = $(window).scrollTop(); |
| 290 | var headerHeight = $('#header').outerHeight(); |
| 291 | var subheaderHeight = $('#nav-x').outerHeight(); |
| 292 | var searchResultHeight = $('#searchResults').is(":visible") ? $('#searchResults').outerHeight() : 0; |
| 293 | var totalHeaderHeight = headerHeight + subheaderHeight + searchResultHeight; |
| 294 | var navBarShouldBeFixed = scrollTop > totalHeaderHeight; |
| 295 | |
| 296 | var scrollLeft = $(window).scrollLeft(); |
| 297 | // When the sidenav is fixed and user scrolls horizontally, reposition the sidenav to match |
| 298 | if (navBarIsFixed && (scrollLeft != prevScrollLeft)) { |
| 299 | updateSideNavPosition(); |
| 300 | prevScrollLeft = scrollLeft; |
| 301 | } |
| 302 | |
| 303 | // Don't continue if the header is sufficently far away (to avoid intensive resizing that slows scrolling) |
| 304 | if (navBarIsFixed && navBarShouldBeFixed) { |
| 305 | return; |
| 306 | } |
| 307 | |
| 308 | if (navBarIsFixed != navBarShouldBeFixed) { |
| 309 | if (navBarShouldBeFixed) { |
| 310 | // make it fixed |
| 311 | var width = $('#devdoc-nav').width(); |
| 312 | var margin = $('#devdoc-nav').parent().css('margin'); |
| 313 | $('#devdoc-nav') |
| 314 | .addClass('fixed') |
| 315 | .css({'width':width+'px','margin':margin}) |
| 316 | .prependTo('#body-content'); |
| 317 | // add neato "back to top" button |
| 318 | $('#devdoc-nav a.totop').css({'display':'block','width':$("#nav").innerWidth()+'px'}); |
| 319 | |
| 320 | // update the sidenaav position for side scrolling |
| 321 | updateSideNavPosition(); |
| 322 | } else { |
| 323 | // make it static again |
| 324 | $('#devdoc-nav') |
| 325 | .removeClass('fixed') |
| 326 | .css({'width':'auto','margin':''}) |
| 327 | .prependTo('#side-nav'); |
| 328 | $('#devdoc-nav a.totop').hide(); |
| 329 | } |
| 330 | navBarIsFixed = navBarShouldBeFixed; |
| 331 | } |
| 332 | |
| 333 | resizeNav(250); // pass true in order to delay the scrollbar re-initialization for performance reasons |
| 334 | }); |
| 335 | |
| 336 | |
| 337 | var navBarLeftPos; |
| 338 | if ($('#devdoc-nav').length) { |
| 339 | setNavBarLeftPos(); |
| 340 | } |
| 341 | |
| 342 | |
| 343 | // Stop expand/collapse behavior when clicking on nav section links (since we're navigating away |
| 344 | // from the page) |
| 345 | $('.nav-section-header').find('a:eq(0)').click(function(evt) { |
| 346 | window.location.href = $(this).attr('href'); |
| 347 | return false; |
| 348 | }); |
| 349 | |
| 350 | // Set up play-on-hover <video> tags. |
| 351 | $('video.play-on-hover').bind('click', function(){ |
| 352 | $(this).get(0).load(); // in case the video isn't seekable |
| 353 | $(this).get(0).play(); |
| 354 | }); |
| 355 | |
| 356 | // Set up tooltips |
| 357 | var TOOLTIP_MARGIN = 10; |
| 358 | $('acronym').each(function() { |
| 359 | var $target = $(this); |
| 360 | var $tooltip = $('<div>') |
| 361 | .addClass('tooltip-box') |
| 362 | .text($target.attr('title')) |
| 363 | .hide() |
| 364 | .appendTo('body'); |
| 365 | $target.removeAttr('title'); |
| 366 | |
| 367 | $target.hover(function() { |
| 368 | // in |
| 369 | var targetRect = $target.offset(); |
| 370 | targetRect.width = $target.width(); |
| 371 | targetRect.height = $target.height(); |
| 372 | |
| 373 | $tooltip.css({ |
| 374 | left: targetRect.left, |
| 375 | top: targetRect.top + targetRect.height + TOOLTIP_MARGIN |
| 376 | }); |
| 377 | $tooltip.addClass('below'); |
| 378 | $tooltip.show(); |
| 379 | }, function() { |
| 380 | // out |
| 381 | $tooltip.hide(); |
| 382 | }); |
| 383 | }); |
| 384 | |
| 385 | // Set up <h2> deeplinks |
| 386 | $('h2').click(function() { |
| 387 | var id = $(this).attr('id'); |
| 388 | if (id) { |
| 389 | document.location.hash = id; |
| 390 | } |
| 391 | }); |
| 392 | |
| 393 | //Loads the +1 button |
| 394 | var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; |
| 395 | po.src = 'https://apis.google.com/js/plusone.js'; |
| 396 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); |
| 397 | |
| 398 | |
| 399 | // Revise the sidenav widths to make room for the scrollbar |
| 400 | // which avoids the visible width from changing each time the bar appears |
| 401 | var $sidenav = $("#side-nav"); |
| 402 | var sidenav_width = parseInt($sidenav.innerWidth()); |
| 403 | |
| 404 | $("#devdoc-nav #nav").css("width", sidenav_width - 4 + "px"); // 4px is scrollbar width |
| 405 | |
| 406 | |
| 407 | $(".scroll-pane").removeAttr("tabindex"); // get rid of tabindex added by jscroller |
| 408 | |
| 409 | if ($(".scroll-pane").length > 1) { |
| 410 | // Check if there's a user preference for the panel heights |
| 411 | var cookieHeight = readCookie("reference_height"); |
| 412 | if (cookieHeight) { |
| 413 | restoreHeight(cookieHeight); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | resizeNav(); |
| 418 | |
| 419 | |
| 420 | }); |
| 421 | |
| 422 | |
| 423 | |
| 424 | function toggleFullscreen(enable) { |
| 425 | var delay = 20; |
| 426 | var enabled = false; |
| 427 | var stylesheet = $('link[rel="stylesheet"][title="fullscreen"]'); |
| 428 | if (enable) { |
| 429 | // Currently NOT USING fullscreen; enable fullscreen |
| 430 | stylesheet.removeAttr('disabled'); |
| 431 | $('#nav-swap .fullscreen').removeClass('disabled'); |
| 432 | $('#devdoc-nav').css({left:''}); |
| 433 | setTimeout(updateSidenavFullscreenWidth,delay); // need to wait a moment for css to switch |
| 434 | enabled = true; |
| 435 | } else { |
| 436 | // Currently USING fullscreen; disable fullscreen |
| 437 | stylesheet.attr('disabled', 'disabled'); |
| 438 | $('#nav-swap .fullscreen').addClass('disabled'); |
| 439 | setTimeout(updateSidenavFixedWidth,delay); // need to wait a moment for css to switch |
| 440 | enabled = false; |
| 441 | } |
| 442 | writeCookie("fullscreen", enabled, null, null); |
| 443 | setNavBarLeftPos(); |
| 444 | resizeNav(delay); |
| 445 | setTimeout(initSidenavHeightResize,delay); |
| 446 | } |
| 447 | |
| 448 | |
| 449 | function setNavBarLeftPos() { |
| 450 | navBarLeftPos = $('#body-content').offset().left; |
| 451 | } |
| 452 | |
| 453 | |
| 454 | function updateSideNavPosition() { |
| 455 | var newLeft = $(window).scrollLeft() - navBarLeftPos; |
| 456 | $('#devdoc-nav').css({left: -newLeft}); |
| 457 | $('#devdoc-nav .totop').css({left: -(newLeft - parseInt($('#side-nav').css('margin-left')))}); |
| 458 | } |
| 459 | |