1function getCurrentDateInMs() { 2 return (new Date()).valueOf() 3} 4 5function getIsDocumentVisible() { 6 return document.visibilityState === 'visible' || document.hidden === false 7} 8 9/** 10 * Calls the callback at most every `interval` milliseconds if the window is visible. 11 * 12 * @param {number} interval in ms 13 * @param {() => void} callback 14 */ 15function intervalCallbackOnWindowVisible(interval, callback) { 16 var nextRefresh = getCurrentDateInMs() + interval 17 /** @type {NodeJS.Timeout} */ 18 var refreshTimeout = null 19 20 function refresh() { 21 nextRefresh = getCurrentDateInMs() + interval 22 // Queue next refresh 23 refreshTimeout = setTimeout(refresh, interval); 24 // Call callback asynchronously 25 setTimeout(callback, 0) 26 } 27 28 document.addEventListener('visibilitychange', function () { 29 clearTimeout(refreshTimeout) 30 31 if (getIsDocumentVisible()) { 32 var currentDateMs = getCurrentDateInMs() 33 // Refresh if the document is stale, otherwise set a timeout to refresh later 34 if (nextRefresh <= currentDateMs) { 35 refresh() 36 } else { 37 refreshTimeout = setTimeout(refresh, nextRefresh - currentDateMs) 38 } 39 } 40 }) 41 42 if (getIsDocumentVisible()) { 43 refreshTimeout = setTimeout(refresh, interval); 44 } 45} 46 47var refreshIds; 48var currentVersion = null; 49var pageRefreshInterval = 3600 * 1000; 50function RefreshInit(refresh, ids, initVersion) { 51 refreshIds = ids; 52 intervalCallbackOnWindowVisible(refresh * 1000, Refresh) 53 54 if (initVersion !== undefined) currentVersion = initVersion.toString() 55} 56 57function RefreshOnVersionChange(initVersion, interval) { 58 currentVersion = initVersion.toString() 59 60 intervalCallbackOnWindowVisible(interval || pageRefreshInterval, () => { 61 simpleFetch(window.location.href, { method: 'options' }) 62 .then(function (res) { 63 if (!checkVersionMatches(res.headers)) window.location.reload() 64 }) 65 .catch(function () {}) 66 }) 67} 68 69function RefreshImageRevInit(refresh, container) { 70 var containerEl = document.getElementById(container) 71 72 intervalCallbackOnWindowVisible(refresh * 1000, function OnRefreshImageRevs() { 73 if (!containerEl) return 74 75 var imgs = containerEl.getElementsByTagName('img'); 76 var rev = new Date().getTime(); 77 for (var i = 0; i < imgs.length; i++) { 78 var img = imgs[i]; 79 var isChartImage = img.srcset.indexOf('/chart?') >= 0 || img.srcset.indexOf('fut_image') >= 0 || img.srcset.indexOf('fx_image') >= 0 80 81 if (!isChartImage) continue; 82 83 img.srcset = img.srcset.split(', ').map(function (set) { 84 var setParts = set.trim().split(' ').reverse(); 85 var url = setParts.pop(); 86 return [updateRevForProp(url, rev)].concat(setParts.reverse()).join(' ') 87 }).join(', ') 88 } 89 }) 90} 91 92function OnRefresh(result) { 93 for (var i = 0; i < refreshIds.length; i++) { 94 var id = refreshIds[i]; 95 var newDocument = new DOMParser().parseFromString(result, 'text/html') 96 if (newDocument) { 97 var oldContainerElement = document.getElementById(id) 98 var newContainerElement = newDocument.getElementById(id) 99 100 if (oldContainerElement && newContainerElement) { 101 oldContainerElement.innerHTML = newContainerElement.innerHTML 102 } 103 } 104 } 105 106 // Dispatch custom event for React components to re-attach listeners 107 window.dispatchEvent(new CustomEvent('finviz:refresh', { detail: { ids: refreshIds } })) 108 109 window.gtag && window.gtag('event', 'refresh', { event_category: 'page' }); 110 // Refresh any ads that might be on the page 111 if (window.finvizBannersLoaded === true) { 112 typeof window.initFinvizBanners === "function" && window.initFinvizBanners() 113 } else if (typeof window.refreshAds === 'function') { 114 var adSelectors = refreshIds.reduce(function (prev, current) { 115 prev.push('#' + current + ' [id*=IC_D_]'); 116 return prev; 117 }, []); 118 window.refreshAds(adSelectors.join(',')); 119 } 120} 121 122function simpleFetch(url, options) { 123 return fetch(url, options) 124 .then(function(response) { 125 if (!response.ok) return 126 return response 127 }) 128 .catch(function() {}) 129} 130 131function Refresh() { 132 simpleFetch(window.location.href, { 133 headers: { 134 'X-Theme': window.FinvizSettings.hasDarkTheme ? 'dark' : window.FinvizSettings.hasRedesignEnabled ? 'light' : 'legacy', 135 } 136 }) 137 .then(function(res){ 138 if (!res) { 139 return Promise.resolve(undefined) 140 } 141 142 if (!checkVersionMatches(res.headers)) { 143 window.location.reload() 144 return 145 } 146 return res.text() 147 }) 148 .then(function (data) { 149 if (data === undefined) { 150 return 151 } 152 OnRefresh(data) 153 }) 154} 155 156function updateRevForProp(prop, rev) { 157 let srcRevPos = prop.indexOf("&rev="); 158 let searchPartIndex = prop.indexOf('?') 159 if (srcRevPos >= 0 && searchPartIndex > -1) { 160 let path = prop.substring(0, searchPartIndex) 161 let search = prop.substring(searchPartIndex + 1) 162 let searchParams = new URLSearchParams(search) 163 searchParams.set('rev', rev) 164 165 return path + '?' + searchParams.toString() 166 }; 167 168 return prop + "&rev=" + rev 169} 170 171function checkVersionMatches(headers) { 172 var refreshVersion = headers.get('X-REFRESH-VERSION') 173 if (currentVersion === null) currentVersion = refreshVersion 174 175 return refreshVersion === null || refreshVersion === currentVersion 176}
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.