1/*! ============ Functions ============ */ 2/*jshint esversion: 6 */ 3 4'use strict'; 5 6/** 7 * window.requestIdleCallback() 8 * version 0.0.0 9 * Browser Compatibility: 10 * https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback#browser_compatibility 11 */ 12if (!window.requestIdleCallback) { 13 window.requestIdleCallback = function (callback, opts) { 14 var options = opts || {}; 15 var relaxation = 1; 16 var timeout = options.timeout || relaxation; 17 var start = performance.now(); 18 return setTimeout(function () { 19 callback({ 20 get didTimeout() { 21 return options.timeout ? false : performance.now() - start - relaxation > timeout; 22 }, 23 timeRemaining: function () { 24 return Math.max(0, relaxation + (performance.now() - start)); 25 }, 26 }); 27 }, relaxation); 28 }; 29} 30 31/** 32 * window.cancelIdleCallback() 33 * version 0.0.0 34 * Browser Compatibility: 35 * https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelIdleCallback#browser_compatibility 36 */ 37if (!window.cancelIdleCallback) { 38 window.cancelIdleCallback = function (id) { 39 clearTimeout(id); 40 }; 41} 42 43/** 44 * window.requestAnimationFrame() 45 * version 0.0.0 46 * Browser Compatibility: 47 * https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#browser_compatibility 48 */ 49if (!window.requestAnimationFrame) { 50 window.requestAnimationFrame = function (callback) { 51 return window.setTimeout(function () { 52 callback(Date.now()); 53 }, 1000 / 60); 54 }; 55} 56 57/** 58 * window.cancelAnimationFrame() 59 * version 0.0.0 60 * Browser Compatibility: 61 * https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame#browser_compatibility 62 */ 63if (!window.cancelAnimationFrame) { 64 window.cancelAnimationFrame = function (id) { 65 clearTimeout(id); 66 }; 67} 68 69// debounce callback calls 70function debounce(cb, delay = 250) { 71 let timeout; 72 73 return (...args) => { 74 clearTimeout(timeout); 75 timeout = setTimeout(() => { 76 cb(...args); 77 }, delay); 78 }; 79} 80 81window.addEventListener('pageshow', (event) => { 82 // You can measure bfcache hit rate by tracking all bfcache restores and 83 // other back/forward navigations via a seperate event. 84 const navigationType = performance.getEntriesByType('navigation')[0].type; 85 if (event.persisted || navigationType == 'back_forward') { 86 // Log a Conversion for Goal 1 87 window._paq = window._paq || []; 88 window._paq.push(['trackGoal', 1, 0, { 'isBFCache': event.persisted }]); 89 } 90}); 91 92// event delegation 93function delegate_event(event_type, ancestor_element, target_element_selector, listener_function) { 94 ancestor_element.addEventListener(event_type, function (event) { 95 if (event.target && event.target.matches && event.target.matches(target_element_selector)) { 96 listener_function(event); 97 } 98 }); 99} 100 101function remove_delegated_event(event_type, ancestor_element, listener_function) { 102 ancestor_element.removeEventListener(event_type, listener_function); 103} 104 105// viewport visibility 106function viewportObserver(elm, handleIntersect) { 107 let observer; 108 let options = {}; 109 observer = new IntersectionObserver(handleIntersect, options); 110 observer.observe(elm); 111 return observer; 112} 113 114// check for storage functionality 115function storageAvailable(type) { 116 let storage; 117 try { 118 storage = window[type]; 119 const x = '__storage_test__'; 120 storage.setItem(x, x); 121 storage.removeItem(x); 122 return true; 123 } catch (e) { 124 return ( 125 e instanceof DOMException && 126 // everything except Firefox 127 (e.code === 22 || 128 // Firefox 129 e.code === 1014 || 130 // test name field too, because code might not be present 131 // everything except Firefox 132 e.name === 'QuotaExceededError' || 133 // Firefox 134 e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && 135 // acknowledge QuotaExceededError only if there's something already stored 136 storage && 137 storage.length !== 0 138 ); 139 } 140} 141 142// handle local storage 143function setLocalStorage(key, val) { 144 document.body.classList.toggle(key, val); 145 if (storageAvailable('localStorage')) { 146 localStorage.setItem(key, JSON.stringify(val)); 147 } 148}
149function getLocalStorage(key) { 150 if (storageAvailable('localStorage')) { 151 return JSON.parse(localStorage.getItem(key)); 152 } 153} 154 155// change button state to loading 156function btnLoading(btn) { 157 let orig = btn.innerHTML; 158 window.requestAnimationFrame(function () { 159 btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Loading...'; 160 btn.dataset.orig = orig; 161 btn.setAttribute('disabled', ''); 162 }); 163} 164 165// revert button state to original 166function btnLoaded(btn) { 167 let orig = btn.dataset.orig; 168 window.requestAnimationFrame(function () { 169 btn.innerHTML = orig; 170 btn.removeAttribute('disabled'); 171 }); 172} 173 174// equal height elements 175function sameHeights(selector) { 176 const nodes = [...document.querySelectorAll(selector)]; 177 let tallest = Math.max.apply( 178 Math, 179 nodes.map(function (elem, index) { 180 elem.style.minHeight = ''; // clean first 181 return elem.offsetHeight; 182 }) 183 ); 184 nodes.forEach(function (node, index, arr) { 185 node.style.height = tallest + 'px'; 186 }); 187}
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.