AtoZ Working time calculator

Working time calculator for AtoZ - English

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);

})();