|
| 1 | +/** |
| 2 | + * Formats a datetime string to "Dec 16, 2025 6:30 PM EST" format |
| 3 | + * @param {String} datetimeString ISO 8601 datetime string |
| 4 | + * @returns {String} Formatted date string |
| 5 | + */ |
| 6 | +function formatTimestamp(datetimeString) { |
| 7 | + const date = new Date(datetimeString); |
| 8 | + const options = { |
| 9 | + month: 'short', |
| 10 | + day: 'numeric', |
| 11 | + year: 'numeric', |
| 12 | + hour: 'numeric', |
| 13 | + minute: 'numeric', |
| 14 | + hour12: true, |
| 15 | + timeZoneName: 'short', |
| 16 | + }; |
| 17 | + return new Intl.DateTimeFormat('en-US', options).format(date); |
| 18 | +} |
| 19 | + |
| 20 | +/** |
| 21 | + * Converts all relative-time elements on the page based on the preference |
| 22 | + * @param {Boolean} useAbsoluteTimestamps Whether to use absolute timestamps |
| 23 | + */ |
| 24 | +function convertTimestamps(useAbsoluteTimestamps) { |
| 25 | + const shouldUseAbsoluteTimestamps = !!useAbsoluteTimestamps; |
| 26 | + |
| 27 | + if (shouldUseAbsoluteTimestamps) { |
| 28 | + // Find all relative-time elements that don't have absolute timestamp added yet |
| 29 | + const elements = document.querySelectorAll('relative-time:not([data-k2-absolute-added])'); |
| 30 | + |
| 31 | + Array.from(elements).forEach((el) => { |
| 32 | + const datetime = el.getAttribute('datetime'); |
| 33 | + |
| 34 | + if (!datetime) { |
| 35 | + return; |
| 36 | + } |
| 37 | + |
| 38 | + // Create a span for the absolute timestamp |
| 39 | + const absoluteSpan = document.createElement('span'); |
| 40 | + const absoluteTime = formatTimestamp(datetime); |
| 41 | + absoluteSpan.textContent = ` (${absoluteTime})`; |
| 42 | + absoluteSpan.dataset.k2AbsolutePart = 'true'; |
| 43 | + |
| 44 | + // Add the absolute span after the relative-time element |
| 45 | + const parent = el.parentNode; |
| 46 | + if (parent) { |
| 47 | + // Insert the absolute span right after the relative-time element |
| 48 | + if (el.nextSibling) { |
| 49 | + parent.insertBefore(absoluteSpan, el.nextSibling); |
| 50 | + } else { |
| 51 | + parent.appendChild(absoluteSpan); |
| 52 | + } |
| 53 | + |
| 54 | + // Mark that we've added the absolute part |
| 55 | + // eslint-disable-next-line no-param-reassign |
| 56 | + el.dataset.k2AbsoluteAdded = 'true'; |
| 57 | + |
| 58 | + // Store datetime for updates |
| 59 | + // eslint-disable-next-line no-param-reassign |
| 60 | + el.dataset.k2OriginalDatetime = datetime; |
| 61 | + } |
| 62 | + }); |
| 63 | + |
| 64 | + // Update existing absolute parts (refresh the absolute timestamp) |
| 65 | + const elementsWithAbsolute = document.querySelectorAll('relative-time[data-k2-absolute-added]'); |
| 66 | + Array.from(elementsWithAbsolute).forEach((el) => { |
| 67 | + const datetime = el.dataset.k2OriginalDatetime || el.getAttribute('datetime'); |
| 68 | + if (datetime) { |
| 69 | + // Find the absolute span that follows this element |
| 70 | + let absoluteSpan = el.nextSibling; |
| 71 | + while (absoluteSpan && (!absoluteSpan.dataset || !absoluteSpan.dataset.k2AbsolutePart)) { |
| 72 | + absoluteSpan = absoluteSpan.nextSibling; |
| 73 | + } |
| 74 | + |
| 75 | + if (absoluteSpan && absoluteSpan.dataset.k2AbsolutePart) { |
| 76 | + const absoluteTime = formatTimestamp(datetime); |
| 77 | + // eslint-disable-next-line no-param-reassign |
| 78 | + absoluteSpan.textContent = ` (${absoluteTime})`; |
| 79 | + } |
| 80 | + } |
| 81 | + }); |
| 82 | + } else { |
| 83 | + // Find all relative-time elements that have absolute timestamps added |
| 84 | + const elements = document.querySelectorAll('relative-time[data-k2-absolute-added]'); |
| 85 | + |
| 86 | + Array.from(elements).forEach((el) => { |
| 87 | + // Find and remove the absolute timestamp span |
| 88 | + let absoluteSpan = el.nextSibling; |
| 89 | + while (absoluteSpan && (!absoluteSpan.dataset || !absoluteSpan.dataset.k2AbsolutePart)) { |
| 90 | + absoluteSpan = absoluteSpan.nextSibling; |
| 91 | + } |
| 92 | + |
| 93 | + if (absoluteSpan && absoluteSpan.dataset.k2AbsolutePart) { |
| 94 | + const parent = absoluteSpan.parentNode; |
| 95 | + if (parent) { |
| 96 | + parent.removeChild(absoluteSpan); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + // Remove our markers |
| 101 | + // eslint-disable-next-line no-param-reassign |
| 102 | + delete el.dataset.k2AbsoluteAdded; |
| 103 | + // eslint-disable-next-line no-param-reassign |
| 104 | + delete el.dataset.k2OriginalDatetime; |
| 105 | + }); |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +export default convertTimestamps; |
0 commit comments