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