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();

})();