Change Clicked Link Color

Change color of clicked links in body with color presets and custom hex input

当前为 2024-07-20 提交的版本,查看 最新版本

// ==UserScript==
// @name         Change Clicked Link Color
// @license      MIT
// @namespace    http://tampermonkey.net/
// @author       [email protected]
// @version      0.4.12
// @description  Change color of clicked links in body with color presets and custom hex input
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// ==/UserScript==



(function() {
    'use strict';

    const colorPresets = {
        'Purple': '#800080',
        'Red': '#FF0000',
        'Blue': '#0000FF',
        'Green': '#008000',
        'Orange': '#FFA500',
        'Pink': '#FFC0CB',
        'Brown': '#A52A2A',
        'Gray': '#808080'
    };

    let clickedColor = GM_getValue('clickedLinkColor', '#800080');
    let enabledSites = GM_getValue('enabledSites', []);
    let applyToAllSites = GM_getValue('applyToAllSites', false);

    GM_addStyle(`
        .custom-dialog {
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            font-family: Arial, sans-serif;
            max-width: 400px;
        }
        .custom-dialog h3 {
            margin-top: 0;
            margin-bottom: 15px;
        }
        .custom-dialog select,
        .custom-dialog input[type="text"],
        .custom-dialog textarea {
            width: calc(100% - 16px);
            padding: 8px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .custom-dialog .section {
            margin-bottom: 20px;
            padding-bottom: 20px;
            border-bottom: 1px solid #eee;
        }
        .custom-dialog .section:last-child {
            border-bottom: none;
            margin-bottom: 0;
            padding-bottom: 0;
        }
        .custom-dialog button {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            height: 36px;
            line-height: 20px;
            vertical-align: middle;
        }
        .custom-dialog button[type="submit"] {
            background-color: #4CAF50;
            color: white;
        }
        .custom-dialog #addCurrentDomain {
            background-color: #4CAF50;
            color: white;
            margin-left: 10px;
        }
        .custom-dialog #cancelBtn {
            background-color: #f44336;
            color: white;
        }
        .custom-dialog .button-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .custom-dialog .button-group {
            display: flex;
            gap: 10px;
        }
        .custom-dialog .domain-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 5px;
        }
        .custom-dialog .domain-item button {
            background-color: #f44336;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            padding: 4px 8px;
        }
        .custom-dialog .add-domain-container {
            display: flex;
            align-items: center;
        }
        .custom-dialog .add-domain-container input[type="text"] {
            margin-right: 10px;
            flex-grow: 1;
        }
    `);

    function createColorSelector() {
        const dialog = document.createElement('dialog');
        dialog.className = 'custom-dialog';
        dialog.innerHTML = `
            <form method="dialog">
                <h3>Choose Clicked Link Color</h3>
                <div class="section">
                    <label for="colorPreset">Choose a preset color:</label>
                    <select id="colorPreset">
                        <option value="">-- Choose a preset color --</option>
                        ${Object.entries(colorPresets).map(([name, value]) => 
                            `<option value="${value}">${name} (${value})</option>`
                        ).join('')}
                    </select>
                </div>
                <div class="section">
                    <label for="customColor">Or enter a custom hex color:</label>
                    <input type="text" id="customColor" placeholder="#RRGGBB" pattern="^#[0-9A-Fa-f]{6}$">
                </div>
                <div class="button-container">
                    <button type="submit">Apply</button>
                    <button type="button" id="cancelBtn">&#x2715; Cancel</button>
                </div>
            </form>
        `;
        document.body.appendChild(dialog);

        const colorPreset = dialog.querySelector('#colorPreset');
        const customColor = dialog.querySelector('#customColor');
        const cancelBtn = dialog.querySelector('#cancelBtn');

        colorPreset.value = clickedColor;
        customColor.value = clickedColor;

        colorPreset.addEventListener('change', function() {
            if (this.value) {
                customColor.value = this.value;
            }
        });

        cancelBtn.addEventListener('click', () => dialog.close());

        dialog.addEventListener('close', () => {
            if (dialog.returnValue !== 'cancel') {
                const newColor = customColor.value;
                if (/^#[0-9A-F]{6}$/i.test(newColor)) {
                    clickedColor = newColor;
                    GM_setValue('clickedLinkColor', clickedColor);
                    applyClickedLinkColor();
                } else {
                    alert("Invalid color code. Please use hex format (e.g., #800080).");
                }
            }
        });

        return dialog;
    }

    function createDomainManager() {
        const dialog = document.createElement('dialog');
        dialog.className = 'custom-dialog';
        dialog.innerHTML = `
            <form method="dialog">
                <h3>Manage Enabled Domains</h3>
                <div class="section">
                    <label for="enabledSites">Enable on these sites (one per line):</label>
                    <textarea id="enabledSites" rows="5" placeholder="example.com"></textarea>
                    <div class="add-domain-container">
                        <input type="text" id="currentDomainInput" placeholder="Enter domain">
                        <button type="button" id="addCurrentDomain">Add</button>
                    </div>
                </div>
                <div id="enabledSitesList"></div>
                <div class="section">
                    <label>
                        <input type="checkbox" id="applyToAllSites" ${applyToAllSites ? 'checked' : ''}>
                        Apply to all sites
                    </label>
                </div>
                <div class="button-container">
                    <div class="button-group">
                        <button type="submit">Save</button>
                        <button type="button" id="cancelBtn">&#x2715; Cancel</button>
                    </div>
                </div>
            </form>
        `;
        document.body.appendChild(dialog);

        const enabledSitesTextarea = dialog.querySelector('#enabledSites');
        const enabledSitesList = dialog.querySelector('#enabledSitesList');
        const addCurrentDomainBtn = dialog.querySelector('#addCurrentDomain');
        const currentDomainInput = dialog.querySelector('#currentDomainInput');
        const cancelBtn = dialog.querySelector('#cancelBtn');
        const applyToAllSitesCheckbox = dialog.querySelector('#applyToAllSites');

        function renderEnabledSites() {
            enabledSitesList.innerHTML = '';
            enabledSites.forEach((site, index) => {
                const domainItem = document.createElement('div');
                domainItem.className = 'domain-item';
                domainItem.innerHTML = `
                    <span>${site}</span>
                    <button type="button" data-index="${index}">&#x2715;</button>
                `;
                enabledSitesList.appendChild(domainItem);
            });
        }

        enabledSitesList.addEventListener('click', function(event) {
            if (event.target.tagName === 'BUTTON') {
                const index = event.target.getAttribute('data-index');
                enabledSites.splice(index, 1);
                GM_setValue('enabledSites', enabledSites);
                renderEnabledSites();
            }
        });

        addCurrentDomainBtn.addEventListener('click', () => {
            const currentDomain = currentDomainInput.value.trim();
            if (currentDomain && !enabledSites.includes(currentDomain)) {
                enabledSites.push(currentDomain);
                GM_setValue('enabledSites', enabledSites);
                renderEnabledSites();
                currentDomainInput.value = '';
            }
        });

        cancelBtn.addEventListener('click', () => dialog.close());

        dialog.addEventListener('close', () => {
            if (dialog.returnValue !== 'cancel') {
                applyToAllSites = applyToAllSitesCheckbox.checked;
                GM_setValue('applyToAllSites', applyToAllSites);
                enabledSites = enabledSitesTextarea.value.split('\n').map(site => site.trim()).filter(Boolean);
                GM_setValue('enabledSites', enabledSites);
                applyClickedLinkColor();
            }
        });

        enabledSitesTextarea.value = enabledSites.join('\n');
        renderEnabledSites();

        return dialog;
    }

    function showColorSelector() {
        const dialog = createColorSelector();
        dialog.showModal();
    }

    function showDomainManager() {
        const dialog = createDomainManager();
        dialog.showModal();
    }

    GM_registerMenuCommand("Choose clicked link color", showColorSelector);
    GM_registerMenuCommand("Manage enabled domains", showDomainManager);

    function applyClickedLinkColor() {
        if (applyToAllSites || isEnabledForCurrentSite()) {
            let style = document.getElementById('clickedLinkStyle');
            if (!style) {
                style = document.createElement('style');
                style.id = 'clickedLinkStyle';
                document.head.appendChild(style);
            }
            style.textContent = `
                body:not(header):not(footer) a:visited {
                    color: ${clickedColor} !important;
                }
            `;
        }
    }

    function isEnabledForCurrentSite() {
        const currentHost = window.location.hostname.replace(/^www\./, '');
        return enabledSites.some(site => currentHost.includes(site));
    }

    if (applyToAllSites || isEnabledForCurrentSite()) {
        window.addEventListener('load', applyClickedLinkColor);

        document.body.addEventListener('click', function(e) {
            if (e.target.tagName === 'A' && !e.target.closest('header, footer')) {
                e.target.style.color = clickedColor;
            }
        });
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址