75 lines
2.8 KiB
JavaScript
75 lines
2.8 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
const language = document.documentElement.lang || navigator.language || 'de-DE';
|
|
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
const formatter = new Intl.DateTimeFormat(language, {
|
|
dateStyle: 'short',
|
|
timeStyle: 'medium'
|
|
});
|
|
|
|
function parseUtc(value) {
|
|
if (!value) return null;
|
|
let normalized = String(value).trim();
|
|
if (!normalized) return null;
|
|
// Project database timestamps without an offset are UTC by convention.
|
|
if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/.test(normalized)) {
|
|
normalized = normalized.replace(' ', 'T') + 'Z';
|
|
}
|
|
const date = new Date(normalized);
|
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
}
|
|
|
|
function formatUtc(value, fallback = '') {
|
|
const date = parseUtc(value);
|
|
return date ? formatter.format(date) : (fallback || value || '');
|
|
}
|
|
|
|
function localizeElement(element) {
|
|
const value = element.getAttribute('datetime') || element.dataset.utc;
|
|
if (!value) return;
|
|
const formatted = formatUtc(value, element.textContent);
|
|
element.textContent = formatted;
|
|
element.title = `${value} UTC · ${zone}`;
|
|
element.dataset.localTimeApplied = 'true';
|
|
const cell = element.closest('[data-sort-value]');
|
|
if (cell) cell.dataset.sortValue = value;
|
|
}
|
|
|
|
const isoPattern = /(?<!\d)(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)(?!\d)/g;
|
|
function localizeLogText(element) {
|
|
if (!element || element.dataset.localizingLog === 'true') return;
|
|
const original = element.textContent || '';
|
|
const converted = original.replace(isoPattern, match => formatUtc(match, match));
|
|
if (converted !== original) {
|
|
element.dataset.localizingLog = 'true';
|
|
element.textContent = converted;
|
|
delete element.dataset.localizingLog;
|
|
}
|
|
element.title = zone;
|
|
}
|
|
|
|
function scan(root = document) {
|
|
if (root.matches?.('time[data-utc], time[datetime].local-time')) localizeElement(root);
|
|
root.querySelectorAll?.('time[data-utc], time[datetime].local-time').forEach(localizeElement);
|
|
if (root.matches?.('[data-localize-log]')) localizeLogText(root);
|
|
root.querySelectorAll?.('[data-localize-log]').forEach(localizeLogText);
|
|
}
|
|
|
|
window.AssetManagerTime = {formatUtc, parseUtc, zone, scan};
|
|
scan(document);
|
|
|
|
const observer = new MutationObserver(mutations => {
|
|
for (const mutation of mutations) {
|
|
mutation.addedNodes.forEach(node => {
|
|
if (node.nodeType === Node.ELEMENT_NODE) scan(node);
|
|
});
|
|
const owner = mutation.target.nodeType === Node.ELEMENT_NODE
|
|
? mutation.target.closest?.('[data-localize-log]')
|
|
: mutation.target.parentElement?.closest?.('[data-localize-log]');
|
|
if (owner) localizeLogText(owner);
|
|
}
|
|
});
|
|
observer.observe(document.body, {childList: true, subtree: true, characterData: true});
|
|
})();
|