Greasy Fork

Greasy Fork is available in English.

eagle tagger | 区分不同浏览器身份,添加不同的 tag

hover 和按键追加 eagle-tags,英文逗号隔开,去重添加,标签配置持久化存储,支持Alt键批量添加+节流防抖

当前为 2025-05-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         eagle tagger | 区分不同浏览器身份,添加不同的 tag
// @namespace    http://leizingyiu.net/
// @version      20250511
// @description  hover 和按键追加 eagle-tags,英文逗号隔开,去重添加,标签配置持久化存储,支持Alt键批量添加+节流防抖
// @author       leizingyiu
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      GNU AGPLv3 

// ==/UserScript==

(function() {
    'use strict';

    const defaultBrowserTags = "browser-A,browser-B,browser-C";
    const defaultTagsDict = {
        a: "tagA",
        b: "tagB",
        c: "tagC"
    };

    function getBrowserTags() {
        let tags = GM_getValue('thisBrowserTags', defaultBrowserTags);
        return tags.replace(/,/g, ',');
    }

    function getTagsDict() {
        let dictStr = GM_getValue('tagsDict', JSON.stringify(defaultTagsDict));
        try {
            let dict = JSON.parse(dictStr);
            return dict;
        } catch (e) {
            console.error('tagsDict 格式错误,已重置默认值');
            GM_setValue('tagsDict', JSON.stringify(defaultTagsDict));
            return defaultTagsDict;
        }
    }

    function addTagsToItem(item, tags) {
        if (!item) return;

        let oriTag = item.getAttribute('eagle-tags') || '';

        let oriTagList = oriTag.split(',')
            .map(tag => tag.trim())
            .filter(tag => tag.length > 0);

        let addTagList = Array.isArray(tags)
            ? tags
            : tags.replace(/,/g, ',')
                .split(',')
                .map(tag => tag.trim())
                .filter(tag => tag.length > 0);

        addTagList.forEach(tag => {
            if (!oriTagList.includes(tag)) {
                oriTagList.push(tag);
            }
        });

        item.setAttribute('eagle-tags', oriTagList.join(', '));
        console.log('已更新 eagle-tags:', item.getAttribute('eagle-tags'));
    }

     function debounce(func, delay) {
        let timer;
        return function(...args) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                func.apply(this, args);
            }, delay);
        };
    }

     function throttle(func, limit) {
        let inThrottle;
        return function(...args) {
            if (!inThrottle) {
                func.apply(this, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    }

    let currentHoverItem = null;

    function addHoverListeners() {
        document.querySelectorAll('img, video').forEach(item => {
            if (item.hasAttribute('data-hover-tagged')) return;
            item.setAttribute('data-hover-tagged', 'true');

            item.addEventListener('mouseenter', debounce(function() {
                currentHoverItem = item;
                addTagsToItem(item, getBrowserTags());
            }, 200));

            item.addEventListener('mouseleave', function() {
                if (currentHoverItem === item) {
                    currentHoverItem = null;
                }
            });
        });
    }

    addHoverListeners();

    const observer = new MutationObserver((mutationsList) => {
        for (let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                addHoverListeners();
            }
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

     window.addEventListener('keydown', throttle(function(e) {
        const key = e.code.replace('Key','').toLowerCase();
        const tagsDict = getTagsDict();

        if (tagsDict[key] && currentHoverItem) {
            addTagsToItem(currentHoverItem, tagsDict[key]);
        }

        if (e.altKey && tagsDict[key]) {
            document.querySelectorAll('img, video').forEach(item => {
                addTagsToItem(item, tagsDict[key]);
            });
        }
    }, 200));

})();