1(function () { 2 const MS_SECOND = 1000; 3 const MS_MINUTE = 60 * MS_SECOND; 4 const MS_HOUR = 60 * MS_MINUTE; 5 const MS_DAY = 24 * MS_HOUR; 6 7 let deadlineTs = null; 8 let currentTimerId = null; 9 let pendingDeadlineTs = null; 10 let pendingTimerId = null; 11 let intervalId = null; 12 let diff = 0; 13 let isFetching = false; 14 15 function getEl(root, cls) { 16 return root.querySelector('#' + cls) || root.querySelector('.' + cls); 17 } 18 19 function getTimerRoots() { 20 const byId = document.getElementById('sale-timer'); 21 const roots = [...document.querySelectorAll('.sale-timer')]; 22 if (byId && !roots.includes(byId)) roots.push(byId); 23 return roots; 24 } 25 26 function fetchDeadline(onSuccess, onFail) { 27 fetch('/Home/GetDeadline') 28 .then(res => res.json()) 29 .then(data => { 30 if (!data?.deadline || !data?.timerId) return onFail?.('nodata'); 31 32 const ts = new Date(data.deadline).getTime(); 33 34 if (ts > Date.now()) 35 onSuccess?.(ts, data.timerId); 36 else 37 onFail?.('expired'); 38 }) 39 .catch(() => onFail?.('error')); 40 } 41 42 setInterval(function () { 43 fetchDeadline(function (newDeadline, newTimerId) { 44 if (deadlineTs === null || currentTimerId === null) { 45 deadlineTs = newDeadline; 46 currentTimerId = newTimerId; 47 return; 48 } 49 50 // Same timer edited -> update immediately 51 if (newTimerId === currentTimerId) { 52 deadlineTs = newDeadline; 53 pendingDeadlineTs = null; 54 pendingTimerId = null; 55 return; 56 } 57 58 // Different timer -> wait until current timer expires 59 pendingDeadlineTs = newDeadline; 60 pendingTimerId = newTimerId; 61 }); 62 }, 15000); 63 64 function updateAllTimers() { 65 if (deadlineTs !== null) diff = deadlineTs - Date.now(); 66 67 const timerRoots = getTimerRoots(); 68 69 if (diff <= 0 || deadlineTs === null) { 70 if (pendingDeadlineTs && pendingDeadlineTs > Date.now()) { 71 deadlineTs = pendingDeadlineTs; 72 currentTimerId = pendingTimerId; 73 pendingDeadlineTs = null; 74 pendingTimerId = null; 75 updateAllTimers(); 76 return; 77 } 78 79 timerRoots.forEach(root => root.style.display = 'none'); 80 81 if (intervalId) { 82 clearInterval(intervalId); 83 intervalId = null; 84 } 85 86 if (!isFetching) { 87 isFetching = true; 88 89 fetchDeadline(function (newTs, newTimerId) { 90 isFetching = false; 91 deadlineTs = newTs; 92 currentTimerId = newTimerId; 93 pendingDeadlineTs = null; 94 pendingTimerId = null; 95 96 timerRoots.forEach(root => root.style.display = ''); 97 updateAllTimers(); 98 intervalId = setInterval(updateAllTimers, 1000); 99 100 }, function () { 101 isFetching = false; 102 deadlineTs = null; 103 currentTimerId = null; 104 pendingDeadlineTs = null; 105 pendingTimerId = null; 106 hideSaleTimer(); 107 }); 108 } 109 110 return; 111 } 112 113 timerRoots.forEach(function (root) { 114 const mode = root.dataset.mode || 'last24h'; 115 const elDays = getEl(root, 'js-sale-days'); 116 const elHours = getEl(root, 'js-sale-hours'); 117 const elMinutes = getEl(root, 'js-sale-minutes'); 118 const elSeconds = getEl(root, 'js-sale-seconds'); 119 120 if (!elDays || !elHours || !elMinutes || !elSeconds) return; 121 122 const daysBox = elDays.closest('.timer-box'); 123 124 if (mode === 'last24h') { 125 if (diff <= MS_DAY) { 126 root.classList.remove('d-none'); 127 if (daysBox) daysBox.style.display = 'none'; 128 } else { 129 root.classList.add('d-none'); 130 return; 131 } 132 } else { 133 if (daysBox) daysBox.style.display = ''; 134 elDays.textContent = Math.floor(diff / MS_DAY); 135 } 136 137 elHours.textContent = Math.floor((diff % MS_DAY) / MS_HOUR).toString().padStart(2, '0'); 138 elMinutes.textContent = Math.floor((diff % MS_HOUR) / MS_MINUTE).toString().padStart(2, '0'); 139 elSeconds.textContent = Math.floor((diff % MS_MINUTE) / MS_SECOND).toString().padStart(2, '0'); 140 }); 141 } 142 143 function hideSaleTimer() {
144 getTimerRoots().forEach(root => root.style.display = 'none'); 145 146 if (intervalId) { 147 clearInterval(intervalId); 148 intervalId = null; 149 } 150 } 151 152 fetchDeadline(function (newTs, newTimerId) { 153 deadlineTs = newTs; 154 currentTimerId = newTimerId; 155 updateAllTimers(); 156 intervalId = setInterval(updateAllTimers, 1000); 157 }, hideSaleTimer); 158 159})();
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.