github.com/mweagle/Sparta@v1.15.0/docs_source/themes/hugo-theme-learn/static/js/learn.js (about) 1 // Scrollbar Width function 2 function getScrollBarWidth() { 3 var inner = document.createElement('p'); 4 inner.style.width = "100%"; 5 inner.style.height = "200px"; 6 7 var outer = document.createElement('div'); 8 outer.style.position = "absolute"; 9 outer.style.top = "0px"; 10 outer.style.left = "0px"; 11 outer.style.visibility = "hidden"; 12 outer.style.width = "200px"; 13 outer.style.height = "150px"; 14 outer.style.overflow = "hidden"; 15 outer.appendChild(inner); 16 17 document.body.appendChild(outer); 18 var w1 = inner.offsetWidth; 19 outer.style.overflow = 'scroll'; 20 var w2 = inner.offsetWidth; 21 if (w1 == w2) w2 = outer.clientWidth; 22 23 document.body.removeChild(outer); 24 25 return (w1 - w2); 26 }; 27 28 function setMenuHeight() { 29 $('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40); 30 $('#sidebar .highlightable').perfectScrollbar('update'); 31 } 32 33 function fallbackMessage(action) { 34 var actionMsg = ''; 35 var actionKey = (action === 'cut' ? 'X' : 'C'); 36 37 if (/iPhone|iPad/i.test(navigator.userAgent)) { 38 actionMsg = 'No support :('; 39 } 40 else if (/Mac/i.test(navigator.userAgent)) { 41 actionMsg = 'Press ⌘-' + actionKey + ' to ' + action; 42 } 43 else { 44 actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action; 45 } 46 47 return actionMsg; 48 } 49 50 // for the window resize 51 $(window).resize(function() { 52 setMenuHeight(); 53 }); 54 55 // debouncing function from John Hann 56 // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 57 (function($, sr) { 58 59 var debounce = function(func, threshold, execAsap) { 60 var timeout; 61 62 return function debounced() { 63 var obj = this, args = arguments; 64 65 function delayed() { 66 if (!execAsap) 67 func.apply(obj, args); 68 timeout = null; 69 }; 70 71 if (timeout) 72 clearTimeout(timeout); 73 else if (execAsap) 74 func.apply(obj, args); 75 76 timeout = setTimeout(delayed, threshold || 100); 77 }; 78 } 79 // smartresize 80 jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); }; 81 82 })(jQuery, 'smartresize'); 83 84 85 jQuery(document).ready(function() { 86 jQuery('#sidebar .category-icon').on('click', function() { 87 $( this ).toggleClass("fa-angle-down fa-angle-right") ; 88 $( this ).parent().parent().children('ul').toggle() ; 89 return false; 90 }); 91 92 var sidebarStatus = searchStatus = 'open'; 93 $('#sidebar .highlightable').perfectScrollbar(); 94 setMenuHeight(); 95 96 jQuery('#overlay').on('click', function() { 97 jQuery(document.body).toggleClass('sidebar-hidden'); 98 sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open'); 99 100 return false; 101 }); 102 103 jQuery('[data-sidebar-toggle]').on('click', function() { 104 jQuery(document.body).toggleClass('sidebar-hidden'); 105 sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open'); 106 107 return false; 108 }); 109 jQuery('[data-clear-history-toggle]').on('click', function() { 110 sessionStorage.clear(); 111 location.reload(); 112 return false; 113 }); 114 jQuery('[data-search-toggle]').on('click', function() { 115 if (sidebarStatus == 'closed') { 116 jQuery('[data-sidebar-toggle]').trigger('click'); 117 jQuery(document.body).removeClass('searchbox-hidden'); 118 searchStatus = 'open'; 119 120 return false; 121 } 122 123 jQuery(document.body).toggleClass('searchbox-hidden'); 124 searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open'); 125 126 return false; 127 }); 128 129 var ajax; 130 jQuery('[data-search-input]').on('input', function() { 131 var input = jQuery(this), 132 value = input.val(), 133 items = jQuery('[data-nav-id]'); 134 items.removeClass('search-match'); 135 if (!value.length) { 136 $('ul.topics').removeClass('searched'); 137 items.css('display', 'block'); 138 sessionStorage.removeItem('search-value'); 139 $(".highlightable").unhighlight({ element: 'mark' }) 140 return; 141 } 142 143 sessionStorage.setItem('search-value', value); 144 $(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' }); 145 146 if (ajax && ajax.abort) ajax.abort(); 147 148 jQuery('[data-search-clear]').on('click', function() { 149 jQuery('[data-search-input]').val('').trigger('input'); 150 sessionStorage.removeItem('search-input'); 151 $(".highlightable").unhighlight({ element: 'mark' }) 152 }); 153 }); 154 155 $.expr[":"].contains = $.expr.createPseudo(function(arg) { 156 return function( elem ) { 157 return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; 158 }; 159 }); 160 161 if (sessionStorage.getItem('search-value')) { 162 var searchValue = sessionStorage.getItem('search-value') 163 $(document.body).removeClass('searchbox-hidden'); 164 $('[data-search-input]').val(searchValue); 165 $('[data-search-input]').trigger('input'); 166 var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0); 167 if (searchedElem) { 168 searchedElem.scrollIntoView(true); 169 var scrolledY = window.scrollY; 170 if(scrolledY){ 171 window.scroll(0, scrolledY - 125); 172 } 173 } 174 } 175 176 // clipboard 177 var clipInit = false; 178 $('code').each(function() { 179 var code = $(this), 180 text = code.text(); 181 182 if (text.length > 5) { 183 if (!clipInit) { 184 var text, clip = new ClipboardJS('.copy-to-clipboard', { 185 text: function(trigger) { 186 text = $(trigger).prev('code').text(); 187 return text.replace(/^\$\s/gm, ''); 188 } 189 }); 190 191 var inPre; 192 clip.on('success', function(e) { 193 e.clearSelection(); 194 inPre = $(e.trigger).parent().prop('tagName') == 'PRE'; 195 $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); 196 }); 197 198 clip.on('error', function(e) { 199 inPre = $(e.trigger).parent().prop('tagName') == 'PRE'; 200 $(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); 201 $(document).one('copy', function(){ 202 $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's')); 203 }); 204 }); 205 206 clipInit = true; 207 } 208 209 code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />'); 210 code.next('.copy-to-clipboard').on('mouseleave', function() { 211 $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w'); 212 }); 213 } 214 }); 215 216 // allow keyboard control for prev/next links 217 jQuery(function() { 218 jQuery('.nav-prev').click(function(){ 219 location.href = jQuery(this).attr('href'); 220 }); 221 jQuery('.nav-next').click(function() { 222 location.href = jQuery(this).attr('href'); 223 }); 224 }); 225 226 jQuery('input, textarea').keydown(function (e) { 227 // left and right arrow keys 228 if (e.which == '37' || e.which == '39') { 229 e.stopPropagation(); 230 } 231 }); 232 233 jQuery(document).keydown(function(e) { 234 // prev links - left arrow key 235 if(e.which == '37') { 236 jQuery('.nav.nav-prev').click(); 237 } 238 239 // next links - right arrow key 240 if(e.which == '39') { 241 jQuery('.nav.nav-next').click(); 242 } 243 }); 244 245 $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight'); 246 $('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight'); 247 248 var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0) 249 if (!touchsupport){ // browser doesn't support touch 250 $('#toc-menu').hover(function() { 251 $('.progress').stop(true, false, true).fadeToggle(100); 252 }); 253 254 $('.progress').hover(function() { 255 $('.progress').stop(true, false, true).fadeToggle(100); 256 }); 257 } 258 if (touchsupport){ // browser does support touch 259 $('#toc-menu').click(function() { 260 $('.progress').stop(true, false, true).fadeToggle(100); 261 }); 262 $('.progress').click(function() { 263 $('.progress').stop(true, false, true).fadeToggle(100); 264 }); 265 } 266 267 /** 268 * Fix anchor scrolling that hides behind top nav bar 269 * Courtesy of https://stackoverflow.com/a/13067009/28106 270 * 271 * We could use pure css for this if only heading anchors were 272 * involved, but this works for any anchor, including footnotes 273 **/ 274 (function (document, history, location) { 275 var HISTORY_SUPPORT = !!(history && history.pushState); 276 277 var anchorScrolls = { 278 ANCHOR_REGEX: /^#[^ ]+$/, 279 OFFSET_HEIGHT_PX: 50, 280 281 /** 282 * Establish events, and fix initial scroll position if a hash is provided. 283 */ 284 init: function () { 285 this.scrollToCurrent(); 286 $(window).on('hashchange', $.proxy(this, 'scrollToCurrent')); 287 $('body').on('click', 'a', $.proxy(this, 'delegateAnchors')); 288 }, 289 290 /** 291 * Return the offset amount to deduct from the normal scroll position. 292 * Modify as appropriate to allow for dynamic calculations 293 */ 294 getFixedOffset: function () { 295 return this.OFFSET_HEIGHT_PX; 296 }, 297 298 /** 299 * If the provided href is an anchor which resolves to an element on the 300 * page, scroll to it. 301 * @param {String} href 302 * @return {Boolean} - Was the href an anchor. 303 */ 304 scrollIfAnchor: function (href, pushToHistory) { 305 var match, anchorOffset; 306 307 if (!this.ANCHOR_REGEX.test(href)) { 308 return false; 309 } 310 311 match = document.getElementById(href.slice(1)); 312 313 if (match) { 314 anchorOffset = $(match).offset().top - this.getFixedOffset(); 315 $('html, body').animate({ scrollTop: anchorOffset }); 316 317 // Add the state to history as-per normal anchor links 318 if (HISTORY_SUPPORT && pushToHistory) { 319 history.pushState({}, document.title, location.pathname + href); 320 } 321 } 322 323 return !!match; 324 }, 325 326 /** 327 * Attempt to scroll to the current location's hash. 328 */ 329 scrollToCurrent: function (e) { 330 if (this.scrollIfAnchor(window.location.hash) && e) { 331 e.preventDefault(); 332 } 333 }, 334 335 /** 336 * If the click event's target was an anchor, fix the scroll position. 337 */ 338 delegateAnchors: function (e) { 339 var elem = e.target; 340 341 if (this.scrollIfAnchor(elem.getAttribute('href'), true)) { 342 e.preventDefault(); 343 } 344 } 345 }; 346 347 $(document).ready($.proxy(anchorScrolls, 'init')); 348 })(window.document, window.history, window.location); 349 350 }); 351 352 jQuery(window).on('load', function() { 353 354 function adjustForScrollbar() { 355 if ((parseInt(jQuery('#body-inner').height()) + 83) >= jQuery('#body').height()) { 356 jQuery('.nav.nav-next').css({ 'margin-right': getScrollBarWidth() }); 357 } else { 358 jQuery('.nav.nav-next').css({ 'margin-right': 0 }); 359 } 360 } 361 362 // adjust sidebar for scrollbar 363 adjustForScrollbar(); 364 365 jQuery(window).smartresize(function() { 366 adjustForScrollbar(); 367 }); 368 369 // store this page in session 370 sessionStorage.setItem(jQuery('body').data('url'), 1); 371 372 // loop through the sessionStorage and see if something should be marked as visited 373 for (var url in sessionStorage) { 374 if (sessionStorage.getItem(url) == 1) jQuery('[data-nav-id="' + url + '"]').addClass('visited'); 375 } 376 377 378 $(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' }); 379 }); 380 381 $(function() { 382 $('a[rel="lightbox"]').featherlight({ 383 root: 'section#body' 384 }); 385 }); 386 387 jQuery.extend({ 388 highlight: function(node, re, nodeName, className) { 389 if (node.nodeType === 3) { 390 var match = node.data.match(re); 391 if (match) { 392 var highlight = document.createElement(nodeName || 'span'); 393 highlight.className = className || 'highlight'; 394 var wordNode = node.splitText(match.index); 395 wordNode.splitText(match[0].length); 396 var wordClone = wordNode.cloneNode(true); 397 highlight.appendChild(wordClone); 398 wordNode.parentNode.replaceChild(highlight, wordNode); 399 return 1; //skip added node in parent 400 } 401 } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children 402 !/(script|style)/i.test(node.tagName) && // ignore script and style nodes 403 !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted 404 for (var i = 0; i < node.childNodes.length; i++) { 405 i += jQuery.highlight(node.childNodes[i], re, nodeName, className); 406 } 407 } 408 return 0; 409 } 410 }); 411 412 jQuery.fn.unhighlight = function(options) { 413 var settings = { 414 className: 'highlight', 415 element: 'span' 416 }; 417 jQuery.extend(settings, options); 418 419 return this.find(settings.element + "." + settings.className).each(function() { 420 var parent = this.parentNode; 421 parent.replaceChild(this.firstChild, this); 422 parent.normalize(); 423 }).end(); 424 }; 425 426 jQuery.fn.highlight = function(words, options) { 427 var settings = { 428 className: 'highlight', 429 element: 'span', 430 caseSensitive: false, 431 wordsOnly: false 432 }; 433 jQuery.extend(settings, options); 434 435 if (!words) { return; } 436 437 if (words.constructor === String) { 438 words = [words]; 439 } 440 words = jQuery.grep(words, function(word, i) { 441 return word != ''; 442 }); 443 words = jQuery.map(words, function(word, i) { 444 return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 445 }); 446 if (words.length == 0) { return this; } 447 ; 448 449 var flag = settings.caseSensitive ? "" : "i"; 450 var pattern = "(" + words.join("|") + ")"; 451 if (settings.wordsOnly) { 452 pattern = "\\b" + pattern + "\\b"; 453 } 454 var re = new RegExp(pattern, flag); 455 456 return this.each(function() { 457 jQuery.highlight(this, re, settings.element, settings.className); 458 }); 459 };