Via Css隐藏规则日志
// ==UserScript==
// @name Via Css隐藏规则日志
// @namespace https://viayoo.com/
// @version 2.3.8
// @license MIT
// @description 检测哪些Css在Via上规则生效,并输出匹配日志。
// @author Copilot & Grok
// @run-at document-start
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
const BUTTON_STORAGE = {
ENABLED: 'floatingButtonEnabled',
LEFT: 'floatingButtonLeft',
TOP: 'floatingButtonTop'
};
const CSS_FILE_PATH = '/via_inject_blocker.css';
const LONG_PRESS_THRESHOLD = 500;
const createStyledElement = (tag, styles, text) => {
const el = document.createElement(tag);
Object.assign(el.style, styles);
if (text) el.textContent = text;
return el;
};
const splitSelectors = cssText => {
const selectors = [];
let current = '';
let bracketDepth = 0;
let parenDepth = 0;
let inQuote = false;
let quoteChar = null;
for (let i = 0; i < cssText.length; i++) {
const char = cssText[i];
if (inQuote) {
current += char;
if (char === quoteChar) inQuote = false;
continue;
}
if (char === '"' || char === "'") {
inQuote = true;
quoteChar = char;
current += char;
continue;
}
if (char === '[') bracketDepth++;
if (char === ']') bracketDepth--;
if (char === '(') parenDepth++;
if (char === ')') parenDepth--;
if (char === '{' && bracketDepth === 0 && parenDepth === 0 && !inQuote) {
if (current.trim()) selectors.push(current.trim());
break;
}
if (char === ',' && bracketDepth === 0 && parenDepth === 0 && !inQuote) {
if (current.trim()) selectors.push(current.trim());
current = '';
} else {
current += char;
}
}
if (current.trim()) selectors.push(current.trim());
return selectors;
};
const checkActiveSelectors = cssText => {
const selectors = splitSelectors(cssText);
const activeRules = [];
selectors.forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
if (elements.length) {
activeRules.push({
selector: selector.trim(),
count: elements.length
});
}
} catch (e) {
console.warn(`[Via CSS Logger] 无效选择器: ${selector}, 错误: ${e.message}`);
}
});
return activeRules;
};
const checkCssFile = async () => {
const cssUrl = `https://${window.location.hostname}${CSS_FILE_PATH}`;
try {
const response = await fetch(cssUrl);
if (!response.ok) throw new Error(`状态码: ${response.status}`);
const rawCss = await response.text();
if (!rawCss.trim()) throw new Error('CSS文件为空');
const checkRules = () => {
const activeRules = checkActiveSelectors(rawCss);
const messageLines = activeRules.length ? [
`🎉 检测完成!共 ${activeRules.length} 条规则生效:`,
'--------------------------------',
...activeRules.map((r, i) =>
`${i + 1}. 规则: ${window.location.hostname}##${r.selector}\n 匹配数: ${r.count}\n`
),
'--------------------------------'
] : ['⚠️ 没有发现生效的CSS规则!'];
const fullMessage = messageLines.join('\n');
console.log(fullMessage);
alert(fullMessage.slice(0, 1000) + (fullMessage.length > 1000 ? '\n\nℹ️ 日志过长,请查看控制台以获取完整信息' : ''));
};
if (document.readyState === 'complete') {
checkRules();
} else {
window.addEventListener('load', checkRules, {
once: true
});
}
} catch (e) {
console.error(`[Via CSS Logger] CSS检查失败:${e.message}`);
alert(`❌ 检查CSS文件失败:${e.message}\nURL: ${cssUrl}`);
}
};
const createFloatingButton = () => {
if (window.self !== window.top) return;
const button = createStyledElement('div', {
position: 'fixed',
zIndex: '10000',
width: '70px',
height: '35px',
backgroundColor: '#2d89ef',
color: 'white',
borderRadius: '5px',
textAlign: 'center',
lineHeight: '35px',
fontSize: '14px',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
cursor: 'pointer',
opacity: '0.9',
transition: 'opacity 0.3s, transform 0.3s',
touchAction: 'none'
}, 'CSS日志');
const defaultLeft = window.innerWidth - 100;
const defaultTop = window.innerHeight - 100;
button.style.left = `${GM_getValue(BUTTON_STORAGE.LEFT, defaultLeft)}px`;
button.style.top = `${GM_getValue(BUTTON_STORAGE.TOP, defaultTop)}px`;
document.body.appendChild(button);
let isDragging = false,
startX, startY, startLeft, startTop, touchStartTime;
button.addEventListener('touchstart', e => {
e.preventDefault();
touchStartTime = Date.now();
isDragging = false;
const touch = e.touches[0];
startX = touch.clientX;
startY = touch.clientY;
startLeft = parseInt(button.style.left) || 0;
startTop = parseInt(button.style.top) || 0;
});
button.addEventListener('touchmove', e => {
e.preventDefault();
const touch = e.touches[0];
const deltaX = touch.clientX - startX;
const deltaY = touch.clientY - startY;
if (Date.now() - touchStartTime >= LONG_PRESS_THRESHOLD) {
isDragging = true;
const newLeft = startLeft + deltaX;
const newTop = startTop + deltaY;
const rect = button.getBoundingClientRect();
button.style.left = `${Math.max(0, Math.min(newLeft, window.innerWidth - rect.width))}px`;
button.style.top = `${Math.max(0, Math.min(newTop, window.innerHeight - rect.height))}px`;
}
});
button.addEventListener('touchend', e => {
e.preventDefault();
const touchDuration = Date.now() - touchStartTime;
if (isDragging && touchDuration >= LONG_PRESS_THRESHOLD) {
const rect = button.getBoundingClientRect();
const newLeft = rect.left + rect.width / 2 < window.innerWidth / 2 ? 0 : window.innerWidth - rect.width;
button.style.left = `${newLeft}px`;
GM_setValue(BUTTON_STORAGE.LEFT, newLeft);
GM_setValue(BUTTON_STORAGE.TOP, parseInt(button.style.top));
} else if (touchDuration < LONG_PRESS_THRESHOLD) {
checkCssFile();
}
});
return button;
};
const ensureButtonExists = () => {
if (!document.querySelector("div[style*='CSS日志']")) {
createFloatingButton();
}
};
const resetButtonPosition = () => {
const defaultLeft = window.innerWidth - 100;
const defaultTop = window.innerHeight - 100;
GM_setValue(BUTTON_STORAGE.LEFT, defaultLeft);
GM_setValue(BUTTON_STORAGE.TOP, defaultTop);
const button = document.querySelector("div[style*='CSS日志']");
if (button) {
button.style.left = `${defaultLeft}px`;
button.style.top = `${defaultTop}px`;
}
alert('✅ 悬浮按钮位置已重置至默认位置!');
};
const init = () => {
const isButtonEnabled = GM_getValue(BUTTON_STORAGE.ENABLED, false);
GM_registerMenuCommand(
isButtonEnabled ? '关闭悬浮按钮' : '开启悬浮按钮',
() => {
GM_setValue(BUTTON_STORAGE.ENABLED, !isButtonEnabled);
alert(`✅ 悬浮按钮已${isButtonEnabled ? '关闭' : '开启'}!`);
location.reload();
}
);
GM_registerMenuCommand('检测CSS隐藏规则', checkCssFile);
GM_registerMenuCommand('重置悬浮按钮位置', resetButtonPosition);
if (isButtonEnabled) {
document.readyState === 'loading' ?
document.addEventListener('DOMContentLoaded', ensureButtonExists) :
ensureButtonExists();
}
};
init();
})();