blob: 2c9afe1aa8b5a6e4d0218105f67c2676f31978b9 [file] [log] [blame]
Scott Maine4d8f1b2012-06-21 18:03:05 -07001var 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