// ==UserScript==
// @name AtoZ Working time calculator
// @namespace https://atoz.amazon.work
// @version 1.0
// @description Working time calculator for AtoZ - English
// @author @celvip
// @match https://atoz.amazon.work/timecard*
// @match https://atoz.amazon.work/schedule*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Helper functions for localStorage
function setValue(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (e) {
console.error('Error saving to localStorage:', e);
}
}
function getValue(key, defaultValue) {
try {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) : defaultValue;
} catch (e) {
console.error('Error reading from localStorage:', e);
return defaultValue;
}
}
let breakTime = getValue('breakTime', 30);
let workTime = getValue('workTime', 8);
const FIXED_MAX_BREAK = 45; // Fixed break time for maximum working time
// Extended translations for different languages and variations
const TRANSLATIONS = {
IN: ['Einstempeln', 'Clock in', 'Punch in', 'In'],
OUT: ['Ausstempeln', 'Clock out', 'Punch out', 'Out'],
MISSING: ['Fehlende Stempelzeit', 'Missing punch', 'Missed Punch', 'Missed punch', '--:--']
};
const COLORS = {
GREEN: {
bg: '#c6efce',
text: '#006100'
},
YELLOW: {
bg: '#ffeb9c',
text: '#9c6500'
},
RED: {
bg: '#ffc7ce',
text: '#9c0006'
}
};
function findPunchTimes() {
let punchInTime = null;
let punchOutTime = null;
let missingPunch = false;
console.log("Searching for punch times...");
// Define search terms for both languages
const searchTerms = {
punchIn: ['Einstempeln', 'Clock In', 'Punch In', 'Time In', 'In'],
punchOut: ['Ausstempeln', 'Clock Out', 'Punch Out', 'Time Out', 'Out'],
missing: ['Fehlende Stempelzeit', 'Missing Punch', 'Missed Punch', '--:--']
};
// Search for all elements containing text
const elements = document.getElementsByTagName('*');
for (const element of elements) {
const text = element.textContent.trim();
// Search for Clock In
if (searchTerms.punchIn.some(term => text.includes(term))) {
const timeMatch = text.match(/\d{1,2}:\d{2}/);
if (timeMatch) {
punchInTime = timeMatch[0].padStart(5, '0');
console.log("Punch in time found:", punchInTime);
}
}
// Search for Missing Punch
if (searchTerms.missing.some(term => text.includes(term))) {
missingPunch = true;
console.log("Missing punch found");
}
// If no missing punch, search for Clock Out
if (!missingPunch && searchTerms.punchOut.some(term => text.includes(term))) {
const timeMatch = text.match(/\d{1,2}:\d{2}/);
if (timeMatch) {
punchOutTime = timeMatch[0].padStart(5, '0');
console.log("Punch out time found:", punchOutTime);
}
}
}
// Alternative search method for more complex cases
if (!punchInTime) {
const timeNodes = document.evaluate(
"//*[contains(text(), ':')]",
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0; i < timeNodes.snapshotLength; i++) {
const node = timeNodes.snapshotItem(i);
const text = node.textContent.trim();
const timeMatch = text.match(/\d{1,2}:\d{2}/);
if (timeMatch) {
const parentText = node.parentElement?.textContent.toLowerCase() || '';
if (searchTerms.punchIn.some(term => parentText.toLowerCase().includes(term.toLowerCase()))) {
punchInTime = timeMatch[0].padStart(5, '0');
console.log("Alternative method - Punch in time found:", punchInTime);
} else if (!missingPunch && searchTerms.punchOut.some(term => parentText.toLowerCase().includes(term.toLowerCase()))) {
punchOutTime = timeMatch[0].padStart(5, '0');
console.log("Alternative method - Punch out time found:", punchOutTime);
}
}
}
}
// If "Missing Punch" was found, reset punch out time
if (missingPunch) {
punchOutTime = null;
}
console.log("Final times - In:", punchInTime, "Out:", punchOutTime, "Missing:", missingPunch);
return { punchInTime, punchOutTime };
}
function getCurrentWorktime(startTime, endTime) {
if (endTime) {
return getTimeDifference(startTime, endTime);
}
const now = new Date();
const nowHours = now.getHours();
const nowMinutes = now.getMinutes();
const [startHours, startMinutes] = startTime.split(':').map(Number);
let hours = nowHours - startHours;
let minutes = nowMinutes - startMinutes;
if (minutes < 0) {
hours--;
minutes += 60;
}
if (hours < 0) {
hours += 24;
}
return hours + (minutes / 60);
}
function getTimeDifference(startTime, endTime) {
const [startHours, startMinutes] = startTime.split(':').map(Number);
const [endHours, endMinutes] = endTime.split(':').map(Number);
let hours = endHours - startHours;
let minutes = endMinutes - startMinutes;
if (minutes < 0) {
hours--;
minutes += 60;
}
if (hours < 0) {
hours += 24;
}
return hours + (minutes / 60);
}
function getBackgroundColor(hours) {
if (hours <= 8.5) return COLORS.GREEN;
if (hours <= 10) return COLORS.YELLOW;
return COLORS.RED;
}
function calculateEndTime(startTime, workHours, breakMinutes) {
const [hours, minutes] = startTime.split(':').map(Number);
let endHours = hours + workHours;
let endMinutes = minutes + breakMinutes;
if (endMinutes >= 60) {
endHours += Math.floor(endMinutes / 60);
endMinutes = endMinutes % 60;
}
if (endHours >= 24) {
endHours -= 24;
}
return `${String(endHours).padStart(2, '0')}:${String(endMinutes).padStart(2, '0')}`;
}
function formatHoursAndMinutes(hours) {
if (isNaN(hours)) return "0:00";
const fullHours = Math.floor(hours);
const minutes = Math.round((hours - fullHours) * 60);
return `${fullHours}h ${minutes.toString().padStart(2, '0')}min`;
}
function updateDisplay(punchInTime = null, punchOutTime = null) {
const display = document.getElementById('timeCalculator');
if (display) {
let content;
const currentTime = new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
if (punchInTime) {
const currentWorktime = getCurrentWorktime(punchInTime, punchOutTime);
const colors = getBackgroundColor(currentWorktime);
const maxEndTime = calculateEndTime(punchInTime, 10, FIXED_MAX_BREAK);
display.style.backgroundColor = colors.bg;
content = `
<div style="margin-bottom: 12px; font-weight: bold; color: ${colors.text}; font-size: 20px; text-align: center;"><u>Working time calculator</u></div>
<div style="margin-bottom: 8px; color: ${colors.text};">Current time: <strong>${currentTime}</strong></div>
<div style="margin-bottom: 8px; color: ${colors.text};">Stamp IN: <strong>${punchInTime}</strong></div>
${punchOutTime ? `<div style="margin-bottom: 8px; color: ${colors.text};">Stamp OUT: <strong>${punchOutTime}</strong></div>` : ''}
<div style="margin-bottom: 8px; color: ${colors.text};">
Working time:
<select id="workTimeSelect" style="margin-left: 5px; background-color: white;">
<option value="6" ${workTime === 6 ? 'selected' : ''}>6 hours</option>
<option value="8" ${workTime === 8 ? 'selected' : ''}>8 hours</option>
<option value="9" ${workTime === 9 ? 'selected' : ''}>9 hours</option>
<option value="10" ${workTime === 10 ? 'selected' : ''}>10 hours</option>
</select>
</div>
<div style="margin-bottom: 8px; color: ${colors.text};">
Break time:
<select id="breakTimeSelect" style="margin-left: 5px; background-color: white;">
<option value="0" ${breakTime === 0 ? 'selected' : ''}>No break</option>
<option value="30" ${breakTime === 30 ? 'selected' : ''}>30 minutes</option>
<option value="45" ${breakTime === 45 ? 'selected' : ''}>45 minutes</option>
<option value="60" ${breakTime === 60 ? 'selected' : ''}>60 minutes</option>
</select>
</div>
${!punchOutTime ? `
<div style="margin-top: 12px; font-weight: bold; color: ${colors.text};">
Planned end of work: <strong>${calculateEndTime(punchInTime, workTime, breakTime)}</strong>
</div>
<div style="margin-top: 4px; font-weight: bold; color: red;">
Latest end of work: <span style="color: red; font-weight: bold; background-color: #ffdddd; padding: 2px 5px; border-radius: 3px;">❗${maxEndTime}❗</span>
</div>
` : ''}
<div style="font-size: 12px; color: ${colors.text}; margin-top: 8px;">
${punchOutTime ? 'Total' : 'Current'} working time: ${formatHoursAndMinutes(currentWorktime)}
</div>
${!punchOutTime ? `
<div style="font-size: 12px; color: ${colors.text}; margin-top: 4px;">
Total planned time: ${workTime}h ${breakTime}min
</div>
<div style="font-size: 12px; color: ${colors.text}; margin-top: 4px;">
Maximum total time: 10h ${FIXED_MAX_BREAK}min
</div>
` : ''}
`;
} else {
display.style.backgroundColor = 'white';
content = `
<div style="margin-bottom: 12px; font-weight: bold; color: #666;">Working time calculator</div>
<div style="margin-bottom: 8px; color: #666;">Current time: <strong>${currentTime}</strong></div>
<div style="margin-bottom: 8px; color: #666;">Stamp IN: <strong>No punch time found</strong></div>
<div style="margin-bottom: 8px; color: #666;">
Working time:
<select id="workTimeSelect" style="margin-left: 5px; background-color: white;">
<option value="6" ${workTime === 6 ? 'selected' : ''}>6 hours</option>
<option value="8" ${workTime === 8 ? 'selected' : ''}>8 hours</option>
<option value="9" ${workTime === 9 ? 'selected' : ''}>9 hours</option>
<option value="10" ${workTime === 10 ? 'selected' : ''}>10 hours</option>
</select>
</div>
<div style="margin-bottom: 8px; color: #666;">
Break time:
<select id="breakTimeSelect" style="margin-left: 5px; background-color: white;">
<option value="30" ${breakTime === 30 ? 'selected' : ''}>30 minutes</option>
<option value="45" ${breakTime === 45 ? 'selected' : ''}>45 minutes</option>
<option value="60" ${breakTime === 60 ? 'selected' : ''}>60 minutes</option>
</select>
</div>
<div style="font-size: 12px; color: #666; margin-top: 8px;">
Waiting for punch time...
</div>
`;
}
display.innerHTML = content;
// Event Listeners
const breakSelect = document.getElementById('breakTimeSelect');
if (breakSelect) {
breakSelect.addEventListener('change', function() {
breakTime = parseInt(this.value);
setValue('breakTime', breakTime);
updateDisplay(punchInTime, punchOutTime);
});
}
const workSelect = document.getElementById('workTimeSelect');
if (workSelect) {
workSelect.addEventListener('change', function() {
workTime = parseInt(this.value);
setValue('workTime', workTime);
updateDisplay(punchInTime, punchOutTime);
});
}
}
}
function addTimeCalculator() {
let display = document.getElementById('timeCalculator');
if (!display) {
display = document.createElement('div');
display.id = 'timeCalculator';
display.style.cssText = `
position: fixed;
bottom: 100px;
right: 10px;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
z-index: 9999;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
min-width: 220px;
transition: background-color 0.3s ease;
background-color: white;
`;
document.body.appendChild(display);
}
const { punchInTime, punchOutTime } = findPunchTimes();
updateDisplay(punchInTime, punchOutTime);
}
// Initial execution
setTimeout(() => {
addTimeCalculator();
setInterval(addTimeCalculator, 5000);
}, 1000);
})();