Via Css 检验

用于检验Via的Adblock规则中的Css隐藏规则是否有错误,支持自动运行和菜单操作,动态控制自动运行开关。

// ==UserScript==
// @name         Via Css 检验
// @namespace    https://viayoo.com/
// @version      2.7
// @license      MIT
// @description  用于检验Via的Adblock规则中的Css隐藏规则是否有错误,支持自动运行和菜单操作,动态控制自动运行开关。
// @author       Copilot
// @run-at       document-end
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://cdn.jsdelivr.net/npm/[email protected]/js/lib/beautify-css.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/csstree.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 获取CSS文件URL
    function getCssFileUrl() {
        const currentHost = window.location.hostname;
        return `https://${currentHost}/via_inject_blocker.css`;
    }

    // 使用 js-beautify 格式化CSS内容
    function formatCssWithJsBeautify(rawCss) {
        try {
            return css_beautify(rawCss, {
                indent_size: 2,
                selector_separator_newline: true
            });
        } catch (error) {
            console.error(`CSS格式化失败:${error.message}`);
            return null;
        }
    }

    // 截断错误行以限制字符数
    function truncateErrorLine(errorLine, maxLength = 150) {
        if (errorLine.length > maxLength) {
            return errorLine.substring(0, maxLength) + "..."; // 添加省略号表示截断
        }
        return errorLine;
    }

    // 读取并格式化CSS内容
    async function fetchAndFormatCss() {
        const url = getCssFileUrl();
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`无法获取CSS文件:${response.statusText}`);
            }
            const rawCss = await response.text();
            return formatCssWithJsBeautify(rawCss);
        } catch (error) {
            console.error(`无法获取或格式化CSS文件:${error.message}`);
            return null;
        }
    }

    // 翻译错误信息为中文
    function translateErrorMessage(englishMessage) {
        const translations = {
            "Identifier is expected": "需要标识符",
            "Unexpected end of input": "输入意外结束",
            "Selector is expected": "需要选择器",
            "Invalid character": "无效字符",
            "Unexpected token": "意外的标记",
            '"]" is expected': '需要 "]"',
            '"{" is expected': '需要 "{"',
            'Unclosed block': '未闭合的块',
            'Unclosed string': '未闭合的字符串',
            'Property is expected': '需要属性名',
            'Value is expected': '需要属性值',
            "Percent sign is expected": "需要百分号 (%)",
            'Attribute selector (=, ~=, ^=, $=, *=, |=) is expected': '需要属性选择器运算符(=、~=、^=、$=、*=、|=)',
            'Semicolon is expected': '需要分号 ";"',
            'Number is expected': '需要数字',
            'Colon is expected': '需要冒号 ":"'
        };
        return translations[englishMessage] || `${englishMessage}`;
    }

    // 验证格式化后的CSS内容
    function validateCss(formattedCss, isAutoRun = false) {
        if (!formattedCss) return;

        let hasError = false;
        const errors = [];
        const lines = formattedCss.split('\n'); // 将格式化的CSS按行分割

        try {
            csstree.parse(formattedCss, {
                onParseError(error) {
                    hasError = true;

                    // 获取并截断错误的具体片段
                    const errorLine = lines[error.line - 1] || "无法提取错误行";
                    const truncatedErrorLine = truncateErrorLine(errorLine);

                    // 翻译错误信息
                    const translatedMessage = translateErrorMessage(error.message);

                    const errorMessage = `
CSS 解析错误:
- 位置:第 ${error.line} 行
- 错误信息:${translatedMessage}
- 错误片段:${truncatedErrorLine}
                    `.trim();
                    errors.push(errorMessage);
                }
            });

            if (hasError) {
                if (isAutoRun) {
                    alert(errors.join('\n\n'));
                }
            } else if (!isAutoRun) {
                alert("CSS验证通过:未发现错误。");
            }
        } catch (error) {
            const translatedMessage = translateErrorMessage(error.message);
            if (isAutoRun) {
                console.error(`自动运行时CSS验证失败:${translatedMessage}`);
            } else {
                alert(`CSS验证失败:${translatedMessage}`);
            }
        }
    }

    // 自动运行逻辑
    async function autoRunCssValidation() {
        const formattedCss = await fetchAndFormatCss();
        if (formattedCss) {
            validateCss(formattedCss, true); // 自动运行时传递 true
        }
    }

    // 初始化菜单和自动运行状态
    function initializeScript() {
        const isAutoRunEnabled = GM_getValue("autoRun", true); // 默认开启自动运行

        // 注册菜单项
        GM_registerMenuCommand(isAutoRunEnabled ? "关闭自动运行" : "开启自动运行", () => {
            GM_setValue("autoRun", !isAutoRunEnabled);
            alert(`自动运行已${isAutoRunEnabled ? "关闭" : "开启"}!`);
        });

        GM_registerMenuCommand("验证CSS文件", async () => {
            const formattedCss = await fetchAndFormatCss();
            if (formattedCss) {
                validateCss(formattedCss, false); // 手动运行时传递 false
            }
        });

        // 如果自动运行被启用,则运行自动验证逻辑
        if (isAutoRunEnabled) {
            autoRunCssValidation();
        }
    }

    // 脚本初始化
    initializeScript();

})();