Time Zone Display

add your friends local times to the clock in torn!

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Time Zone Display
// @namespace    http://tampermonkey.net/
// @version      1.8.2
// @description  add your friends local times to the clock in torn!
// @author       Pint-Shot-Riot
// @match        https://www.torn.com/*
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const styleContent = `
        #ups-tz-dropdown {
            position: fixed; top: 55px; right: 10px;
            background: rgba(30, 30, 30, 0.95); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 14px; padding: 12px; z-index: 999999;
            box-shadow: 0 10px 30px rgba(0,0,0,0.6); min-width: 210px; display: none; color: #fff;
            animation: upsSlideIn 0.2s ease-out;
            pointer-events: auto !important;
        }

        @media screen and (max-width: 600px) {
            #ups-tz-dropdown { min-width: 160px; padding: 8px; top: 50px; right: 5px; }
            .ups-tz-item { padding: 6px 4px; }
            .ups-tz-label { font-size: 10px; }
            .ups-tz-time { font-size: 14px; }
            .ups-tz-manage-btn { padding: 8px; margin-top: 6px; font-size: 10px; }
        }

        /* Torn-Style Nameplate Clock */
        .ups-nameplate-time {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            margin-left: 10px;
            padding: 0 8px;
            height: 24px;
            background: linear-gradient(180deg, #444 0%, #222 100%);
            border: 1px solid #000;
            border-radius: 4px;
            color: #fff;
            font-family: 'Orbitron', 'Roboto', monospace;
            font-size: 13px;
            font-weight: 700;
            text-shadow: 0 1px 0 rgba(0,0,0,0.5);
            box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
            vertical-align: middle;
        }

        .ups-quick-add-wrap { display: inline-flex; align-items: center; gap: 5px; margin: 2px 5px; vertical-align: middle; }
        .ups-quick-add {
            display: inline-flex; align-items: center; justify-content: center;
            background: linear-gradient(180deg, #444 0%, #222 100%);
            color: #ccc; border-radius: 4px; padding: 4px 10px; height: 24px;
            font-size: 10px; font-weight: bold; cursor: pointer;
            border: 1px solid #000; text-shadow: 0 -1px 0 rgba(0,0,0,0.5);
            transition: color 0.2s; text-transform: uppercase; font-family: Arial, sans-serif;
        }
        .ups-quick-add:hover { color: #fff; background: linear-gradient(180deg, #555 0%, #333 100%); }

        @keyframes upsSlideIn { from { opacity: 0; transform: translateY(-10px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }

        .ups-tz-header { display: flex; justify-content: flex-end; margin-bottom: -5px; }
        .ups-tz-close { cursor: pointer; color: #666; font-size: 20px; font-weight: bold; padding: 0 5px; user-select: none; }
        .ups-tz-close:hover { color: #fff; }
        .ups-tz-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 6px; border-bottom: 1px solid rgba(255,255,255,0.05); }
        .ups-tz-label { color: #aaa; font-size: 11px; text-transform: uppercase; font-weight: 600; }
        .ups-tz-time { color: #fff; font-size: 16px; font-weight: 700; font-family: monospace; }
        .ups-tz-manage-btn { background: rgba(255,255,255,0.15); color: #fff; text-align: center; padding: 12px; margin-top: 10px; border-radius: 10px; cursor: pointer; font-size: 11px; font-weight: bold; }

        #tz-manager-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #111; z-index: 1000001; padding: 15px; box-sizing: border-box; overflow-y: auto; }
        .ups-card { background: #1a1a1a; border-radius: 18px; padding: 20px; border: 1px solid #2a2a2a; margin-bottom: 20px; }
        .ups-input { background: #000; color: #fff; border: 1px solid #333; padding: 15px; width: 100%; margin-bottom: 15px; border-radius: 10px; box-sizing: border-box; font-size: 16px; }
        .ups-btn-save { width: 100%; padding: 16px; background: #6b8e23; color: #fff; border: none; border-radius: 10px; font-weight: 800; font-size: 14px; cursor: pointer; }

        .ups-table { width: 100%; border-collapse: separate; border-spacing: 0 10px; }
        .ups-table tr { background: #222; }
        .ups-table td { padding: 15px; color: #fff; border-radius: 12px; }

        .del-btn, .move-btn { cursor: pointer !important; border: 1px solid rgba(255,255,255,0.1); }
        .del-btn { color: #ff4444; background: rgba(255, 68, 68, 0.1); padding: 8px 14px; border-radius: 8px; font-size: 11px; margin-left: 5px; }
        .move-btn { color: #fff; background: rgba(255, 255, 255, 0.1); padding: 8px 12px; border-radius: 8px; font-size: 11px; margin-left: 4px; }

        .ups-data-box { background: #000; color: #0f0; font-family: monospace; font-size: 12px; width: 100%; height: 60px; border: 1px solid #333; border-radius: 8px; padding: 10px; box-sizing: border-box; resize: none; margin-bottom: 10px; -webkit-user-select: text; user-select: text; }
        .ups-data-btns { display: flex; gap: 10px; }
        .ups-btn-small { flex: 1; padding: 10px; background: #444; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 11px; font-weight: bold; }
    `;

    function getZones() {
        const raw = localStorage.getItem("upscript_tz_v2");
        return raw ? JSON.parse(raw) : [{label: "Torn", timezone: "UTC"}];
    }

    function saveZones(zones) {
        localStorage.setItem("upscript_tz_v2", JSON.stringify(zones));
        renderList();
        updateIOBox();
    }

    function updateIOBox() {
        const io = document.getElementById("ups-io-box");
        if (io) io.value = btoa(JSON.stringify(getZones()));
    }

    function isValidTimezone(tz) {
        try { if (!tz) return false; Intl.DateTimeFormat(undefined, {timeZone: tz}); return true; } 
        catch (ex) { return false; }
    }

    function findTimezoneSmartly(input) {
        if (!input) return null;
        const clean = input.trim().toLowerCase().replace(/\s+/g, '_');
        const offsetMatch = clean.match(/^(?:utc|gmt)?([+-]\d+)/);
        if (offsetMatch) {
            const hours = parseInt(offsetMatch[1]);
            const inverseSign = hours > 0 ? "-" : "+";
            return `Etc/GMT${inverseSign}${Math.abs(hours)}`;
        }
        const allZones = Intl.supportedValuesOf('timeZone');
        let match = allZones.find(z => z.toLowerCase() === clean || z.toLowerCase().endsWith('/' + clean));
        if (!match) { match = allZones.find(z => z.toLowerCase().includes(clean)); }
        return match || null;
    }

    function formatZoneID(str) {
        if (!str) return "";
        if (str.startsWith('Etc/GMT')) return str;
        if (!str.includes('/')) return str;
        return str.split('/').map(part =>
            part.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join('_')
        ).join('/');
    }

    function renderList() {
        const list = document.getElementById("tz-list");
        if (!list) return;
        list.innerHTML = "";
        getZones().forEach((z, i) => {
            const row = list.insertRow();
            row.innerHTML = `
                <td><span style="font-size:14px; font-weight:700;">${z.label}</span></td>
                <td style="text-align:right">
                    <button class="move-btn up-btn" data-index="${i}">▲</button>
                    <button class="move-btn down-btn" data-index="${i}">▼</button>
                    <button class="del-btn" data-index="${i}">REMOVE</button>
                </td>`;
        });
    }

    function openManager(prefillLabel = "", prefillZone = "") {
        if (document.getElementById("tz-manager-overlay")) return;
        const overlay = document.createElement("div");
        overlay.id = "tz-manager-overlay";
        overlay.innerHTML = `
            <div style="max-width: 450px; margin: auto;">
                <h2 style="color:#fff; text-align:center;">Time Zones</h2>
                <div class="ups-card">
                    <input id="new-label" class="ups-input" placeholder="Name" value="${prefillLabel}">
                    <input id="new-zone" class="ups-input" placeholder="City or Offset" value="${prefillZone}">
                    <button id="add-tz" class="ups-btn-save">ADD TIMEZONE</button>
                </div>
                <table class="ups-table" id="tz-list"></table>
                <div class="ups-card" style="margin-top:20px;">
                    <h3 style="color:#fff; font-size:12px; margin-bottom:10px; text-transform:uppercase;">Import / Export</h3>
                    <textarea id="ups-io-box" class="ups-data-box" readonly></textarea>
                    <div class="ups-data-btns">
                        <button id="ups-copy-btn" class="ups-btn-small">COPY CODE</button>
                        <button id="ups-import-btn" class="ups-btn-small">IMPORT CODE</button>
                    </div>
                </div>
                <button id="close-tz" style="width:100%; color:#666; background:none; border:none; padding:20px; cursor:pointer; font-weight:bold;">CLOSE</button>
            </div>
        `;
        document.body.appendChild(overlay);
        renderList();
        updateIOBox();

        overlay.addEventListener('click', (e) => {
            if (e.target.id === 'close-tz') overlay.remove();
            if (e.target.id === 'ups-copy-btn') {
                const box = document.getElementById("ups-io-box");
                box.select(); document.execCommand('copy');
                e.target.textContent = "COPIED!"; setTimeout(() => e.target.textContent = "COPY CODE", 2000);
            }
            if (e.target.id === 'ups-import-btn') {
                const code = prompt("Paste your export code here:");
                if (code) {
                    try { const decoded = JSON.parse(atob(code)); if (Array.isArray(decoded)) saveZones(decoded); } 
                    catch (err) { alert("Invalid code format."); }
                }
            }
            if (e.target.id === 'add-tz') {
                let l = document.getElementById("new-label").value.trim();
                let zRaw = document.getElementById("new-zone").value.trim();
                if (l && zRaw) {
                    let zFinal = formatZoneID(findTimezoneSmartly(zRaw) || zRaw);
                    if (isValidTimezone(zFinal)) {
                        const zones = getZones();
                        zones.push({label: l, timezone: zFinal});
                        saveZones(zones);
                        document.getElementById("new-label").value = "";
                        document.getElementById("new-zone").value = "";
                        injectProfileFeatures(); 
                    } else { alert("Invalid city or timezone."); }
                }
            }
            const btn = e.target.closest('button');
            if (btn && btn.dataset.index !== undefined) {
                const index = parseInt(btn.dataset.index);
                let zones = getZones();
                if (btn.classList.contains('del-btn')) zones.splice(index, 1);
                else if (btn.classList.contains('up-btn') && index > 0) [zones[index], zones[index - 1]] = [zones[index - 1], zones[index]];
                else if (btn.classList.contains('down-btn') && index < zones.length - 1) [zones[index], zones[index + 1]] = [zones[index + 1], zones[index]];
                saveZones(zones);
                injectProfileFeatures();
            }
        });
    }

    function injectProfileFeatures() {
        if (!window.location.href.includes('profiles.php')) return;

        const nameElem = document.querySelector('.profile-wrapper .name') || document.querySelector('h1');
        if (!nameElem) return;

        const playerName = nameElem.textContent.trim().split('[')[0].trim();
        const zones = getZones();
        const savedZone = zones.find(z => z.label.toLowerCase() === playerName.toLowerCase());

        // 1. NAMEPLATE DISPLAY
        if (savedZone && !document.querySelector('.ups-nameplate-time')) {
            const plateTime = document.createElement('span');
            plateTime.className = 'ups-nameplate-time';
            const updatePlate = () => {
                plateTime.innerText = new Date().toLocaleTimeString("en-GB", { timeZone: savedZone.timezone, hour12: false, hour: '2-digit', minute: '2-digit' });
            };
            updatePlate();
            setInterval(updatePlate, 15000);
            nameElem.appendChild(plateTime);
        }

        // 2. QUICK ADD BUTTON
        if (!document.querySelector('.ups-quick-add-wrap')) {
            const target = document.querySelector('.actions-container') || 
                           document.querySelector('.profile-wrapper .actions') || 
                           document.querySelector('.basic-info');

            if (target) {
                let foundCity = "";
                document.querySelectorAll('.profile-container .info-section ul li, .basic-info li').forEach(li => {
                    if (li.textContent.includes('Location:')) foundCity = li.textContent.replace('Location:', '').trim();
                });

                const wrap = document.createElement('div');
                wrap.className = 'ups-quick-add-wrap';
                const addBtn = document.createElement('button');
                addBtn.className = 'ups-quick-add';
                addBtn.innerText = savedZone ? 'EDIT TZ' : 'ADD TZ';
                addBtn.onclick = (e) => { e.preventDefault(); openManager(playerName, foundCity); };
                wrap.appendChild(addBtn);

                if (target.classList.contains('actions') || target.classList.contains('actions-container')) target.appendChild(wrap);
                else target.insertBefore(wrap, target.firstChild);
            }
        }
    }

    const style = document.createElement("style");
    style.textContent = styleContent;
    document.head.appendChild(style);

    const dropdown = document.createElement("div");
    dropdown.id = "ups-tz-dropdown";
    document.body.appendChild(dropdown);

    dropdown.addEventListener('click', (e) => {
        if (e.target.id === 'ups-tz-close-btn') dropdown.style.display = 'none';
        else if (e.target.id === 'ups-manage-trigger') { dropdown.style.display = 'none'; openManager(); }
    });

    document.addEventListener('mousedown', (e) => {
        const clock = e.target.closest('#server-time') || e.target.closest('[class*="clock"]');
        if (clock) {
            if (dropdown.style.display === "block") dropdown.style.display = "none";
            else { updateDropdownContent(); dropdown.style.display = "block"; }
        } else if (!e.target.closest('#ups-tz-dropdown')) dropdown.style.display = "none";
    });

    function updateDropdownContent() {
        let html = `<div class="ups-tz-header"><span class="ups-tz-close" id="ups-tz-close-btn">&times;</span></div>`;
        getZones().forEach(z => {
            try {
                const time = new Date().toLocaleTimeString("en-GB", { timeZone: z.timezone, hour12: false, hour: '2-digit', minute: '2-digit' });
                html += `<div class="ups-tz-item"><span class="ups-tz-label">${z.label}</span><span class="ups-tz-time">${time}</span></div>`;
            } catch (e) { html += `<div class="ups-tz-item"><span class="ups-tz-label">${z.label}</span><span style="color:#ff4444">ERR</span></div>`; }
        });
        html += `<div class="ups-tz-manage-btn" id="ups-manage-trigger">SETTINGS</div>`;
        dropdown.innerHTML = html;
    }

    const observer = new MutationObserver(injectProfileFeatures);
    observer.observe(document.body, { childList: true, subtree: true });
    injectProfileFeatures();

})();