const DEFAULT_OPTIONS = {showSeconds: true} const HOUR_NAMES = [ "Candle", "Ice", "Comet", "Owl", "Yarn", "Mist", "Sprout", "Rainbow", "Worm", "Rabbit", "Blossom", "Nest", "Coral", "Cherry", "Bee", "Melon", "Seashell", "Dragon", "Chestnut", "Kite", "Mushroom", "Lightning", "Mountain", "Lantern", ]; const HOUR_WIDTH = 70; const HOUR_ICONS = [ "🕯️️", "❄️️", "☄️️", "🦉️", "🧶️", "🌫️️", "🌱️", "🌈️", "🪱️", "🐇️", "🌸️", "🪺️", "🪸️", "🍒️", "🐝️", "🍉️", "🐚️", "🐉️", "🌰️", "🪁️", "🍄️", "⚡️️", "⛰️️", "🏮️", ]; const loadSettings = () => { var loadedOpts; try { var data = localStorage.getItem("options"); console.debug("Loaded", data); loadedOpts = JSON.parse(data); } catch (e) { loadedOpts = null; } console.debug("Parsed as", loadedOpts); return loadedOpts || DEFAULT_OPTIONS; }; const saveSettings = () => { console.debug("Saving", options) localStorage.setItem("options", JSON.stringify(options)); }; const zeroPad = (num, numZeros) => { if (num == 0) { return "".padStart(numZeros, "0") } var an = Math.abs (num); var digitCount = 1 + Math.floor (Math.log (an) / Math.LN10); if (digitCount >= numZeros) { return String(num); } var zeroString = Math.pow (10, numZeros - digitCount).toString ().substr (1); return num < 0 ? "-" + zeroString + an : zeroString + an; }; const updateClock = () => { var docCenter = document.body.offsetWidth / 2; var momentNow = moment(); var momentUTC = momentNow.utc(); var utcHour = momentUTC.hours(); var utcMinute = momentUTC.minutes(); var momentLocal = momentNow.tz(options.timezone); var localHour = momentLocal.hours(); var localMinute = momentLocal.minutes(); currentTimeContainer.innerHTML = ( HOUR_ICONS[utcHour] + "
" + momentLocal.locale(options.locale).format(options.showSeconds ? "LTS" : "LT") + "
" + HOUR_NAMES[utcHour] ); var utcOffset = ( 24 * HOUR_WIDTH + utcHour * HOUR_WIDTH + utcMinute * HOUR_WIDTH / 60 ); var localOffset = ( 24 * HOUR_WIDTH + localHour * HOUR_WIDTH + localMinute * HOUR_WIDTH / 60 ); utcHourContainer.style.left = (docCenter - utcOffset - HOUR_WIDTH / 2) + "px"; hourNameContainer.style.left = (docCenter - utcOffset) + "px"; hourIconContainer.style.left = (docCenter - utcOffset) + "px"; localHourContainer.style.left = (docCenter - localOffset - HOUR_WIDTH / 2) + "px"; marker.style.left = docCenter + "px"; for (var nameSpan of document.getElementsByClassName("active")) { nameSpan.classList.remove("active"); } var currentNameSpan = document.getElementById("h-present-" + utcHour); currentNameSpan.classList.add("active"); }; const showSettingsPanel = () => { settingsPanel.style.display = "block"; }; const hideSettingsPanel = () => { settingsPanel.style.display = "none"; }; const createClock = () => { for (round = 0; round < 3; round++) { let prefix; switch (round) { case 0: prefix = "past"; break; case 1: prefix = "present"; break; case 2: prefix = "future"; break; } for (hour = 0; hour < 24; hour++) { var nameClass; if (hour < 6) { nameClass = "winter"; } else if (hour < 12) { nameClass = "spring"; } else if (hour < 18) { nameClass = "summer"; } else { nameClass = "autumn"; } let utcSpan = document.createElement("span"); utcSpan.id = "u-" + prefix + "-" + hour; utcSpan.innerHTML = "U " + zeroPad(hour, 2); utcHourContainer.appendChild(utcSpan); let localSpan = document.createElement("span"); localSpan.id = "l-" + prefix + "-" + hour; localSpan.innerHTML = zeroPad(hour, 2); localHourContainer.appendChild(localSpan); let hourNameSpan = document.createElement("span"); hourNameSpan.id = "h-" + prefix + "-" + hour; hourNameSpan.classList.add(nameClass); hourNameSpan.innerHTML = HOUR_NAMES[hour]; hourNameContainer.appendChild(hourNameSpan); let hourIconSpan = document.createElement("span"); hourIconSpan.id = "i-" + prefix + "-" + hour; hourIconSpan.classList.add(nameClass); hourIconSpan.innerHTML = HOUR_ICONS[hour]; hourIconContainer.appendChild(hourIconSpan); } } }; const initSettings = () => { var timeZoneSelector = document.getElementById("timezone-selector"); if (!options.timezone) { options.timezone = moment.tz.guess(); } for (tzname of moment.tz.names()) { var tzElem = document.createElement("option"); tzElem.innerHTML = tzname; if (tzname == options.timezone) { tzElem.selected = true; } timeZoneSelector.appendChild(tzElem); } timeZoneSelector.addEventListener("change", () => { options.timezone = timeZoneSelector.value; console.log(options) updateClock(); saveSettings(); }) var localeSelector = document.getElementById("locale-selector"); if (!options.locale) { options.locale = moment.locale(); } for (localename of moment.locales()) { var locElem = document.createElement("option"); locElem.innerHTML = localename; if (localename == options.locale) { locElem.selected = true; } localeSelector.appendChild(locElem); } localeSelector.addEventListener("change", () => { options.locale = localeSelector.value; updateClock(); saveSettings(); }); var showSecondsOption = document.getElementById("show-seconds-option"); showSecondsOption.checked = options.showSeconds; showSecondsOption.addEventListener("click", () => { options.showSeconds = showSecondsOption.checked; updateClock(); saveSettings(); }); showSettingsButton.addEventListener("click", showSettingsPanel); closeSettingsButton.addEventListener("click", hideSettingsPanel); } const run = () => { document.documentElement.style.setProperty("--hour-width", HOUR_WIDTH + "px"); createClock(); initSettings(); addEventListener("resize", updateClock); updateClock(); setInterval(updateClock, 1000); } var options = loadSettings(); let utcHourContainer = document.getElementById("utc-hours-inner"); let hourNameContainer = document.getElementById("hour-names-inner"); let hourIconContainer = document.getElementById("hour-icons-inner"); let localHourContainer = document.getElementById("local-hours-inner"); let currentTimeContainer = document.getElementById("current-time"); let showSettingsButton = document.getElementById("settings-button"); let closeSettingsButton = document.getElementById("settings-close"); let settingsPanel = document.getElementById("settings-wrapper") let marker = document.getElementById("marker"); run();