Safe Fold

Requires a double click to fold when checking is available. Prevents accidental folds.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Safe Fold
// @namespace    torn.poker.safefold
// @version      1.1
// @description  Requires a double click to fold when checking is available. Prevents accidental folds.
// @author       HopesG
// @match        https://www.torn.com/*
// @grant        none
// @run-at       document-end
// @license      All rights reserved
// ==/UserScript==

(function () {
'use strict';

/* ---------- page check ---------- */

function isPokerPage() {
    return document.body && document.body.dataset.page && document.body.dataset.page.includes('holdem');
}

/* ---------- helpers ---------- */

function getText(el) {
    return (el.innerText || el.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
}

function isVisible(el) {
    return !!(el && el.offsetParent !== null);
}

function canCheck() {
    // Look for any visible button that is check or check/fold
    return Array.from(document.querySelectorAll('button')).some(btn => {
        if (!isVisible(btn)) return false;
        const text = getText(btn);
        return text === 'check' || text === 'check / fold' || text === 'call any / check';
    });
}

function findFoldButtons() {
    return Array.from(document.querySelectorAll('button')).filter(btn => {
        return isVisible(btn) && getText(btn) === 'fold';
    });
}

/* ---------- style ---------- */

const STYLE_ID = 'tp-safefold-style';

function injectStyle() {
    if (document.getElementById(STYLE_ID)) return;
    const style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = `
    .tp-fold-armed {
        background: linear-gradient(180deg, #ffb347 0%, #cc6600 100%) !important;
        box-shadow:
            inset 0 1px 0 rgba(255,255,255,.20),
            0 0 0 2px rgba(255,160,50,.50),
            0 0 14px rgba(255,140,0,.45) !important;
        animation: tpFoldPulse .4s ease-in-out infinite alternate !important;
        transition: none !important;
    }

    .tp-fold-armed::after {
        content: "Click again to fold";
        position: absolute;
        bottom: calc(100% + 8px);
        left: 50%;
        transform: translateX(-50%);
        background: rgba(20, 10, 0, 0.92);
        color: #ffcc88;
        font-size: 12px;
        font-weight: 600;
        padding: 5px 10px;
        border-radius: 8px;
        white-space: nowrap;
        pointer-events: none;
        box-shadow: 0 2px 8px rgba(0,0,0,.5);
        border: 1px solid rgba(255,160,50,.35);
        z-index: 99999;
    }

    .tp-fold-armed::before {
        /* override the button shine from Colorful Poker if present */
        opacity: 0 !important;
    }

    @keyframes tpFoldPulse {
        from { filter: brightness(1.00); }
        to   { filter: brightness(1.15); }
    }
    `;
    document.head.appendChild(style);
}

/* ---------- core logic ---------- */

const armed = new WeakMap(); // btn → timeout id
const intercepted = new WeakSet();

function armButton(btn) {
    // Already armed — this is the confirmation click, let it through
    if (armed.has(btn)) {
        clearTimeout(armed.get(btn));
        armed.delete(btn);
        btn.classList.remove('tp-fold-armed');
        // Don't block — the real click fires naturally
        return true;
    }

    // First click — arm it
    btn.classList.add('tp-fold-armed');
    const timer = setTimeout(() => {
        btn.classList.remove('tp-fold-armed');
        armed.delete(btn);
    }, 2000);
    armed.set(btn, timer);
    return false; // block this click
}

function interceptFoldButton(btn) {
    if (intercepted.has(btn)) return;
    intercepted.add(btn);

    btn.addEventListener('click', (e) => {
        // Only intercept when check is available
        if (!canCheck()) return;

        // If already armed, let the click through (confirmation)
        if (armed.has(btn)) {
            clearTimeout(armed.get(btn));
            armed.delete(btn);
            btn.classList.remove('tp-fold-armed');
            return; // allow
        }

        // First click — block and arm
        e.preventDefault();
        e.stopImmediatePropagation();
        armButton(btn);
    }, true); // capture phase so we beat Torn's handler
}

/* ---------- main loop ---------- */

function run() {
    if (!isPokerPage()) return;
    injectStyle();
    findFoldButtons().forEach(interceptFoldButton);
}

setInterval(run, 700);

})();