Initial version

This commit is contained in:
Gergely Polonkai 2023-11-10 17:54:25 +01:00
commit 42ef6c66ea
No known key found for this signature in database
GPG Key ID: 2D2885533B869ED4
5 changed files with 466 additions and 0 deletions

244
clock.js Normal file
View File

@ -0,0 +1,244 @@
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]
+ "<br>"
+ momentLocal.locale(options.locale).format(options.showSeconds ? "LTS" : "LT")
+ "<br>"
+ 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();

37
index.html Normal file
View File

@ -0,0 +1,37 @@
<!doctype html>
<html>
<head>
<title>Seasonal Hours Clock</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<script src="moment-with-locales.min.js"></script>
<script src="moment-timezone-with-data.min.js"></script>
</head>
<body>
<div id="marker"></div>
<div id="utc-hours"><div id="utc-hours-inner"></div></div>
<div id="hour-names"><div id="hour-names-inner"></div></div>
<div id="hour-icons"><div id="hour-icons-inner"></div></div>
<div id="local-hours"><div id="local-hours-inner"></div></div>
<div id="current-time-wrapper"><div id="current-time"></div></div>
<div id="settings-button">⚙️</div>
<div id="settings-wrapper">
<div id="settings-close">×</div>
<div id="settings">
<h2>Settings</h2>
<p>Changes are saved immediately!</p>
<div id="timezone-changer">
<span>Time zone</span><select id="timezone-selector"></select><br>
</div>
<div id="locale-changer">
<span>Locale</span><select id="locale-selector"></select><br>
</div>
<div id="show-seconds">
<span>Show seconds</span><input type="checkbox" id="show-seconds-option">
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>

1
moment-timezone-with-data.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
moment-with-locales.min.js vendored Normal file

File diff suppressed because one or more lines are too long

182
style.css Normal file
View File

@ -0,0 +1,182 @@
body {
background-color: #2e3440;
margin: 0;
}
#settings-wrapper {
width: 40%;
margin: 0 auto;
background-color: white;
padding: 5px;
position: relative;
display: none;
}
#settings-close {
position: absolute;
top: 5px;
right: calc(5px + .5em);
cursor: pointer;
}
#settings {
padding: 1em;
border: 1px solid red;
background-color: white;
}
#settings span {
float: left;
}
#settings select,input {
float: right;
}
#settings br {
float: none;
clear: both;
}
#settings h2 {
margin: 0 0 1em 0;
text-align: center;
}
#settings-button {
position: absolute;
left: 5px;
bottom: 5px;
cursor: pointer;
}
#marker {
width: 1px;
height: 75px;
background-color: rgba(255, 255, 0, 0.5);
position: absolute;
top: 3em;
z-index: 1000;
}
#current-time-wrapper {
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: flex;
justify-content: center;
align-items: center;
}
#current-time {
font-size: 300%;
font-weight: bold;
text-align: center;
color: white;
}
#utc-hours {
white-space: nowrap;
overflow-x: hidden;
position: relative;
margin-top: 3em;
}
#utc-hours-inner {
position: relative;
}
#utc-hours-inner span {
width: var(--hour-width);
display: inline-block;
text-align: center;
color: #555555;
}
#local-hours {
white-space: nowrap;
overflow-x: hidden;
position: relative;
margin-top: -48px;
}
#local-hours-inner {
position: relative;
}
#local-hours-inner span {
width: var(--hour-width);
display: inline-block;
text-align: center;
color: #cccccc;
font-size: 30px;
}
#hour-icons {
white-space: nowrap;
overflow-x: hidden;
position: relative;
}
#hour-icons-inner {
position: relative;
}
#hour-icons-inner span {
width: var(--hour-width);
display: inline-block;
text-align: center;
padding: 5px 0;
font-size: 30px;
}
#hour-names {
white-space: nowrap;
overflow-x: hidden;
position: relative;
}
#hour-names-inner {
position: relative;
}
#hour-names-inner span {
width: var(--hour-width);
display: inline-block;
text-align: center;
color: rgb(238, 187, 85);
padding-top: 5px;
}
#hour-names-inner .winter {
background-color: rgb(70, 62, 108);
}
#hour-names-inner .active.winter {
background-color: rgb(100, 92, 138);
}
#hour-names-inner .spring {
background-color: rgb(55, 87, 55);
}
#hour-names-inner .active.spring {
background-color: rgb(85, 117, 85);
}
#hour-names-inner .summer {
background-color: rgb(113, 92, 43);
}
#hour-names-inner .active.summer {
background-color: rgb(143, 122, 73);
}
#hour-names-inner .autumn {
background-color: rgb(108, 68, 44);
}
#hour-names-inner .active.autumn {
background-color: rgb(138, 98, 74);
}