diff --git a/src/index.html b/src/index.html index 6b36a88..d35eec8 100644 --- a/src/index.html +++ b/src/index.html @@ -1,8 +1,6 @@ - - w-timerer diff --git a/src/index.js b/src/index.js index ce79324..fd68996 100644 --- a/src/index.js +++ b/src/index.js @@ -1,81 +1,49 @@ -function updateTimes() { - const travelTime = 1; // in hours +const TRAVEL_TIME_HOURS = 1; +const FIRST_HOUR = 13; +const LAST_HOUR = 22; - const now = new Date(); - const currentHours = now.getHours(); - const currentMinutes = now.getMinutes(); - const currentSeconds = now.getSeconds(); +let clockEl, dateEl; +let diffNodes = []; +let resizeTimer = null; +let lastDayKey = ""; - const currentTimeElement = document.getElementById('currentTime'); - currentTimeElement.textContent = `${currentHours.toString().padStart(2, '0')}:${currentMinutes.toString().padStart(2, '0')}:${currentSeconds.toString().padStart(2, '0')}`; - - const targetTimesElement = document.getElementById('targetTimes'); - const leaveTimesElement = document.getElementById('leaveTimes'); - const timeDifferencesElement = document.getElementById('timeDifferences'); - - targetTimesElement.innerHTML = ''; - leaveTimesElement.innerHTML = ''; - timeDifferencesElement.innerHTML = ''; - - const ttBlock = document.createElement('span'); - ttBlock.className = 'time-block label-block'; - ttBlock.textContent = 'TT'; - targetTimesElement.appendChild(ttBlock); - - const ltBlock = document.createElement('span'); - ltBlock.className = 'time-block label-block'; - ltBlock.textContent = 'LT'; - leaveTimesElement.appendChild(ltBlock); - - const tdBlock = document.createElement('span'); - tdBlock.className = 'time-block label-block'; - tdBlock.textContent = 'TD'; - timeDifferencesElement.appendChild(tdBlock); - - const targetTimeBlocks = []; - const leaveTimeBlocks = []; - const diffTimeBlocks = []; - - for (let hour = 13; hour <= 22; hour++) { - let leaveHour = hour - travelTime; - - const timeBlock = document.createElement('span'); - timeBlock.className = 'time-block'; - timeBlock.textContent = `${hour.toString().padStart(2, '0')}:00`; - targetTimeBlocks.push(timeBlock); - - const leaveTimeBlock = document.createElement('span'); - leaveTimeBlock.className = 'time-block'; - leaveTimeBlock.textContent = `${(leaveHour).toString().padStart(2, '0')}:00`; - leaveTimeBlocks.push(leaveTimeBlock); - - const targetTime = new Date(); - targetTime.setHours(leaveHour, 0, 0, 0); - - const diffBlock = document.createElement('span'); - diffBlock.className = 'time-block'; - - const diffMs = targetTime - now; - if (diffMs < 0) { - const diffHours = -1 * Math.ceil(diffMs / (1000 * 60 * 60)); - const diffMinutes = -1 * Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); - diffBlock.textContent = `-${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; - } else { - const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); - const diffMinutes = Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); - diffBlock.textContent = `${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; - } - - diffTimeBlocks.push(diffBlock); - } - - distributeBlocks(targetTimesElement, targetTimeBlocks); - distributeBlocks(leaveTimesElement, leaveTimeBlocks); - distributeBlocks(timeDifferencesElement, diffTimeBlocks); - - updateCalendar(); +function pad2(n) { + return n < 10 ? "0" + n : "" + n; } +function formatClock(d) { + return pad2(d.getHours()) + ":" + pad2(d.getMinutes()) + ":" + pad2(d.getSeconds()); +} + +function formatDate(d) { + return pad2(d.getDate()) + "." + pad2(d.getMonth() + 1) + "." + d.getFullYear(); +} + +function diffText(targetMs, nowMs) { + const diffMs = targetMs - nowMs; + if (diffMs < 0) { + const diffHours = -1 * Math.ceil(diffMs / 3600000); + const diffMinutes = -1 * Math.ceil((diffMs % 3600000) / 60000); + return "-" + pad2(diffHours) + ":" + pad2(diffMinutes); + } + const diffHours = Math.floor(diffMs / 3600000); + const diffMinutes = Math.ceil((diffMs % 3600000) / 60000); + return pad2(diffHours) + ":" + pad2(diffMinutes); +} + +function buildLabel(text) { + const span = document.createElement("span"); + span.className = "time-block label-block"; + span.textContent = text; + return span; +} + +function buildTimeBlock(text) { + const span = document.createElement("span"); + span.className = "time-block"; + span.textContent = text; + return span; +} function distributeBlocks(container, blocks) { const screenWidth = window.innerWidth; @@ -85,86 +53,105 @@ function distributeBlocks(container, blocks) { const availableWidth = screenWidth - labelWidth; const blocksPerRow = Math.floor(availableWidth / blockWidth); + while (container.childNodes.length > 1) { + container.removeChild(container.lastChild); + } + if (blocksPerRow > blocks.length) { - blocks.forEach(block => container.appendChild(block)); + blocks.forEach(function (b) { container.appendChild(b); }); return; } const totalBlocks = blocks.length; const halfBlocks = Math.ceil(totalBlocks / 2); - const rowsContainer = document.createElement('div'); - rowsContainer.className = 'multi-rows-container'; + const rowsContainer = document.createElement("div"); + rowsContainer.className = "multi-rows-container"; - const topRow = document.createElement('div'); - topRow.className = 'time-row'; + const topRow = document.createElement("div"); + topRow.className = "time-row"; for (let i = 0; i < halfBlocks; i++) { topRow.appendChild(blocks[i]); } rowsContainer.appendChild(topRow); - const bottomRow = document.createElement('div'); - bottomRow.className = 'time-row'; + const bottomRow = document.createElement("div"); + bottomRow.className = "time-row"; for (let i = halfBlocks; i < totalBlocks; i++) { bottomRow.appendChild(blocks[i]); } rowsContainer.appendChild(bottomRow); - const label = container.firstChild; container.appendChild(rowsContainer); } -function updateCalendar() { +function buildTargetBlocks() { + const targetTimesElement = document.getElementById("targetTimes"); + const leaveTimesElement = document.getElementById("leaveTimes"); + const timeDifferencesElement = document.getElementById("timeDifferences"); + + targetTimesElement.appendChild(buildLabel("TT")); + leaveTimesElement.appendChild(buildLabel("LT")); + timeDifferencesElement.appendChild(buildLabel("TD")); + + const targetBlocks = []; + const leaveBlocks = []; + const diffBlocks = []; + + for (let hour = FIRST_HOUR; hour <= LAST_HOUR; hour++) { + const leaveHour = hour - TRAVEL_TIME_HOURS; + + targetBlocks.push(buildTimeBlock(pad2(hour) + ":00")); + leaveBlocks.push(buildTimeBlock(pad2(leaveHour) + ":00")); + diffBlocks.push(buildTimeBlock("")); + } + + distributeBlocks(targetTimesElement, targetBlocks); + distributeBlocks(leaveTimesElement, leaveBlocks); + distributeBlocks(timeDifferencesElement, diffBlocks); + + diffNodes = diffBlocks; +} + +function buildCalendar() { + const calendar = document.getElementById("calendar"); + calendar.innerHTML = ""; + const now = new Date(); - const currentDateElement = document.getElementById('currentDate'); + const daysOfWeek = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]; - const day = now.getDate().toString().padStart(2, '0'); - const month = (now.getMonth() + 1).toString().padStart(2, '0'); - const year = now.getFullYear(); - currentDateElement.textContent = `${day}.${month}.${year}`; - - const calendarElement = document.getElementById('calendar'); - calendarElement.innerHTML = ''; - - const daysOfWeek = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']; - - const firstDayOfMonth = new Date(year, now.getMonth(), 1); - let firstDayWeek = firstDayOfMonth.getDay(); - firstDayWeek = firstDayWeek === 0 ? 7 : firstDayWeek; - - const lastDayOfMonth = new Date(year, now.getMonth() + 1, 0); - const daysInMonth = lastDayOfMonth.getDate(); - - const lastDayOfPrevMonth = new Date(year, now.getMonth(), 0); - const daysInPrevMonth = lastDayOfPrevMonth.getDate(); - - const thead = document.createElement('thead'); - const headerRow = document.createElement('tr'); - daysOfWeek.forEach((day, index) => { - const th = document.createElement('th'); + const thead = document.createElement("thead"); + const headerRow = document.createElement("tr"); + daysOfWeek.forEach(function (day, index) { + const th = document.createElement("th"); th.textContent = day; - th.className = 'calendar-weekday'; if (index === 5 || index === 6) { - th.classList.add('weekend-text'); + th.classList.add("weekend-text"); } headerRow.appendChild(th); }); thead.appendChild(headerRow); - calendarElement.appendChild(thead); + calendar.appendChild(thead); - const tbody = document.createElement('tbody'); + const tbody = document.createElement("tbody"); + const year = now.getFullYear(); + const month = now.getMonth(); + const firstDayOfMonth = new Date(year, month, 1); + let firstDayWeek = firstDayOfMonth.getDay(); + firstDayWeek = firstDayWeek === 0 ? 7 : firstDayWeek; + + const daysInMonth = new Date(year, month + 1, 0).getDate(); + const daysInPrevMonth = new Date(year, month, 0).getDate(); let dayCounter = 1; let nextMonthDayCounter = 1; - let startDay = daysInPrevMonth - firstDayWeek + 2; - for (let row = 0; row < 5; row++) { - const tr = document.createElement('tr'); - + const totalRows = Math.ceil((firstDayWeek - 1 + daysInMonth) / 7); + for (let row = 0; row < totalRows; row++) { + const tr = document.createElement("tr"); for (let col = 0; col < 7; col++) { - const td = document.createElement('td'); - td.className = 'calendar-cell'; + const td = document.createElement("td"); let cellDay; let isCurrentMonth = true; @@ -184,28 +171,100 @@ function updateCalendar() { td.textContent = cellDay; if (isCurrentMonth && cellDay === now.getDate()) { - td.classList.add('today-text'); + td.classList.add("today-text"); } - if (col === 5 || col === 6) { - td.classList.add('weekend-text'); + td.classList.add("weekend-text"); } - if (!isCurrentMonth) { - td.classList.add('other-month'); + td.classList.add("other-month"); } - tr.appendChild(td); } - tbody.appendChild(tr); } - - calendarElement.appendChild(tbody); + calendar.appendChild(tbody); } +function updateClock() { + const now = new Date(); + clockEl.textContent = formatClock(now); + scheduleNextClockTick(); +} -// Init -updateTimes(); -setInterval(updateTimes, 200); -window.addEventListener('resize', updateTimes); +function scheduleNextClockTick() { + const delay = 1000 - new Date().getMilliseconds(); + setTimeout(updateClock, delay); +} + +function updateDiffs() { + const now = new Date(); + for (let hour = FIRST_HOUR; hour <= LAST_HOUR; hour++) { + const target = new Date(); + target.setHours(hour - TRAVEL_TIME_HOURS, 0, 0, 0); + diffNodes[hour - FIRST_HOUR].textContent = diffText(target.getTime(), now.getTime()); + } + scheduleNextDiffTick(); +} + +function scheduleNextDiffTick() { + const now = new Date(); + const msUntilNextMinute = + (60 - now.getSeconds()) * 1000 - now.getMilliseconds(); + setTimeout(updateDiffs, msUntilNextMinute); +} + +function checkDayChange() { + const now = new Date(); + const key = + now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate(); + if (key !== lastDayKey) { + lastDayKey = key; + dateEl.textContent = formatDate(now); + buildCalendar(); + } +} + +function init() { + clockEl = document.getElementById("currentTime"); + dateEl = document.getElementById("currentDate"); + + buildTargetBlocks(); + buildCalendar(); + + const now = new Date(); + lastDayKey = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate(); + dateEl.textContent = formatDate(now); + + updateClock(); + updateDiffs(); + + setInterval(checkDayChange, 60000); +} + +function onResize() { + if (resizeTimer !== null) clearTimeout(resizeTimer); + resizeTimer = setTimeout(function () { + resizeTimer = null; + const targetTimesElement = document.getElementById("targetTimes"); + const leaveTimesElement = document.getElementById("leaveTimes"); + const timeDifferencesElement = document.getElementById("timeDifferences"); + + const allBlocks = [].slice.call( + timeDifferencesElement.querySelectorAll(".time-block:not(.label-block)") + ); + const targetBlocks = [].slice.call( + targetTimesElement.querySelectorAll(".time-block:not(.label-block)") + ); + const leaveBlocks = [].slice.call( + leaveTimesElement.querySelectorAll(".time-block:not(.label-block)") + ); + + distributeBlocks(targetTimesElement, targetBlocks); + distributeBlocks(leaveTimesElement, leaveBlocks); + distributeBlocks(timeDifferencesElement, allBlocks); + }, 150); +} + +init(); +window.addEventListener("resize", onResize);