Initial version
This commit is contained in:
commit
42ef6c66ea
244
clock.js
Normal file
244
clock.js
Normal 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
37
index.html
Normal 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
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
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
182
style.css
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user