159 lines
5.8 KiB
JavaScript
Executable File
159 lines
5.8 KiB
JavaScript
Executable File
|
|
function ktc_open_edit_modal(id, ts, isToday) {
|
|
document.getElementById("ktc_edit_id").value=id;
|
|
const origTimeDisplay = document.getElementById("ktc_modal_orig_time");
|
|
if (origTimeDisplay) { origTimeDisplay.innerText = "Original: " + ts; }
|
|
document.getElementById("ktc_edit_modal").style.display="block";
|
|
}
|
|
|
|
// --- Guardrail 1: Punch Lockout ---
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const btn = document.getElementById('ktc-punch-button');
|
|
const timerDiv = document.getElementById('ktc-timer');
|
|
const countdown = document.getElementById('ktc-countdown');
|
|
const form = document.getElementById('ktc-punch-form');
|
|
|
|
function updateTimer() {
|
|
const expiry = localStorage.getItem('ktc_lockout_expiry');
|
|
if (expiry) {
|
|
const now = Date.now();
|
|
const timeLeft = Math.ceil((expiry - now) / 1000);
|
|
|
|
if (timeLeft > 0) {
|
|
btn.disabled = true;
|
|
btn.style.opacity = '0.5';
|
|
btn.style.cursor = 'not-allowed';
|
|
timerDiv.style.display = 'block';
|
|
countdown.innerText = timeLeft;
|
|
setTimeout(updateTimer, 1000); // Check again in 1s
|
|
} else {
|
|
btn.disabled = false;
|
|
btn.style.opacity = '1';
|
|
btn.style.cursor = 'pointer';
|
|
timerDiv.style.display = 'none';
|
|
localStorage.removeItem('ktc_lockout_expiry');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initial check on page load
|
|
updateTimer();
|
|
|
|
form?.addEventListener('submit', function(e) {
|
|
// Set expiry for 60 seconds from now
|
|
const expiryTime = Date.now() + 60000;
|
|
localStorage.setItem('ktc_lockout_expiry', expiryTime);
|
|
// The page will now refresh, and updateTimer() will catch it on reload
|
|
});
|
|
});
|
|
|
|
// --- Guardrail 2: Modal Future-Time Validation ---
|
|
document.getElementById('ktc_proposed_time')?.addEventListener('input', function() {
|
|
const submitBtn = this.form.querySelector('button[type="submit"]');
|
|
// If the input has a 'max' attribute and value exceeds it
|
|
if (this.max && this.value > this.max) {
|
|
this.style.borderColor = 'red';
|
|
this.style.backgroundColor = '#fff0f0';
|
|
submitBtn.disabled = true;
|
|
submitBtn.style.opacity = '0.5';
|
|
} else {
|
|
this.style.borderColor = '#ccc';
|
|
this.style.backgroundColor = '#fff';
|
|
submitBtn.disabled = false;
|
|
submitBtn.style.opacity = '1';
|
|
}
|
|
});
|
|
|
|
function ktc_open_edit_modal(id, ts, isToday) {
|
|
document.getElementById("ktc_edit_id").value = id;
|
|
document.getElementById("ktc_modal_orig_time").innerText = "Original: " + ts;
|
|
|
|
const timeInput = document.getElementById("ktc_proposed_time");
|
|
if (isToday) {
|
|
// Set max to current Chicago time (HH:MM)
|
|
const now = new Date().toLocaleTimeString('en-GB', {
|
|
timeZone: ktcSovereignTZ,
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
timeInput.setAttribute('max', now);
|
|
} else {
|
|
timeInput.removeAttribute('max');
|
|
}
|
|
|
|
document.getElementById("ktc_edit_modal").style.display = "block";
|
|
}
|
|
|
|
// --- Guardrail 3: The Live Heartbeat ---
|
|
function ktc_start_heartbeat() {
|
|
const elapsedElement = document.getElementById('ktc-live-elapsed');
|
|
if (!elapsedElement) return;
|
|
|
|
// Pull the initial seconds we injected via PHP
|
|
let totalSeconds = parseInt(elapsedElement.getAttribute('data-seconds'));
|
|
|
|
setInterval(() => {
|
|
totalSeconds++;
|
|
|
|
// Math to format seconds into "Xh Ym Zs" or "Xh Ym"
|
|
const hours = Math.floor(totalSeconds / 3600);
|
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
const seconds = totalSeconds % 60;
|
|
|
|
// Update the display (Adding seconds for that "Live" feel)
|
|
elapsedElement.innerText = `${hours}h ${minutes}m ${seconds}s`;
|
|
}, 1000);
|
|
}
|
|
|
|
/**
|
|
* Consolidated KTC Heartbeat: Handles Real-Time Clock & Title Teleportation
|
|
*/
|
|
function ktc_init_digital_clock() {
|
|
const clock = document.getElementById('ktc-digital-clock');
|
|
if (!clock) return;
|
|
|
|
// Use a single interval to handle both time updates and UI placement
|
|
setInterval(() => {
|
|
// 1. Generate the time string using our Sovereign Timezone
|
|
const tz = typeof ktcSovereignTZ !== 'undefined' ? ktcSovereignTZ : 'America/Chicago';
|
|
const timeString = new Date().toLocaleTimeString('en-US', {
|
|
timeZone: tz,
|
|
hour12: true,
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
|
|
// 2. Update the display
|
|
clock.innerText = timeString;
|
|
|
|
// 3. One-time Teleport Check
|
|
// We only attempt to move it if it's not already inside the 'ktc-clock-target'
|
|
const pageTitle = document.querySelector('.entry-title, h1.page-title, h1');
|
|
const isAlreadyTeleported = document.getElementById('ktc-clock-target');
|
|
|
|
if (pageTitle && !isAlreadyTeleported) {
|
|
const target = document.createElement('span');
|
|
target.id = 'ktc-clock-target';
|
|
target.style.marginLeft = '20px';
|
|
target.style.fontSize = '0.6em';
|
|
target.style.verticalAlign = 'middle';
|
|
target.style.color = '#888'; // Subtle color for the clock in the title
|
|
|
|
pageTitle.appendChild(target);
|
|
target.appendChild(clock); // Physically moves the element in the DOM
|
|
|
|
// Cleanup the source container if it exists
|
|
const sourceContainer = document.getElementById('ktc-clock-teleport-source');
|
|
if (sourceContainer) sourceContainer.remove();
|
|
}
|
|
}, 1000);
|
|
}
|
|
// Initialize on DOM load
|
|
document.addEventListener('DOMContentLoaded', ktc_init_digital_clock);
|
|
|
|
// Initialize on load
|
|
document.addEventListener('DOMContentLoaded', ktc_start_heartbeat);
|
|
|
|
|