| // Scrollbar Width function |
| function getScrollBarWidth() { |
| var inner = document.createElement('p'); |
| inner.style.width = "100%"; |
| inner.style.height = "200px"; |
| |
| var outer = document.createElement('div'); |
| outer.style.position = "absolute"; |
| outer.style.top = "0px"; |
| outer.style.left = "0px"; |
| outer.style.visibility = "hidden"; |
| outer.style.width = "200px"; |
| outer.style.height = "150px"; |
| outer.style.overflow = "hidden"; |
| outer.appendChild(inner); |
| |
| document.body.appendChild(outer); |
| var w1 = inner.offsetWidth; |
| outer.style.overflow = 'scroll'; |
| var w2 = inner.offsetWidth; |
| if (w1 == w2) w2 = outer.clientWidth; |
| |
| document.body.removeChild(outer); |
| |
| return (w1 - w2); |
| }; |
| |
| function setMenuHeight() { |
| $('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40); |
| $('#sidebar .highlightable').perfectScrollbar('update'); |
| } |
| |
| function fallbackMessage(action) { |
| var actionMsg = ''; |
| var actionKey = (action === 'cut' ? 'X' : 'C'); |
| |
| if (/iPhone|iPad/i.test(navigator.userAgent)) { |
| actionMsg = 'No support :('; |
| } |
| else if (/Mac/i.test(navigator.userAgent)) { |
| actionMsg = 'Press ⌘-' + actionKey + ' to ' + action; |
| } |
| else { |
| actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action; |
| } |
| |
| return actionMsg; |
| } |
| |
| // for the window resize |
| $(window).resize(function() { |
| setMenuHeight(); |
| }); |
| |
| // debouncing function from John Hann |
| // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ |
| (function($, sr) { |
| |
| var debounce = function(func, threshold, execAsap) { |
| var timeout; |
| |
| return function debounced() { |
| var obj = this, args = arguments; |
| |
| function delayed() { |
| if (!execAsap) |
| func.apply(obj, args); |
| timeout = null; |
| }; |
| |
| if (timeout) |
| clearTimeout(timeout); |
| else if (execAsap) |
| func.apply(obj, args); |
| |
| timeout = setTimeout(delayed, threshold || 100); |
| }; |
| } |
| // smartresize |
| jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); }; |
| |
| })(jQuery, 'smartresize'); |
| |
| |
| jQuery(document).ready(function() { |
| jQuery('#sidebar .category-icon').on('click', function() { |
| $( this ).toggleClass("fa-angle-down fa-angle-right") ; |
| $( this ).parent().parent().children('ul').toggle() ; |
| return false; |
| }); |
| |
| var sidebarStatus = searchStatus = 'open'; |
| $('#sidebar .highlightable').perfectScrollbar(); |
| setMenuHeight(); |
| |
| jQuery('#overlay').on('click', function() { |
| jQuery(document.body).toggleClass('sidebar-hidden'); |
| sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open'); |
| |
| return false; |
| }); |
| |
| jQuery('[data-sidebar-toggle]').on('click', function() { |
| jQuery(document.body).toggleClass('sidebar-hidden'); |
| sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open'); |
| |
| return false; |
| }); |
| jQuery('[data-clear-history-toggle]').on('click', function() { |
| sessionStorage.clear(); |
| location.reload(); |
| return false; |
| }); |
| jQuery('[data-search-toggle]').on('click', function() { |
| if (sidebarStatus == 'closed') { |
| jQuery('[data-sidebar-toggle]').trigger('click'); |
| jQuery(document.body).removeClass('searchbox-hidden'); |
| searchStatus = 'open'; |
| |
| return false; |
| } |
| |
| jQuery(document.body).toggleClass('searchbox-hidden'); |
| searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open'); |
| |
| return false; |
| }); |
| |
| var ajax; |
| jQuery('[data-search-input]').on('input', function() { |
| var input = jQuery(this), |
| value = input.val(), |
| items = jQuery('[data-nav-id]'); |
| items.removeClass('search-match'); |
| if (!value.length) { |
| $('ul.topics').removeClass('searched'); |
| items.css('display', 'block'); |
| sessionStorage.removeItem('search-value'); |
| $(".highlightable").unhighlight({ element: 'mark' }) |
| return; |
| } |
| |
| sessionStorage.setItem('search-value', value); |
| $(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' }); |
| |
| if (ajax && ajax.abort) ajax.abort(); |
| |
| jQuery('[data-search-clear]').on('click', function() { |
| jQuery('[data-search-input]').val('').trigger('input'); |
| sessionStorage.removeItem('search-input'); |
| $(".highlightable").unhighlight({ element: 'mark' }) |
| }); |
| }); |
| |
| $.expr[":"].contains = $.expr.createPseudo(function(arg) { |
| return function( elem ) { |
| return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; |
| }; |
| }); |
| |
| if (sessionStorage.getItem('search-value')) { |
| var searchValue = sessionStorage.getItem('search-value') |
| $(document.body).removeClass('searchbox-hidden'); |
| $('[data-search-input]').val(searchValue); |
| $('[data-search-input]').trigger('input'); |
| var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0); |
| if (searchedElem) { |
| searchedElem.scrollIntoView(true); |
| var scrolledY = window.scrollY; |
| if(scrolledY){ |
| window.scroll(0, scrolledY - 125); |
| } |
| } |
| } |
| |
| // clipboard |
| var clipInit = false; |
| $('code').each(function() { |
| var code = $(this), |
| text = code.text(); |
| |
| if (text.length > 5) { |
| if (!clipInit) { |
| var text, clip = new Clipboard('.copy-to-clipboard', { |
| text: function(trigger) { |
| text = $(trigger).prev('code').text(); |
| return text.replace(/^\$\s/gm, ''); |
| } |
| }); |
| |
| var inPre; |
| clip.on('success', function(e) { |
| e.clearSelection(); |
| inPre = $(e.trigger).parent().prop('tagName') == 'PRE'; |
| $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); |
| }); |
| |
| clip.on('error', function(e) { |
| inPre = $(e.trigger).parent().prop('tagName') == 'PRE'; |
| $(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); |
| $(document).one('copy', function(){ |
| $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); |
| }); |
| }); |
| |
| clipInit = true; |
| } |
| |
| code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />'); |
| code.next('.copy-to-clipboard').on('mouseleave', function() { |
| $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w'); |
| }); |
| } |
| }); |
| |
| // allow keyboard control for prev/next links |
| jQuery(function() { |
| jQuery('.nav-prev').click(function(){ |
| location.href = jQuery(this).attr('href'); |
| }); |
| jQuery('.nav-next').click(function() { |
| location.href = jQuery(this).attr('href'); |
| }); |
| }); |
| |
| jQuery(document).keydown(function(e) { |
| // prev links - left arrow key |
| if(e.which == '37') { |
| jQuery('.nav.nav-prev').click(); |
| } |
| |
| // next links - right arrow key |
| if(e.which == '39') { |
| jQuery('.nav.nav-next').click(); |
| } |
| }); |
| |
| $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight'); |
| $('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight'); |
| |
| var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0) |
| if (!touchsupport){ // browser doesn't support touch |
| $('#toc-menu').hover(function() { |
| $('.progress').stop(true, false, true).fadeToggle(100); |
| }); |
| |
| $('.progress').hover(function() { |
| $('.progress').stop(true, false, true).fadeToggle(100); |
| }); |
| } |
| if (touchsupport){ // browser does support touch |
| $('#toc-menu').click(function() { |
| $('.progress').stop(true, false, true).fadeToggle(100); |
| }); |
| $('.progress').click(function() { |
| $('.progress').stop(true, false, true).fadeToggle(100); |
| }); |
| } |
| |
| /** |
| * Fix anchor scrolling that hides behind top nav bar |
| * Courtesy of https://stackoverflow.com/a/13067009/28106 |
| * |
| * We could use pure css for this if only heading anchors were |
| * involved, but this works for any anchor, including footnotes |
| **/ |
| (function (document, history, location) { |
| var HISTORY_SUPPORT = !!(history && history.pushState); |
| |
| var anchorScrolls = { |
| ANCHOR_REGEX: /^#[^ ]+$/, |
| OFFSET_HEIGHT_PX: 50, |
| |
| /** |
| * Establish events, and fix initial scroll position if a hash is provided. |
| */ |
| init: function () { |
| this.scrollToCurrent(); |
| $(window).on('hashchange', $.proxy(this, 'scrollToCurrent')); |
| $('body').on('click', 'a', $.proxy(this, 'delegateAnchors')); |
| }, |
| |
| /** |
| * Return the offset amount to deduct from the normal scroll position. |
| * Modify as appropriate to allow for dynamic calculations |
| */ |
| getFixedOffset: function () { |
| return this.OFFSET_HEIGHT_PX; |
| }, |
| |
| /** |
| * If the provided href is an anchor which resolves to an element on the |
| * page, scroll to it. |
| * @param {String} href |
| * @return {Boolean} - Was the href an anchor. |
| */ |
| scrollIfAnchor: function (href, pushToHistory) { |
| var match, anchorOffset; |
| |
| if (!this.ANCHOR_REGEX.test(href)) { |
| return false; |
| } |
| |
| match = document.getElementById(href.slice(1)); |
| |
| if (match) { |
| anchorOffset = $(match).offset().top - this.getFixedOffset(); |
| $('html, body').animate({ scrollTop: anchorOffset }); |
| |
| // Add the state to history as-per normal anchor links |
| if (HISTORY_SUPPORT && pushToHistory) { |
| history.pushState({}, document.title, location.pathname + href); |
| } |
| } |
| |
| return !!match; |
| }, |
| |
| /** |
| * Attempt to scroll to the current location's hash. |
| */ |
| scrollToCurrent: function (e) { |
| if (this.scrollIfAnchor(window.location.hash) && e) { |
| e.preventDefault(); |
| } |
| }, |
| |
| /** |
| * If the click event's target was an anchor, fix the scroll position. |
| */ |
| delegateAnchors: function (e) { |
| var elem = e.target; |
| |
| if (this.scrollIfAnchor(elem.getAttribute('href'), true)) { |
| e.preventDefault(); |
| } |
| } |
| }; |
| |
| $(document).ready($.proxy(anchorScrolls, 'init')); |
| })(window.document, window.history, window.location); |
| |
| }); |
| |
| jQuery(window).on('load', function() { |
| |
| function adjustForScrollbar() { |
| if ((parseInt(jQuery('#body-inner').height()) + 83) >= jQuery('#body').height()) { |
| jQuery('.nav.nav-next').css({ 'margin-right': getScrollBarWidth() }); |
| } else { |
| jQuery('.nav.nav-next').css({ 'margin-right': 0 }); |
| } |
| } |
| |
| // adjust sidebar for scrollbar |
| adjustForScrollbar(); |
| |
| jQuery(window).smartresize(function() { |
| adjustForScrollbar(); |
| }); |
| |
| // store this page in session |
| sessionStorage.setItem(jQuery('body').data('url'), 1); |
| |
| // loop through the sessionStorage and see if something should be marked as visited |
| for (var url in sessionStorage) { |
| if (sessionStorage.getItem(url) == 1) jQuery('[data-nav-id="' + url + '"]').addClass('visited'); |
| } |
| |
| |
| $(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' }); |
| }); |
| |
| $(function() { |
| $('a[rel="lightbox"]').featherlight({ |
| root: 'section#body' |
| }); |
| }); |
| |
| jQuery.extend({ |
| highlight: function(node, re, nodeName, className) { |
| if (node.nodeType === 3) { |
| var match = node.data.match(re); |
| if (match) { |
| var highlight = document.createElement(nodeName || 'span'); |
| highlight.className = className || 'highlight'; |
| var wordNode = node.splitText(match.index); |
| wordNode.splitText(match[0].length); |
| var wordClone = wordNode.cloneNode(true); |
| highlight.appendChild(wordClone); |
| wordNode.parentNode.replaceChild(highlight, wordNode); |
| return 1; //skip added node in parent |
| } |
| } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children |
| !/(script|style)/i.test(node.tagName) && // ignore script and style nodes |
| !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted |
| for (var i = 0; i < node.childNodes.length; i++) { |
| i += jQuery.highlight(node.childNodes[i], re, nodeName, className); |
| } |
| } |
| return 0; |
| } |
| }); |
| |
| jQuery.fn.unhighlight = function(options) { |
| var settings = { |
| className: 'highlight', |
| element: 'span' |
| }; |
| jQuery.extend(settings, options); |
| |
| return this.find(settings.element + "." + settings.className).each(function() { |
| var parent = this.parentNode; |
| parent.replaceChild(this.firstChild, this); |
| parent.normalize(); |
| }).end(); |
| }; |
| |
| jQuery.fn.highlight = function(words, options) { |
| var settings = { |
| className: 'highlight', |
| element: 'span', |
| caseSensitive: false, |
| wordsOnly: false |
| }; |
| jQuery.extend(settings, options); |
| |
| if (!words) { return; } |
| |
| if (words.constructor === String) { |
| words = [words]; |
| } |
| words = jQuery.grep(words, function(word, i) { |
| return word != ''; |
| }); |
| words = jQuery.map(words, function(word, i) { |
| return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); |
| }); |
| if (words.length == 0) { return this; } |
| ; |
| |
| var flag = settings.caseSensitive ? "" : "i"; |
| var pattern = "(" + words.join("|") + ")"; |
| if (settings.wordsOnly) { |
| pattern = "\\b" + pattern + "\\b"; |
| } |
| var re = new RegExp(pattern, flag); |
| |
| return this.each(function() { |
| jQuery.highlight(this, re, settings.element, settings.className); |
| }); |
| }; |