|  | // 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 ClipboardJS('.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('input, textarea').keydown(function (e) { | 
|  | //  left and right arrow keys | 
|  | if (e.which == '37' || e.which == '39') { | 
|  | e.stopPropagation(); | 
|  | } | 
|  | }); | 
|  |  | 
|  | 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); | 
|  | }); | 
|  | }; |