<!doctype html>
<html lang="nl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>24Hr Day Time Circle Schedule</title>
<style>
body { margin:0; background:#111827; color:#eee; font-family:sans-serif; padding:20px; }
h1 { font-size:18px; margin-bottom:10px; }
/* Day Navigation */
.day-navigation { display:flex; align-items:center; justify-content:center; margin-bottom:15px; gap:10px; }
.nav-btn { background:#374151; color:#eee; border:none; border-radius:50%; width:30px; height:30px; cursor:pointer; font-size:16px; }
.day-slider { display:flex; overflow-x:auto; gap:8px; padding:10px 0; scrollbar-width:none; }
.day-slider::-webkit-scrollbar { display:none; }
.day-tab { padding:8px 12px; background:#374151; border-radius:8px; cursor:pointer; white-space:nowrap; flex-shrink:0; }
.day-tab.active { background:#06b6d4; color:#111; font-weight:bold; }
.circlewrap { position:relative; display:inline-block; }
canvas { background:#111; border-radius:50%; }
.time { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
font-size:20px; font-weight:bold; color:#06b6d4; text-align:center; }
.dateInfo { position:absolute; top:60%; left:50%; transform:translate(-50%, -50%);
font-size:12px; color:#fbbf24; text-align:center; line-height:1.3; }
.moon { position:absolute; top:36%; left:50%; transform:translate(-50%, -50%); font-size:26px; }
.moonText { position:absolute; top:44%; left:50%; transform:translate(-50%, -50%);
font-size:12px; color:#fbbf24; }
#sunTimes { margin-top:8px; text-align:center; font-size:14px; color:#fbbf24; }
#geoMsg { margin-top:4px; text-align:center; font-size:13px; color:#ef4444; }
form { margin-top:15px; display:flex; flex-wrap:wrap; gap:8px; justify-content:center; }
input[type="time"] { padding:6px; border-radius:6px; border:none; }
button { padding:6px 10px; border-radius:6px; border:none; cursor:pointer; }
.addBtn { flex-basis:100%; max-width:150px; margin-top:5px; background:#06b6d4; color:#111; }
ul { list-style:none; padding:0; margin-top:15px; }
li { display:flex; justify-content:space-between; align-items:center;
padding:6px 8px; margin-bottom:6px; background:#1f2937; border-radius:6px; flex-wrap:wrap; gap:6px; }
.controls { display:flex; gap:6px; }
.btn { padding:4px 8px; border-radius:4px; border:none; cursor:pointer; font-size:12px; }
.delBtn { background:#ef4444; color:white; }
.editBtn { background:#f59e0b; color:white; }
.toggleBtn { background:#06b6d4; color:#111; }
.daysContainer { display:flex; gap:2px; margin:4px 0; }
.dayBtn { width:20px; height:20px; border-radius:3px; border:none; cursor:pointer;
font-size:11px; font-weight:bold; display:flex; align-items:center; justify-content:center; }
.dayBtn.on { background:#10b981; color:white; }
.dayBtn.off { background:#6b7280; color:#9ca3af; }
.slotInfo { display:flex; flex-direction:column; }
.current-day-indicator { text-align:center; margin-bottom:10px; font-size:14px; color:#fbbf24; }
.sun-based-indicator { color:#fbbf24; font-size:11px; margin-top:2px; }
/* New styles for sunrise/sunset form */
.sun-form { margin-top:15px; padding:12px; background:#1f2937; border-radius:8px; }
.sun-form h3 { margin:0 0 10px 0; font-size:16px; color:#fbbf24; text-align:center; }
.sun-form-row { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:8px; }
.sun-form-row label { flex:1; min-width:120px; }
.sun-form-row select { flex:1; min-width:120px; padding:6px; border-radius:6px; border:none; background:#374151; color:#eee; }
.sun-form-row .addBtn { margin-top:5px; }
</style>
</head>
<body>
<h1>24/7 Circular TimeSlots Timer</h1>
<!-- Day Navigation -->
<div class="day-navigation">
<button class="nav-btn" id="prevDay">â†</button>
<div class="day-slider" id="daySlider">
<!-- Days will be populated by JavaScript -->
</div>
<button class="nav-btn" id="nextDay">→</button>
</div>
<div class="current-day-indicator" id="currentDayIndicator"></div>
<div class="circlewrap">
<canvas id="dayCircle" width="320" height="320"></canvas>
<div class="moon" id="moonIcon">🌕</div>
<div class="moonText" id="moonText">Volle Maan</div>
<div class="time" id="centerTime">--:--</div>
<div class="dateInfo" id="dateInfo">--</div>
</div>
<div id="sunTimes">Zonsopgang: --:-- | Zonsondergang: --:--</div>
<div id="geoMsg"></div><br>
<!-- New Sunrise/Sunset Form -->
<div class="sun-form">
<h3>Voeg tijdslot toe op basis van zonsopgang/ondergang</h3>
<div class="sun-form-row">
<label for="sunEvent">Gebeurtenis:</label>
<select id="sunEvent">
<option value="sunrise">Zonsopgang</option>
<option value="sunset">Zonsondergang</option>
</select>
</div>
<div class="sun-form-row">
<label for="sunOffset">Offset:</label>
<select id="sunOffset">
<option value="-120">2 uur voor</option>
<option value="-90">1,5 uur voor</option>
<option value="-60">1 uur voor</option>
<option value="-45">45 minuten voor</option>
<option value="-30">30 minuten voor</option>
<option value="-15">15 minuten voor</option>
<option value="0">Precies</option>
<option value="15">15 minuten na</option>
<option value="30">30 minuten na</option>
<option value="45">45 minuten na</option>
<option value="60">1 uur na</option>
<option value="90">1,5 uur na</option>
<option value="120">2 uur na</option>
</select>
</div>
<div class="sun-form-row">
<label for="sunDuration">Duur:</label>
<select id="sunDuration">
<option value="15">15 minuten</option>
<option value="30">30 minuten</option>
<option value="45">45 minuten</option>
<option value="60">1 uur</option>
<option value="90">1,5 uur</option>
<option value="120">2 uur</option>
<option value="180">3 uur</option>
<option value="240">4 uur</option>
</select>
</div>
<div class="daysContainer">
<button type="button" class="dayBtn on" data-day="0">M</button>
<button type="button" class="dayBtn on" data-day="1">T</button>
<button type="button" class="dayBtn on" data-day="2">W</button>
<button type="button" class="dayBtn on" data-day="3">T</button>
<button type="button" class="dayBtn on" data-day="4">F</button>
<button type="button" class="dayBtn on" data-day="5">S</button>
<button type="button" class="dayBtn on" data-day="6">S</button>
</div>
<button type="button" class="addBtn" id="addSunSlot">Voeg SunSlot toe</button>
</div>
<!-- Original Form -->
<form id="addForm">
<h3>Voeg tijdslot toe op basis van timepicker</h3>
<input type="time" id="startTime" required>
<input type="time" id="endTime" required>
<div class="daysContainer">
<button type="button" class="dayBtn on" data-day="0">M</button>
<button type="button" class="dayBtn on" data-day="1">T</button>
<button type="button" class="dayBtn on" data-day="2">W</button>
<button type="button" class="dayBtn on" data-day="3">T</button>
<button type="button" class="dayBtn on" data-day="4">F</button>
<button type="button" class="dayBtn on" data-day="5">S</button>
<button type="button" class="dayBtn on" data-day="6">S</button>
</div><br>
<button type="submit" class="addBtn">Add TimeSlot</button>
<h3>---</h3>
</form>
<ul id="slotList"></ul>
<br><br><br>
now send it JSon to ESP8266 ESP32 <br>
230vac wall plug
<br><br>
<script>
const canvas = document.getElementById("dayCircle");
const ctx = canvas.getContext("2d");
const r = canvas.width / 2 - 30;
const cx = canvas.width / 2;
const cy = canvas.height / 2;
const STORAGE_KEY = "dagcirkel_slots_v11"; // Incremented version to handle new data structure
const dayLetters = ["M", "T", "W", "T", "F", "S", "S"];
const dayNames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
const dayNamesNL = ["Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"];
let slots = [];
let sunrise = "07:00", sunset = "19:00";
let currentViewDay = new Date().getDay(); // 0 = Sunday, 1 = Monday, etc.
let currentOffset = 0; // For endless scrolling
function toMin(t){ const[h,m]=t.split(":").map(Number); return h*60+m; }
function toHHMM(mins){ const h=String(Math.floor(mins/60)).padStart(2,"0"); const m=String(mins%60).padStart(2,"0"); return `${h}:${m}`; }
function isInSlot(mins, slot){ const s=toMin(slot.start), e=toMin(slot.end); return s<e ? (mins>=s && mins<e) : (mins>=s || mins<e); }
function toLocalHHMM(d){ return String(d.getHours()).padStart(2,"0")+":"+String(d.getMinutes()).padStart(2,"0"); }
function getISOWeek(date) {
const tmp = new Date(date.getTime());
tmp.setHours(0,0,0,0);
tmp.setDate(tmp.getDate() + 3 - (tmp.getDay() + 6) % 7);
const week1 = new Date(tmp.getFullYear(),0,4);
return 1 + Math.round(((tmp - week1) / 86400000 - 3 + (week1.getDay()+6)%7)/7);
}
function save() {
slots.sort((a, b) => toMin(a.start) - toMin(b.start));
localStorage.setItem(STORAGE_KEY, JSON.stringify(slots));
}
function load(){
const data = localStorage.getItem(STORAGE_KEY);
if(data){
slots = JSON.parse(data);
// Ensure all slots have days array (for backward compatibility)
slots.forEach(slot => {
if (!slot.days) {
slot.days = [true, true, true, true, true, true, true]; // All days enabled by default
}
});
}
else{
slots = [
{id:1,start:"06:00",end:"08:00",enabled:true,days:[true,true,true,true,true,true,true]},
{id:2,start:"12:00",end:"13:30",enabled:true,days:[true,true,true,true,true,true,true]},
{id:3,start:"19:00",end:"22:00",enabled:true,days:[true,true,true,true,true,true,true]}
];
save();
}
}
function isSlotActiveOnDay(slot, dayIndex) {
if (!slot.enabled) return false;
return slot.days[dayIndex];
}
function drawMarkers(){
ctx.textAlign="center"; ctx.textBaseline="middle";
// Draw all tickmarks first (quarters, halves, hours)
for(let mins=0; mins<1440; mins+=15){ // Every 15 minutes (quarter hours)
const angle=mins/1440*2*Math.PI - Math.PI/2;
let inner, outer, lineWidth;
if(mins % 60 === 0){ // Hour marks
inner = r-10;
outer = r+10;
lineWidth = 2;
} else if(mins % 30 === 0){ // Half-hour marks
inner = r-10;
outer = r+4;
lineWidth = 1;
} else { // Quarter-hour marks (15 and 45 minutes)
inner = r-10;
outer = r+0;
lineWidth = .5;
}
const x1=cx+Math.cos(angle)*inner, y1=cy+Math.sin(angle)*inner;
const x2=cx+Math.cos(angle)*outer, y2=cy+Math.sin(angle)*outer;
ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2);
ctx.strokeStyle="#aaa"; ctx.lineWidth = lineWidth; ctx.stroke();
}
// Draw hour labels
for(let hour=0; hour<24; hour++){
const mins=hour*60;
const angle=mins/1440*2*Math.PI - Math.PI/2;
ctx.fillStyle="#ccc"; ctx.font="12px sans-serif";
const labelX=cx+Math.cos(angle)*(r+20), labelY=cy+Math.sin(angle)*(r+20);
ctx.fillText(hour.toString(),labelX,labelY); // No leading zeros
}
}
function drawDayNight(){
const srMin = toMin(sunrise), ssMin = toMin(sunset);
const srAngle = srMin/1440*2*Math.PI - Math.PI/2;
const ssAngle = ssMin/1440*2*Math.PI - Math.PI/2;
ctx.beginPath(); ctx.arc(cx, cy, r-30, 0, 2*Math.PI);
ctx.strokeStyle="#1e3a8a"; ctx.lineWidth=12; ctx.stroke();
ctx.beginPath();
if(srMin < ssMin){ ctx.arc(cx, cy, r-30, srAngle, ssAngle); }
else{ ctx.arc(cx, cy, r-30, srAngle, 1.5*Math.PI); ctx.arc(cx, cy, r-30, -Math.PI/2, ssAngle); }
ctx.strokeStyle="#fbbf24"; ctx.lineWidth=12; ctx.stroke();
}
function drawCircularText(text, radius, centerAngle) {
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(centerAngle);
ctx.fillStyle = "#ccc";
ctx.font = "14px sans-serif";
ctx.textAlign="center";
ctx.textBaseline="middle";
const chars = [...text];
const totalWidth = chars.reduce((w, ch) => w + ctx.measureText(ch).width, 0);
let offset = -(totalWidth / radius) / 2;
chars.forEach(char => {
const w = ctx.measureText(char).width;
const angle = w / radius;
ctx.rotate(offset + angle/2);
ctx.save();
ctx.translate(0, -radius);
ctx.fillText(char, 0, 0);
ctx.restore();
offset = angle/2;
});
ctx.restore();
}
function formatTimeDiff(mins){
const h=Math.floor(mins/60); const m=mins%60;
return (h>0 ? h+"h ":"")+m+"m";
}
function getNextStateChangeForDay(now, dayIndex) {
const minsNow = now.getHours()*60 + now.getMinutes();
let nextChange = null;
slots.forEach(slot=>{
if(!slot.enabled) return;
if(!slot.days[dayIndex]) return;
const s=toMin(slot.start), e=toMin(slot.end);
const sDelta=(s-minsNow+1440)%1440;
const eDelta=(e-minsNow+1440)%1440;
if(nextChange===null || sDelta < nextChange.mins) {
nextChange={mins:sDelta,type:'ON',time:slot.start};
}
if(nextChange===null || eDelta < nextChange.mins) {
nextChange={mins:eDelta,type:'OFF',time:slot.end};
}
});
return nextChange ? `In ${formatTimeDiff(nextChange.mins)} -> ${nextChange.type} at ${nextChange.time}` : "--";
}
function drawCircle(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath(); ctx.arc(cx,cy,r,0,2*Math.PI);
ctx.strokeStyle="#e0e0e0"; ctx.lineWidth=24; ctx.stroke();
drawDayNight();
const now = new Date();
const mins=now.getHours()*60+now.getMinutes();
let stateColor="grey"; let inAnySlot=false;
// Convert currentViewDay to our day index (0=Monday, 6=Sunday)
const viewDayIndex = currentViewDay === 0 ? 6 : currentViewDay - 1;
slots.forEach(slot=>{
if(!isSlotActiveOnDay(slot, viewDayIndex)) return;
const sMin=toMin(slot.start), eMin=toMin(slot.end);
const active=isInSlot(mins,slot);
if(active) inAnySlot=true;
const color=active?"limegreen":"red";
const startAngle=sMin/1440*2*Math.PI - Math.PI/2;
const endAngle=eMin/1440*2*Math.PI - Math.PI/2;
ctx.beginPath();
if(sMin<eMin){ ctx.arc(cx,cy,r,startAngle,endAngle); }
else{ ctx.arc(cx,cy,r,startAngle,1.5*Math.PI); ctx.arc(cx,cy,r,-Math.PI/2,endAngle); }
ctx.strokeStyle=color; ctx.lineWidth=24; ctx.stroke();
});
drawMarkers();
if(slots.some(s=>isSlotActiveOnDay(s, viewDayIndex))){stateColor=inAnySlot?"limegreen":"red";}
const angle=mins/1440*2*Math.PI - Math.PI/2;
const innerR=40;
ctx.beginPath(); ctx.moveTo(cx+Math.cos(angle)*innerR, cy+Math.sin(angle)*innerR);
ctx.lineTo(cx+Math.cos(angle)*r, cy+Math.sin(angle)*r);
ctx.strokeStyle=stateColor; ctx.lineWidth=4; ctx.stroke();
document.getElementById("centerTime").textContent=toHHMM(mins);
const isToday = currentViewDay === new Date().getDay();
const dayName = dayNamesNL[viewDayIndex];
const options = { day:'numeric', month:'long', year:'numeric' };
const dateStr = now.toLocaleDateString('nl-NL',options);
const weekNum = getISOWeek(now);
const nextChangeText = getNextStateChangeForDay(now, viewDayIndex);
document.getElementById("dateInfo").innerHTML =
`${dayName}${isToday ? ' (Vandaag)' : ''}<br>${dateStr}<br>Week ${weekNum}<br>Next: ${nextChangeText}`;
document.getElementById("sunTimes").textContent = `SunRise: ${sunrise} | SunSet: ${sunset}`;
drawCircularText("Evening", r - 20, -Math.PI/4);
drawCircularText("Night", r - 20, Math.PI/4);
drawCircularText("Morning", r - 20, 3*Math.PI/4);
drawCircularText("Afternoon", r - 20, -3*Math.PI/4);
}
function renderDayNavigation() {
const daySlider = document.getElementById("daySlider");
daySlider.innerHTML = "";
const today = new Date().getDay();
// Create 7 day tabs for endless scrolling
for (let i = 0; i < 7; i++) {
// Calculate the actual day index considering the current offset
const dayIndex = (currentOffset + i) % 7;
const dayTab = document.createElement("div");
dayTab.className = `day-tab ${dayIndex === currentViewDay ? 'active' : ''}`;
dayTab.textContent = dayNamesNL[dayIndex === 0 ? 6 : dayIndex - 1]; // Convert to our index system
dayTab.onclick = () => {
currentViewDay = dayIndex;
renderDayNavigation();
drawCircle();
renderList();
};
daySlider.appendChild(dayTab);
}
// Update current day indicator
const isToday = currentViewDay === today;
document.getElementById("currentDayIndicator").textContent =
`Bekijken: ${dayNamesNL[currentViewDay === 0 ? 6 : currentViewDay - 1]}${isToday ? ' (Vandaag)' : ''}`;
// Auto-scroll to center the current day
const activeTab = daySlider.querySelector('.day-tab.active');
if (activeTab) {
const scrollLeft = activeTab.offsetLeft - (daySlider.offsetWidth / 2) + (activeTab.offsetWidth / 2);
daySlider.scrollLeft = scrollLeft;
}
}
function renderList(){
const ul=document.getElementById("slotList");
ul.innerHTML="";
slots.sort((a, b) => toMin(a.start) - toMin(b.start));
// Convert currentViewDay to our day index (0=Monday, 6=Sunday)
const viewDayIndex = currentViewDay === 0 ? 6 : currentViewDay - 1;
slots.forEach(slot=>{
const li=document.createElement("li");
const slotInfo = document.createElement("div");
slotInfo.className = "slotInfo";
const timeSpan=document.createElement("span");
timeSpan.textContent = `${slot.start} -> ${slot.end}`;
timeSpan.style.color = slot.enabled ? "#eee" : "#666";
slotInfo.appendChild(timeSpan);
// Show sun-based indicator if applicable
if (slot.sunBased) {
const sunIndicator = document.createElement("div");
sunIndicator.className = "sun-based-indicator";
const eventName = slot.sunEvent === "sunrise" ? "Zonsopgang" : "Zonsondergang";
const offsetText = slot.sunOffset > 0 ? `${slot.sunOffset} min na` :
slot.sunOffset < 0 ? `${Math.abs(slot.sunOffset)} min voor` : "Precies";
sunIndicator.textContent = `${eventName} ${offsetText} (${slot.sunDuration} min)`;
slotInfo.appendChild(sunIndicator);
}
// Days toggle buttons
const daysContainer = document.createElement("div");
daysContainer.className = "daysContainer";
dayLetters.forEach((letter, index) => {
const dayBtn = document.createElement("button");
dayBtn.className = `dayBtn ${slot.days[index] ? 'on' : 'off'}`;
dayBtn.textContent = letter;
dayBtn.disabled = !slot.enabled;
dayBtn.onclick = () => {
slot.days[index] = !slot.days[index];
dayBtn.className = `dayBtn ${slot.days[index] ? 'on' : 'off'}`;
save();
drawCircle();
};
daysContainer.appendChild(dayBtn);
});
slotInfo.appendChild(daysContainer);
li.appendChild(slotInfo);
const ctr=document.createElement("div"); ctr.className="controls";
const tgl=document.createElement("button");
tgl.textContent="Toggle"; tgl.className="btn toggleBtn";
tgl.onclick=()=>{
slot.enabled=!slot.enabled;
save();
renderList();
drawCircle();
};
const edit=document.createElement("button");
edit.textContent="Edit"; edit.className="btn editBtn";
edit.onclick=()=>{
li.innerHTML = "";
const startInput = document.createElement("input");
startInput.type = "time"; startInput.value = slot.start;
const endInput = document.createElement("input");
endInput.type = "time"; endInput.value = slot.end;
// Days toggle buttons in edit mode
const editDaysContainer = document.createElement("div");
editDaysContainer.className = "daysContainer";
dayLetters.forEach((letter, index) => {
const dayBtn = document.createElement("button");
dayBtn.className = `dayBtn ${slot.days[index] ? 'on' : 'off'}`;
dayBtn.textContent = letter;
dayBtn.onclick = () => {
slot.days[index] = !slot.days[index];
dayBtn.className = `dayBtn ${slot.days[index] ? 'on' : 'off'}`;
};
editDaysContainer.appendChild(dayBtn);
});
const saveBtn = document.createElement("button");
saveBtn.textContent = "Save"; saveBtn.className="btn toggleBtn";
saveBtn.onclick=()=>{
slot.start = startInput.value;
slot.end = endInput.value;
// Clear sun-based properties when manually editing
delete slot.sunBased;
delete slot.sunEvent;
delete slot.sunOffset;
delete slot.sunDuration;
save(); renderList(); drawCircle();
};
li.appendChild(startInput);
li.appendChild(endInput);
li.appendChild(editDaysContainer);
li.appendChild(saveBtn);
};
const del=document.createElement("button");
del.textContent="Delete"; del.className="btn delBtn";
del.onclick=()=>{slots=slots.filter(s=>s.id!==slot.id);save();renderList();drawCircle();};
ctr.appendChild(tgl); ctr.appendChild(edit); ctr.appendChild(del);
li.appendChild(ctr);
ul.appendChild(li);
});
}
// Initialize form day buttons
function initFormDays() {
const formDays = document.querySelectorAll('#addForm .dayBtn, .sun-form .dayBtn');
formDays.forEach(btn => {
btn.classList.add('on');
btn.onclick = () => {
btn.classList.toggle('on');
btn.classList.toggle('off');
};
});
}
// Function to recalculate sun-based slots
function recalculateSunBasedSlots() {
slots.forEach(slot => {
if (slot.sunBased) {
// Calculate start time based on sunrise/sunset and offset
const baseTime = slot.sunEvent === "sunrise" ? sunrise : sunset;
const baseMins = toMin(baseTime);
const startMins = (baseMins + slot.sunOffset + 1440) % 1440;
const endMins = (startMins + slot.sunDuration) % 1440;
slot.start = toHHMM(startMins);
slot.end = toHHMM(endMins);
}
});
save();
drawCircle();
renderList();
}
// Add sunrise/sunset based slot
document.getElementById("addSunSlot").addEventListener("click", () => {
const event = document.getElementById("sunEvent").value;
const offset = parseInt(document.getElementById("sunOffset").value);
const duration = parseInt(document.getElementById("sunDuration").value);
// Get selected days from form
const formDays = document.querySelectorAll('.sun-form .dayBtn');
const days = Array.from(formDays).map(btn => btn.classList.contains('on'));
// Calculate start time based on sunrise/sunset and offset
const baseTime = event === "sunrise" ? sunrise : sunset;
const baseMins = toMin(baseTime);
const startMins = (baseMins + offset + 1440) % 1440;
const endMins = (startMins + duration) % 1440;
const startTime = toHHMM(startMins);
const endTime = toHHMM(endMins);
slots.push({
id: Date.now(),
start: startTime,
end: endTime,
enabled: true,
days: days,
sunBased: true,
sunEvent: event,
sunOffset: offset,
sunDuration: duration
});
save();
renderList();
drawCircle();
});
// Original form submission
document.getElementById("addForm").addEventListener("submit", e=>{
e.preventDefault();
const st=document.getElementById("startTime").value;
const ed=document.getElementById("endTime").value;
if(!st||!ed) return;
// Get selected days from form
const formDays = document.querySelectorAll('#addForm .dayBtn');
const days = Array.from(formDays).map(btn => btn.classList.contains('on'));
slots.push({id:Date.now(),start:st,end:ed,enabled:true,days:days});
save(); renderList(); drawCircle(); e.target.reset();
// Reset form days to all on
formDays.forEach(btn => {
btn.classList.add('on');
btn.classList.remove('off');
});
});
function fetchSunTimes(lat, lon){
fetch(`https://api.sunrise-sunset.org/json?lat=${lat}&lng=${lon}&formatted=0`)
.then(res=>res.json())
.then(data=>{
if(data.status==="OK"){
sunrise = toLocalHHMM(new Date(data.results.sunrise));
sunset = toLocalHHMM(new Date(data.results.sunset));
document.getElementById("geoMsg").textContent = "";
// Recalculate all sun-based slots when sun times change
recalculateSunBasedSlots();
}
})
.catch(()=>{ document.getElementById("geoMsg").textContent="Kon sunrise/sunset niet ophalen."; });
}
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(pos=>{ fetchSunTimes(pos.coords.latitude, pos.coords.longitude); },
()=>{ document.getElementById("geoMsg").textContent="Geen toegang tot locatie -> standaardtijden gebruikt."; });
} else { document.getElementById("geoMsg").textContent="Geolocatie niet ondersteund door deze browser."; }
function updateMoonPhase() {
const phases = ["🌑","🌒","🌓","🌔","🌕","🌖","🌗","🌘"];
const names = ["Nieuwe Maan","Wassende Sikkel","Eerste Kwartier","Wassende Maan",
"Volle Maan","Afnemende Maan","Laatste Kwartier","Afnemende Sikkel"];
const now = new Date();
const lp = new Date(Date.UTC(2000, 0, 6, 18, 14));
const diff = now - lp;
const days = diff / 1000 / 60 / 60 / 24;
const lunations = days / 29.53058867;
const index = Math.floor((lunations - Math.floor(lunations)) * 8 + 0.5) % 8;
document.getElementById("moonIcon").textContent = phases[index];
document.getElementById("moonText").textContent = names[index];
}
// Navigation button handlers for endless scrolling
document.getElementById("prevDay").addEventListener("click", () => {
currentOffset = (currentOffset - 1 + 7) % 7;
currentViewDay = (currentViewDay - 1 + 7) % 7;
renderDayNavigation();
drawCircle();
renderList();
});
document.getElementById("nextDay").addEventListener("click", () => {
currentOffset = (currentOffset + 1) % 7;
currentViewDay = (currentViewDay + 1) % 7;
renderDayNavigation();
drawCircle();
renderList();
});
// Add touch/swipe support
let touchStartX = 0;
let touchEndX = 0;
document.getElementById('daySlider').addEventListener('touchstart', e => {
touchStartX = e.changedTouches[0].screenX;
});
document.getElementById('daySlider').addEventListener('touchend', e => {
touchEndX = e.changedTouches[0].screenX;
handleSwipe();
});
function handleSwipe() {
const swipeThreshold = 50;
const diff = touchStartX - touchEndX;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
// Swipe left - next day
currentOffset = (currentOffset + 1) % 7;
currentViewDay = (currentViewDay + 1) % 7;
} else {
// Swipe right - previous day
currentOffset = (currentOffset - 1 + 7) % 7;
currentViewDay = (currentViewDay - 1 + 7) % 7;
}
renderDayNavigation();
drawCircle();
renderList();
}
}
load();
initFormDays();
renderDayNavigation();
renderList();
drawCircle();
updateMoonPhase();
setInterval(()=>{drawCircle(); updateMoonPhase();},15000);
</script>
</body>
</html>
/* Add your styles here */
// Add your code here