@@ -83,50 +83,90 @@ const languageEntries = Object.entries(languages);
8383 </div >
8484 </footer >
8585 <script >
86+ // Set the date we're counting down to
87+ const countDownDate = new Date("Sep 1, 2026 00:00:00").getTime();
88+
89+ const locale = document.documentElement.lang; // navigator.language; // Detects browser language (e.g., 'en-US');
90+
91+ // Use Intl.RelativeTimeFormat for localized abbreviations
92+ // 'narrow' style gives us 'd', 'h', 'm', 's' in most locales
93+ const formatter = new Intl.RelativeTimeFormat(locale, { style: 'narrow' });
94+
95+ const prefix = new Array(4);
96+ const suffix = new Array(4);
97+
98+ function getOffset(unit) {
99+ switch (unit) {
100+ case 'day':
101+ return 0;
102+ case 'hour':
103+ return 1;
104+ case 'minute':
105+ return 2;
106+ case 'second':
107+ return 3;
108+ }
109+ }
110+
111+ function extractCommon(p, c, reverse) {
112+ let s = 0;
113+ let w = 0;
114+ let i = reverse ? p.length - 1 : 0;
115+ let j = reverse ? c.length - 1 : 0;
116+ const pEnd = reverse ? 0 : p.length;
117+ const cEnd = reverse ? 0 : c.length;
118+ let chr;
119+ while ((reverse ? i >= pEnd : i < pEnd) && (reverse ? j >= cEnd : j < cEnd) && (chr = p[reverse ? i-- : i++]) === c[reverse ? j-- : j++]) {
120+ w = chr === ' ' ? w + 1 : 0;
121+ s++;
122+ }
123+ return s - w;
124+ }
125+
126+ function cacheFormattingInfo(value, unit) {
127+ // FormatToParts lets us extract just the unit identifier
128+ const p = formatter.formatToParts(value, unit);
129+ if (!p.length) return;
130+ const c = formatter.formatToParts(-value, unit);
131+
132+ const offset = getOffset(unit);
133+ if (p[0].type === 'literal') {
134+ if (!c.length || c[0].type !== 'literal') {
135+ prefix[offset] = p[0].value.length;
136+ } else if (!c[0].value.endsWith(p[0].value)) {
137+ prefix[offset] = p[0].value.length - extractCommon(p[0].value, c[0].value, true);
138+ }
139+ }
140+ if (p[p.length - 1].type === 'literal') {
141+ if (!c.length || c[c.length - 1].type !== 'literal') {
142+ suffix[offset] = p[p.length - 1].value.length;
143+ } else if (!c[c.length - 1].value.startsWith(p[p.length - 1].value)) {
144+ suffix[offset] = p[p.length - 1].value.length - extractCommon(p[p.length - 1].value, c[c.length - 1].value, false);
145+ }
146+ }
147+ }
148+
149+ cacheFormattingInfo(1, 'day');
150+ cacheFormattingInfo(2, 'hour');
151+ cacheFormattingInfo(3, 'minute');
152+ cacheFormattingInfo(4, 'second');
153+
86154 /**
87155 * Localizes a duration based on the browser's language settings.
88156 * @param {number} value - The numerical value (e.g., 5)
89157 * @param {string} unit - The unit ('day', 'hour', 'minute', 'second')
90- * @param {string} locale - The BCP 47 language tag
91158 */
92- function getLocalizedUnit(value, unit, locale, trimConjunction) {
93- // Use Intl.RelativeTimeFormat for localized abbreviations
94- // 'narrow' style gives us 'd', 'h', 'm', 's' in most locales
95-
96- const formatter = new Intl.RelativeTimeFormat(locale, { style: 'narrow' });
97-
98- function findCommonObject(arrays) {
99- if (!arrays.length) return null;
100-
101- return arrays.reduce((common, currentArray) => {
102- return common.filter(objA =>
103- // Check if the current array contains an object that matches objA
104- currentArray.some(objB => JSON.stringify(objA) === JSON.stringify(objB))
105- );
106- });
107- }
108-
109- const p1 = formatter.formatToParts(1, 'day');
110- const p2 = formatter.formatToParts(2, 'hour');
111- const p3 = formatter.formatToParts(3, 'minute');
112- const p4 = formatter.formatToParts(4, 'second');
113- var prefixParts = findCommonObject([p1, p2, p3, p4]);
114- var prefix = prefixParts.length == 0 ? "" : prefixParts[0].value;
115-
116- // FormatToParts lets us extract just the unit identifier
117- const parts = formatter.formatToParts(value, unit);
118-
119- const segments = parts
120- .filter(p => p.type === "integer" || p.type === "literal" || p.type === "unit")
121- .filter(p => p.value !== prefix)
122- .map(p => p.value)
123- .join("");
124-
125- return `${trimConjunction ? "" : prefix}${segments}`;
159+ function getLocalizedUnit(value, unit, trimConjunction, trimSuffix) {
160+ const offset = getOffset(unit);
161+ const string = formatter.format(value, unit);
162+ const p = prefix[offset];
163+ const s = suffix[offset];
164+ return string.slice(trimConjunction && p || p == 1 && string[0] === '+' ? prefix[offset] : 0, trimSuffix && s ? -suffix[offset] : string.length);
126165 }
127-
128- // Set the date we're counting down to
129- var countDownDate = new Date("Sep 1, 2026 00:00:00").getTime();
166+
167+ const remaining = new Array(7);
168+ const separator = ' ';
169+ var timer = null;
130170
131171 function updateBanner() {
132172 // Get today's date and time
@@ -141,28 +181,27 @@ const languageEntries = Object.entries(languages);
141181 var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
142182 var seconds = Math.floor((distance % (1000 * 60)) / 1000);
143183
144- const locale = document.documentElement.lang; // navigator.language; // Detects browser language (e.g., 'en-US');
145-
146- var remaining = [
147- days > 0 ? getLocalizedUnit(days, 'day', locale, false) : '',
148- hours > 0 || days > 0 ? getLocalizedUnit(hours, 'hour', locale, true) : '',
149- minutes > 0 || hours > 0 || days > 0 ? getLocalizedUnit(minutes, 'minute', locale, true) : '',
150- getLocalizedUnit(seconds, 'second', locale, true)
151- ].filter(Boolean).join(' ');
152- remaining = remaining.replace(/^\+\s*/, '');
184+ var parts = 0;
185+ remaining[0] = days > 0 ? getLocalizedUnit(days, 'day', parts++, true) : null;
186+ remaining[1] = parts ? separator : null;
187+ remaining[2] = parts || hours > 0 ? getLocalizedUnit(hours, 'hour', parts++, true) : null;
188+ remaining[3] = parts ? separator : null;
189+ remaining[4] = parts || minutes > 0 ? getLocalizedUnit(minutes, 'minute', parts++, true) : null;
190+ remaining[5] = parts ? separator : null;
191+ remaining[6] = getLocalizedUnit(seconds, 'second', parts++, false);
153192
154193 // Display the result in the element with id="countdown"
155- document.getElementById("countdown").textContent = remaining;
194+ document.getElementById("countdown").textContent = remaining.join('') ;
156195
157196 // // If the count down is finished, write some text
158- // if (distance < 0) {
159- // clearInterval(x );
197+ if (distance < 0) {
198+ clearInterval(timer );
160199 // document.getElementById("countdown").innerHTML = "EXPIRED";
161- // }
200+ }
162201 }
163202
164203 // Update the count down every 1 second
165- setInterval(updateBanner, 1000);
204+ timer = setInterval(updateBanner, 1000);
166205 updateBanner();
167206 </script >
168207</Base >
0 commit comments