// ==UserScript== // @name 仿M浏览器元素审查 // @namespace https://viayoo.com/81gzxv // @version 4.7 // @description 利用AI模仿并生成M浏览器的元素审查(感谢M浏览器原生交互灵感),在脚本菜单开启元素审查,专注精准AD规则生成与编辑,支持DOM树浏览、实时编辑(文字/代码/删除/换图/撤销)、存储管理、JS终端等功能。 // @author Via && Gemini // @match *://*/* // @grant GM_addStyle // @grant GM_setClipboard // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @run-at document-end // @license MIT // @icon data:image/png;base64,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 // ==/UserScript== (function() { 'use strict'; let isDebugMode = false; let isLogging = false; let isPicking = false; let isCollapsed = false; let currentTarget = null; let activePreviewStyle = null; let adUpdateTimer = null; let historyStack = []; const host = document.createElement('div'); host.id = 'mb-debug-host'; host.style.cssText = 'position:absolute;top:0;left:0;width:0;height:0;z-index:2147483647;'; document.documentElement.appendChild(host); const shadow = host.attachShadow({ mode: 'open' }); const api = { addStyle: (css) => { const style = document.createElement('style'); style.textContent = css; shadow.appendChild(style); }, setClipboard: (text) => { if (typeof GM_setClipboard !== 'undefined') { GM_setClipboard(text); alert('已复制'); } else { navigator.clipboard.writeText(text).then(() => alert('已复制')).catch(() => { const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); alert('已复制'); }); } }, registerMenu: (name, fn) => { if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand(name, fn); } }, getValue: (key, def) => { if (typeof GM_getValue !== 'undefined') { return GM_getValue(key, def); } const val = localStorage.getItem(key); return val === null ? def : JSON.parse(val); }, setValue: (key, val) => { if (typeof GM_setValue !== 'undefined') { GM_setValue(key, val); } else { localStorage.setItem(key, JSON.stringify(val)); } } }; const formatAndHighlight = (code, lang) => { if (!code) return ""; let fmt = code.replace(/\{/g, ' {\n ').replace(/\}/g, '\n}\n').replace(/;/g, ';\n ').replace(/\n\s*\n/g, '\n'); let level = 0; let result = fmt.split('\n').map(line => { line = line.trim(); if (line.includes('}')) level--; let l = ' '.repeat(Math.max(0, level)) + line; if (line.includes('{')) level++; return l; }).join('\n'); if (result.length > 15000) result = result.substring(0, 15000) + "\n...[此处代码过长已截断]"; const escapeHTML = (str) => str.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":"'"}[m])); if (lang === 'js') { let safeJS = escapeHTML(result); return safeJS .replace(/(".*?"|'.*?'|`.*?`)/g, '$1') .replace(/\b(var|let|const|function|if|else|return|for|while|new|try|catch|async|await|case|switch|break|default)\b/g, '$1'); } else { let safeCSS = escapeHTML(result); return safeCSS .replace(/^(\s*)([^{}\n]+)(\s*\{)/gm, '$1$2$3') .replace(/:(\s*)([^;#}\n]+)(;|\n)/g, ':$1$2$3'); } }; api.addStyle(` :host { --mb-bg: #ffffff; --mb-text: #333; --mb-header-bg: #f1f1f1; --mb-border: #ddd; --mb-item-bg: #fdfdfd; --mb-code-key: #881280; --mb-code-attr: #994500; --mb-code-val: #1a1aa6; --mb-glass-bg: rgba(255, 255, 255, 0.7); --mb-glass-border: rgba(255, 255, 255, 0.5); font-weight: 700 !important; font-size: 14px !important; line-height: 1.4 !important; font-family: sans-serif !important; -webkit-text-size-adjust: 100% !important; } @media (prefers-color-scheme: dark) { :host { --mb-bg: #1e1e1e; --mb-text: #ccc; --mb-header-bg: #2d2d2d; --mb-border: #444; --mb-item-bg: #252525; --mb-code-key: #d197d9; --mb-code-attr: #deb887; --mb-code-val: #7fb4ca; --mb-glass-bg: rgba(45, 45, 45, 0.7); --mb-glass-border: rgba(255, 255, 255, 0.1); } } #mb-debug-panel { position: fixed; left: 0; bottom: 0; width: 100%; height: 50%; background: var(--mb-bg) !important; z-index: 2147483647 !important; display: none; flex-direction: column; box-shadow: 0 -2px 15px rgba(0,0,0,0.3); border-top: 1px solid var(--mb-border); transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1); color: var(--mb-text); } #mb-debug-panel * { text-align: left; box-sizing: border-box; font-size: 14px; } #mb-main-stage { display: flex; flex-wrap: nowrap; width: auto; height: calc(100% - 40px); transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .mb-page { width: 100%; flex: 0 0 100%; height: 100%; display: flex; flex-direction: column; overflow: hidden; } #mb-debug-header { display: flex; align-items: center; background: var(--mb-header-bg); height: 40px; border-bottom: 1px solid var(--mb-border); flex-shrink: 0; padding: 0; } .mb-header-left, .mb-header-right { flex-shrink: 0; display: flex; align-items: center; padding: 0 12px; } .mb-header-middle { flex: 1; display: flex; align-items: center; overflow-x: auto; white-space: nowrap; } .mb-header-middle::-webkit-scrollbar { display: none; } .mb-tool-btn { margin-right: 18px; cursor: pointer; color: var(--mb-text); font-size: 14px; user-select: none; flex-shrink: 0; } .mb-tool-btn.active { color: #ff4757 !important; font-weight: bold; } #mb-btn-close { margin-right: 0; font-size: 18px; } #mb-debug-content, #mb-ad-content, #mb-data-content, #mb-icon-content { flex: 1; overflow: auto; padding: 10px; background: var(--mb-bg) !important; } .ad-rule-item, .data-group-box, .icon-config-card { background: var(--mb-item-bg); border: 1px solid var(--mb-border); border-radius: 6px; padding: 12px; margin-bottom: 15px; } .ad-rule-display, .data-row-display { display: block; word-break: break-all; font-weight: bold; margin-bottom: 10px; font-size: 14px; line-height: 1.4; font-family: monospace; } .hl-domain { color: #ff8c00; } .hl-sep { color: #007bff; } .hl-selector { color: #808080; } .hl-url { color: #ff0000; } .ad-action-bar, .data-action-bar { display: flex; flex-wrap: wrap; gap: 8px; } .ad-mini-btn { padding: 5px 12px; font-size: 12px !important; border: 1px solid var(--mb-border); background: var(--mb-bg); cursor: pointer; border-radius: 4px; color: var(--mb-text); } .icon-config-row { display: flex; align-items: center; justify-content: space-between; margin-top: 8px; font-size: 14px; } .icon-config-row span { font-size: 14px; } .icon-input { width: 80px; padding: 4px; border: 1px solid var(--mb-border); background: var(--mb-bg); color: var(--mb-text); border-radius: 4px; font-size: 14px; } .icon-area { width: 100%; height: 80px; margin-top: 8px; font-family: monospace; font-size: 12px; padding: 6px; border: 1px solid var(--mb-border); background: var(--mb-bg); color: var(--mb-text); resize: none; } .data-item-card { border-top: 1px solid var(--mb-border); padding: 8px 0; margin-top: 8px; } .data-key-label { color: var(--mb-code-attr); font-weight: bold; font-size: 13px; } .node-wrapper { margin-left: 14px; border-left: 1px solid var(--mb-border); font-family: monospace; font-size: 13px; } .node-row { padding: 2px 4px; cursor: pointer; white-space: pre-wrap; word-break: break-all; display: flex; color: var(--mb-text); font-size: 13px; } .node-row.selected { background: rgba(30, 144, 255, 0.2); outline: 1px solid #1e90ff; } .node-row span { font-size: 13px; } .toggle-btn { width: 18px; flex-shrink: 0; text-align: center; font-size: 10px; color: #999; cursor: pointer; } #mb-debug-trigger { position: fixed; right: 16px; width: 32px; height: 32px; background: var(--mb-glass-bg); backdrop-filter: blur(15px) saturate(160%); -webkit-backdrop-filter: blur(15px) saturate(160%); border-radius: 14px; border: 1.5px solid var(--mb-glass-border); box-shadow: 0 6px 16px rgba(0,0,0,0.12), inset 0 0 2px rgba(255,255,255,0.8); cursor: pointer; z-index: 2147483646; display: none; align-items: center; justify-content: center; transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); user-select: none; -webkit-tap-highlight-color: transparent; } #mb-debug-trigger svg { filter: drop-shadow(0 1px 1.5px rgba(0,0,0,0.15)); } #mb-js-content {height: 100%;display: flex;flex-direction: column; padding: 10px; box-sizing: border-box;background: var(--mb-bg) !important;} #mb-js-log {flex: 1; margin-top: 10px;overflow-y: auto !important;font-family: monospace;font-size: 11px;border-top: 1px solid var(--mb-border);padding-top: 5px;-webkit-overflow-scrolling: touch;} #mb-js-input {flex-shrink: 0; height: 100px; background: var(--mb-bg); color: var(--mb-text); border: 1px solid var(--mb-border); padding: 8px; font-size: 13px;} .log-item { border-bottom: 0.5px solid var(--mb-border); padding: 4px 0; white-space: pre-wrap; font-size: 11px;} .log-warn { color: #f1c40f; } .log-error { color: #ff4757; background: rgba(255, 71, 87, 0.05); } .log-result { color: #2ecc71; } #mb-debug-content { display: flex; flex-direction: column; height: 100%; padding: 0 !important; margin: 0; } #mb-dom-tree { flex: 1; overflow-y: auto; padding: 10px; } #mb-node-actions { display: none; gap: 8px; padding: 10px; background: var(--mb-header-bg); border-top: 1px solid var(--mb-border); flex-wrap: wrap; flex-shrink: 0; } .edit-btn { padding: 6px 12px; border-radius: 4px; border: 1px solid var(--mb-border); background: var(--mb-bg); color: var(--mb-text); cursor: pointer; font-size: 12px; font-weight: bold; } .edit-btn.active { background: #ff4757; color: #fff; border-color: #ff4757; } .html-edit-area { background: #1e1e1e !important; color: #fab1a0 !important; width: calc(100% - 20px); min-height: 80px; outline: none; padding: 8px; margin: 5px 0 5px 18px; font-family: monospace; font-size: 12px; display: none; white-space: pre-wrap; word-break: break-all; border: 1px dashed #ff4757; position: relative; z-index: 10; } @media (min-width: 768px) { #mb-js-content { flex-direction: row; gap: 12px; } #mb-js-input { flex: 1; height: auto !important; } #mb-js-log { flex: 1; margin-top: 0; border-top: none; border-left: 1px solid var(--mb-border); padding-left: 10px; } #mb-ad-content, #mb-data-content, #mb-icon-content { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; align-content: start; } } `); const globalStyle = document.createElement('style'); globalStyle.textContent = ` .mb-inspect-hl { outline: 2px dashed #ff4757 !important; outline-offset: 2px !important; background: rgba(255, 71, 87, 0.1) !important; } body.mb-picking-mode { cursor: crosshair !important; } body.mb-picking-mode a, body.mb-picking-mode button, body.mb-picking-mode [onclick], body.mb-picking-mode input { cursor: crosshair !important; pointer-events: auto !important; } `; document.head.appendChild(globalStyle); const panel = document.createElement('div'); panel.id = 'mb-debug-panel'; panel.innerHTML = `
🎯选取
▼收起 恢复 AD规则 JS代码 🛡️数据 🧩图标 H(复制)
⬅ 返回
代码查看
复制全部
`; shadow.appendChild(panel); const trigger = document.createElement('div'); trigger.id = 'mb-debug-trigger'; shadow.appendChild(trigger); const domContent = shadow.getElementById('mb-debug-content'); const adContent = shadow.getElementById('mb-ad-content'); const dataContent = shadow.getElementById('mb-data-content'); const iconContent = shadow.getElementById('mb-icon-content'); const stage = shadow.getElementById('mb-main-stage'); const btnPick = shadow.getElementById('mb-btn-pick'); const btnFold = shadow.getElementById('mb-btn-fold'); const jsLog = shadow.getElementById('mb-js-log'); const jsInput = shadow.getElementById('mb-js-input'); const btnLogSwitch = shadow.getElementById('btn-log-switch'); function addLog(args, type = '') { const isManualRun = (type === 'log-result' || type === 'log-error'); if (!isManualRun && !isLogging && !type.startsWith('script')) return; if (jsLog.childNodes.length >= 300) jsLog.lastChild.remove(); const div = document.createElement('div'); div.className = `log-item ${type}`; div.style.cssText = "word-break:break-all;line-height:1.4;margin-bottom:2px;font-size:12px;"; const time = `[${new Date().toLocaleTimeString(undefined, {hour12: false})}] `; const ArrayArgs = Array.isArray(args) ? args : [args]; if (type === 'log-table' || (ArrayArgs.length === 1 && ArrayArgs[0] && typeof ArrayArgs[0] === 'object' && !ArrayArgs[0].nodeType)) { try { const data = ArrayArgs[0]; const table = document.createElement('table'); table.style.cssText = "border-collapse:collapse;width:100%;margin:5px 0;border:1px solid var(--mb-border);font-size:10px;"; const isArr = Array.isArray(data); const keys = isArr ? (data[0] && typeof data[0] === 'object' ? Object.keys(data[0]) : ['Value']) : Object.keys(data); const head = table.insertRow(); ['(idx)', ...keys].forEach(k => { const th = document.createElement('th'); th.style.cssText = "border:1px solid var(--mb-border);padding:2px;background:var(--mb-header-bg);"; th.innerText = k; head.appendChild(th); }); const rows = isArr ? data : [data]; rows.forEach((row, i) => { const tr = table.insertRow(); tr.insertCell().innerText = i; keys.forEach(k => { const td = tr.insertCell(); td.style.border = "1px solid var(--mb-border)"; const val = (isArr && typeof row !== 'object') ? row : row[k]; td.innerText = val !== undefined ? String(val) : ''; }); }); div.innerText = time; div.appendChild(table); } catch (e) { div.innerText = time + "[Table解析失败] " + String(ArrayArgs[0]); } } else if (typeof ArrayArgs[0] === 'string' && ArrayArgs[0].includes('%c')) { div.innerText = time; let parts = ArrayArgs[0].split('%c'), styles = ArrayArgs.slice(1); if (parts[0]) div.appendChild(document.createTextNode(parts[0])); for (let i = 1; i < parts.length; i++) { const span = document.createElement('span'); span.innerText = parts[i]; if (styles[i-1]) span.style.cssText = styles[i-1]; div.appendChild(span); } } else { const out = ArrayArgs.map(arg => { if (arg === null) return 'null'; if (typeof arg === 'object') { try { return JSON.stringify(arg, (k,v) => typeof v === 'bigint' ? v.toString() : v, 2); } catch(e) { return Object.prototype.toString.call(arg); } } return String(arg); }).join(' '); div.innerText = time + (out.length > 5000 ? out.substring(0, 5000) + "..." : out); } jsLog.prepend(div); } (function initLogHook() { const levels = { log:'', error:'log-error', warn:'log-warn', info:'log-result', table:'log-table' }; for (const key in levels) { const original = console[key]; console[key] = function(...args) { original.apply(console, args); const isStackIdx = new Error().stack?.includes('eval') || new Error().stack?.includes('anonymous'); if (isLogging || isStackIdx || key === 'error') { addLog(args, levels[key]); } }; } })(); function updateLogBtnUI() { if (isLogging) { btnLogSwitch.style.background = '#f1c40f'; btnLogSwitch.style.color = '#000'; btnLogSwitch.innerText = '停止监听'; } else { btnLogSwitch.style.background = 'var(--mb-bg)'; btnLogSwitch.style.color = 'var(--mb-text)'; btnLogSwitch.innerText = '监听网页日志'; } } btnLogSwitch.onclick = () => { isLogging = !isLogging; updateLogBtnUI(); isLogging ? addLog("已开启网页日志监听", "log-result") : addLog("已停止网页日志监听", "log-warn"); }; function switchToPage(index) { stage.style.transform = `translateX(-${index * 100}%)`; const btnBack = shadow.getElementById('mb-btn-back'); if (btnBack) btnBack.style.display = (index === 0 || index === 5) ? 'none' : 'inline'; } function updateFoldState() { if (isCollapsed) { panel.style.height = '40px'; btnFold.innerText = '▲展开'; } else { const adaptiveHeight = window.innerHeight < 600 ? '40%' : '50%'; panel.style.height = adaptiveHeight; document.body.style.paddingBottom = adaptiveHeight.replace('%', 'vh'); btnFold.innerText = '▼收起'; } } function startPicking() { isPicking = true; btnPick.classList.add('active'); document.body.classList.add('mb-picking-mode'); window.onbeforeunload = () => { if (isPicking) return "正在审查元素"; }; } function stopPicking() { isPicking = false; btnPick.classList.remove('active'); document.body.classList.remove('mb-picking-mode'); window.onbeforeunload = null; } function togglePanel(show) { isDebugMode = show; panel.style.display = show ? 'flex' : 'none'; if (show) { document.body.style.paddingBottom = '50vh'; isCollapsed = false; updateFoldState(); startPicking(); } else { document.body.style.removeProperty('padding-bottom'); stopPicking(); if (currentTarget) currentTarget.classList.remove('mb-inspect-hl'); } } function highlightAdRule(rule) { const match = rule.match(/^(.*?)(###?)(.*)$/); if (!match) return `${rule}`; let rest = match[3].replace(/("(.*?)")/g, '"$2"'); return `${match[1]}${match[2]}${rest}`; } /* 参考了[轻量规则](https://raw.githubusercontent.com/damengzhu/abpmerge/main/abpmerge.txt) (Abpmerge) [混合规则](https://raw.githubusercontent.com/lingeringsound/adblock_auto/main/Rules/adblock_auto_lite.txt) !! 感谢 !! */ function generateSmartRules(el) { const domain = window.location.hostname; const tagName = el.tagName.toLowerCase(); let rules = []; const isInvalid = (str) => !str || /^[:\d]/.test(str) || str.includes(':') || str.includes('(') || str.includes(')'); const adKeywords = /(?:(?:^|[-_ \b])(?:ad|popup|modal|gg|google|float|fixed|sticky|overlay|iframe|script)(?:$|[-_ \b]))|(?:ads|adv|banner|sponsor|推广|广告|棋牌|葡京|威尼斯|太阳城|新葡京|约炮|直播|成人|抖阴|黄播|博彩|体育|下注|开奖|娱乐城|美高梅|金沙|银河|皇冠|开元|利记|沙巴|亚星|迷情|春药)/i; const getAttr = (node, name) => node.getAttribute(name) || ""; const hasAttr = (node, name) => node.hasAttribute(name); const getBestSubSelector = (node) => { if (node.id && !isInvalid(node.id)) return '#' + node.id; const classes = Array.from(node.classList).filter(c => !isInvalid(c) && c !== 'mb-inspect-hl' && !/\d{5,}/.test(c)); if (classes.length > 0) return node.tagName.toLowerCase() + '.' + classes[0]; const style = getAttr(node, 'style'); if (style) { const parts = style.split(';').map(s => s.trim()).filter(s => s.includes(':') && !s.includes('url')); if (parts.length > 0) return node.tagName.toLowerCase() + '[style*="' + parts[0].split(':')[0] + '"]'; } return node.tagName.toLowerCase(); }; if (el.id && !isInvalid(el.id)) { rules.push(`${domain}###${el.id}`); if (adKeywords.test(el.id)) rules.push(`###${el.id}`); } const classList = Array.from(el.classList).filter(c => !/\d{5,}/.test(c) && c.length < 35 && c !== 'mb-inspect-hl' && !isInvalid(c) ); if (classList.length > 0) { classList.forEach(c => { rules.push(`${domain}##.${c}`); if (adKeywords.test(c)) { rules.push(`##.${c}`); rules.push(`${domain}##${tagName}.${c}`); } }); if (classList.length >= 2) { const pair = classList.slice(0, 2).join('.'); rules.push(`${domain}##.${pair}`); if (adKeywords.test(pair)) rules.push(`##.${pair}`); } } if (el.previousElementSibling) { const prev = el.previousElementSibling; const prevS = getBestSubSelector(prev); const currS = getBestSubSelector(el); if (prevS && currS) rules.push(`${domain}##${prevS} + ${currS}`); } if (el.parentElement && el.parentElement.id && el.parentElement.tagName !== 'BODY') { const currS = getBestSubSelector(el); rules.push(`${domain}###${el.parentElement.id} > ${currS}`); } let attrRules = []; let sizeBundle = ""; for (let attr of el.attributes) { let val = attr.value; if (!val || ['id', 'class'].includes(attr.name)) continue; if (['srcid', 'tpl', 'data-module'].includes(attr.name)) { rules.push(`${domain}##${tagName}[${attr.name}="${val}"]`); } if (['width', 'height'].includes(attr.name)) { sizeBundle += `[${attr.name}="${val}"]`; continue; } if (attr.name === 'style') { const isFixed = /fixed|sticky|absolute/.test(val); const isHighZ = /z-index\s*:\s*(99\d+|2147483647)/.test(val); if (isFixed || isHighZ) { if (isFixed) rules.push(`${domain}##${tagName}[style*="fixed"]`); if (isHighZ) rules.push(`${domain}##${tagName}[style*="z-index"]`); } continue; } if (val.startsWith('data:')) { const b64 = val.match(/^data:[^;]+;base64,[A-Za-z0-9+/=]{20,50}/); if (b64) attrRules.push(`${tagName}[${attr.name}^="${b64[0]}"]`); continue; } if (attr.name.startsWith('data-') || ['src', 'href', 'title', 'alt', 'ref', 'rel', 'onclick', 'aria-label'].includes(attr.name)) { if (adKeywords.test(attr.name) || adKeywords.test(val)) { rules.push(`${domain}##${tagName}[${attr.name}]`); if (val.length > 0 && val.length < 50) { const subVal = val.split('-')[0].split(' ')[0].substring(0, 20); rules.push(`${domain}##${tagName}[${attr.name}*="${subVal}"]`); } } if (/^(https?:|)\/\//.test(val)) { const m = val.match(/^((?:https?:|)\/\/[^\/]+\/)/); if (m) attrRules.push(`${tagName}[${attr.name}^="${m[1]}"]`); } else if (val.length > 0 && val.length < 100) { attrRules.push(`${tagName}[${attr.name}*="${val}"]`); } } } if (tagName === 'a' || (tagName === 'img' && el.closest('a'))) { const anchor = tagName === 'a' ? el : el.closest('a'); const img = anchor.querySelector('img'); const aLabel = getAttr(anchor, 'aria-label') || getAttr(anchor, 'title'); const imgAlt = img ? (getAttr(img, 'alt') || getAttr(img, 'aria-label')) : ""; const keyLabel = (aLabel || imgAlt || "").split('-')[0].substring(0, 10); const aOnclick = hasAttr(anchor, 'onclick'); const aRef = getAttr(anchor, 'ref') || getAttr(anchor, 'rel'); if (aOnclick || adKeywords.test(aRef)) { let base = aOnclick ? `a[onclick]` : `a[ref*="sponsored"]`; if (keyLabel) { rules.push(`${domain}##${base}[aria-label*="${keyLabel}"]`); if (img) rules.push(`${domain}##${base} > img[alt*="${keyLabel}"]`); if (img) rules.push(`${domain}##a[href] img[alt*="${keyLabel}"]`); } if (img && hasAttr(img, 'data-src')) rules.push(`${domain}##${base} img[data-src]`); } } if (sizeBundle) rules.push(`${domain}##${tagName}${sizeBundle}`); attrRules.forEach(r => rules.push(`${domain}##${r}`)); const adTags = ['iframe', 'embed', 'ins', 'object']; if (adTags.includes(tagName)) rules.push(`${domain}##${tagName}`); rules = [...new Set(rules)]; rules.sort((a, b) => { const getWeight = (s) => { if (s.includes(' + ')) return 0; const hasDomain = s.includes(domain); if (adKeywords.test(s)) return hasDomain ? 1 : 2; if (s.includes('[onclick]') || s.includes('[srcid') || s.includes('[tpl') || s.includes('[ref*=')) return 2; if (s.includes('###') || s.includes('##.')) return hasDomain ? 3 : 4; if (s.includes('[width')) return 5; if (s.includes(' > ')) return 6; return 7; }; const wa = getWeight(a), wb = getWeight(b); return wa !== wb ? wa - wb : a.length - b.length; }); const genericTags = ['div', 'span', 'p', 'li', 'ul', 'ins', 'section', 'article']; return rules.filter(r => { const selector = r.split(/###?/)[1]; if (selector.includes('[srcid') || selector.includes('[tpl') || r.includes(' + ')) return true; return !genericTags.includes(selector); }); } function renderAdPage() { if (!adContent) return; adContent.innerHTML = ''; if (!currentTarget) { adContent.innerHTML = '
请先在网页上点击选取一个元素...
'; return; } const rules = generateSmartRules(currentTarget); // 优先调用window.via.record // Via广告标记模拟函数 window.viaApiInject = function(rule) { if (!rule) return; let host = location.hostname; let filter = rule.trim(); if (filter.includes('##')) { const parts = filter.split('##'); host = parts[0].trim() || location.host; filter = parts[1].trim(); } if (typeof window.via === "object" && typeof window.via.record === "function") { try { window.via.record(host, filter); window.via.toast(`添加一条自定义规则${host}${filter}`); return; } catch (e) { console.error(e); } } if (!window.via?.cmd) return; let target = filter; const _s = JSON.stringify; JSON.stringify = function(a) { if (target && a && typeof a === 'object') { if (a.action === 102 || a.action === 103) { a.host = host; a.filter = target; return _s.apply(this, [a]); } } return _s.apply(this, arguments); }; via.cmd(517); setTimeout(() => { const el = document.createElement('via-proxy'); el.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none;"; document.body.appendChild(el); el.click(); if (window.__getMarkerFilter) window.__getMarkerFilter(); setTimeout(() => { el.remove(); target = ""; JSON.stringify = _s; if (window.__setMarkerEnabled) window.__setMarkerEnabled(false); }, 300); }, 100); }; if (rules.length === 0) { adContent.innerHTML = '
该元素特征不足,未生成自动规则。
'; return; } rules.forEach(ruleText => { let currentRule = ruleText; const originalRule = ruleText; const item = document.createElement('div'); item.className = 'ad-rule-item'; const updateUI = (isEditing = false) => { const isVia = !!(window.via && window.via.cmd); // 调用X浏览器Api添加广告规则 const isX = !!(window.mbrowser && window.mbrowser.addCustomAdRule); let btnText = isVia ? '添加到Via' : (isX ? '添加到Xbrowser' : '复制'); item.innerHTML = `
${isEditing ? currentRule : highlightAdRule(currentRule)}
${isEditing ? ` ` : ` `}
`; if (isEditing) { const display = item.querySelector('.ad-rule-display'); setTimeout(() => display.focus(), 10); item.querySelector('.btn-save').onclick = () => { currentRule = display.innerText.trim(); updateUI(false); }; item.querySelector('.btn-undo').onclick = () => { currentRule = originalRule; updateUI(false); }; } else { item.querySelector('.btn-copy').onclick = () => { if (isX) window.mbrowser.addCustomAdRule(currentRule); else if (isVia) window.viaApiInject(currentRule); else api.setClipboard(currentRule); }; item.querySelector('.btn-edit').onclick = () => updateUI(true); item.querySelector('.btn-pre').onclick = () => { if (activePreviewStyle) activePreviewStyle.remove(); activePreviewStyle = document.createElement('style'); try { const isIdRule = currentRule.includes('###'); let selector = currentRule.split(/###?/)[1]; if (isIdRule && !selector.startsWith('#')) selector = '#' + selector; activePreviewStyle.innerHTML = `${selector} { display: none !important; }`; document.head.appendChild(activePreviewStyle); } catch (e) { alert("语法错误"); } }; item.querySelector('.btn-res').onclick = () => { if (activePreviewStyle) activePreviewStyle.remove(); }; } }; updateUI(); adContent.appendChild(item); }); } function renderDataPage() { dataContent.innerHTML = ''; const getPaths = () => { const p = window.location.pathname; const s = p.split('/').filter(Boolean); const r = ['/', p]; let c = ''; s.forEach(seg => { c += '/' + seg; r.push(c); }); return [...new Set(r)]; }; const getDoms = () => { const d = window.location.hostname; const r = [d, '.' + d]; const p = d.split('.'); if (p.length > 2) { const root = p.slice(-2).join('.'); r.push(root, '.' + root, 'www.' + root, '.www.' + root); } return [...new Set(r)]; }; const configs = [ { label: 'Cookies', emoji: '🍪', get: () => document.cookie.split('; ').reduce((acc, c) => { const [k, v] = c.split('='); if (k) acc[k] = decodeURIComponent(v); return acc; }, {}), set: (k, v) => { document.cookie = `${encodeURIComponent(k)}=${encodeURIComponent(v)}; path=/`; }, del: (k) => { const ps = getPaths(); const ds = getDoms(); ps.forEach(p => { ds.forEach(d => { document.cookie = `${encodeURIComponent(k)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${p}; domain=${d}`; document.cookie = `${encodeURIComponent(k)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${p}; domain=${d}; SameSite=None; Secure`; document.cookie = `${encodeURIComponent(k)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${p}`; }); }); } }, { label: 'SessionStorage', emoji: '💬', get: () => ({ ...sessionStorage }), set: (k, v) => sessionStorage.setItem(k, v), del: (k) => sessionStorage.removeItem(k) }, { label: 'LocalStorage', emoji: '🌐', get: () => ({ ...localStorage }), set: (k, v) => localStorage.setItem(k, v), del: (k) => localStorage.removeItem(k) } ]; configs.forEach(conf => { const data = conf.get(); const box = document.createElement('div'); box.className = 'data-group-box'; const keys = Object.keys(data); box.innerHTML = `
${conf.emoji} ${conf.label} (${keys.length})
`; const list = box.querySelector('.data-items-list'); box.onclick = (e) => { if (e.target === box || e.target.parentElement === box) list.style.display = list.style.display === 'none' ? 'block' : 'none'; }; box.querySelector('.btn-copy-all').onclick = (e) => { e.stopPropagation(); api.setClipboard(JSON.stringify(data, null, 2)); }; box.querySelector('.btn-clear-all').onclick = (e) => { e.stopPropagation(); if (confirm('确定清理全部?')) { keys.forEach(k => conf.del(k)); renderDataPage(); } }; keys.forEach(k => { const item = document.createElement('div'); item.className = 'data-item-card'; let currentVal = data[k]; const updateItemUI = (isEditing = false) => { item.innerHTML = `
${k} : ${currentVal}
${isEditing ? `` : ``}
`; if (isEditing) { const vDom = item.querySelector('.data-val-text'); setTimeout(() => vDom.focus(), 10); item.querySelector('.btn-save').onclick = () => { conf.set(k, vDom.innerText.trim()); currentVal = vDom.innerText.trim(); updateItemUI(false); }; item.querySelector('.btn-cancel').onclick = () => updateItemUI(false); } else { item.querySelector('.btn-copy').onclick = () => api.setClipboard(currentVal); item.querySelector('.btn-edit').onclick = () => updateItemUI(true); item.querySelector('.btn-del').onclick = () => { conf.del(k); item.remove(); }; } }; updateItemUI(); list.appendChild(item); }); dataContent.appendChild(box); }); } const DEFAULT_SVG = ` `; function renderIconPage() { iconContent.innerHTML = `
位置与尺寸
大小 (px):
距离底部 (px):
距离右侧 (px):
自定义 SVG 代码
`; shadow.getElementById('btn-icon-save').onclick = () => { api.setValue('mb_icon_size', parseInt(shadow.getElementById('in-icon-size').value)); api.setValue('mb_icon_bottom', parseInt(shadow.getElementById('in-icon-bottom').value)); api.setValue('mb_icon_right', parseInt(shadow.getElementById('in-icon-right').value)); api.setValue('mb_icon_svg', shadow.getElementById('in-icon-svg').value); updateIconStyle(); alert('保存成功'); }; shadow.getElementById('btn-icon-reset').onclick = () => { if(confirm('确定恢复默认图标设置吗?')){ api.setValue('mb_icon_size', 32); api.setValue('mb_icon_bottom', 95); api.setValue('mb_icon_right', 16); api.setValue('mb_icon_svg', DEFAULT_SVG); renderIconPage(); updateIconStyle(); } }; shadow.getElementById('btn-icon-temp-hide').onclick = () => { if (trigger) trigger.style.display = 'none'; alert('图标已临时隐藏,刷新页面即可恢复。'); }; } const clearAllHighlights = () => { const highlighters = shadow.querySelectorAll('[id^="mb-highlighter"]'); highlighters.forEach(el => el.remove()); if (currentTarget) { currentTarget.classList.remove('mb-inspect-hl'); currentTarget.contentEditable = 'false'; currentTarget.style.outline = ''; currentTarget.style.backgroundColor = ''; } }; function updateNodeActions() { const actionsBar = shadow.getElementById('mb-node-actions'); if (!currentTarget || !actionsBar) return; actionsBar.style.display = 'flex'; if (!shadow.getElementById('btn-hide-inspect')) { const hideBtn = document.createElement('button'); hideBtn.className = 'edit-btn'; hideBtn.id = 'btn-hide-inspect'; hideBtn.style.cssText = 'color:#95a5a6; border-color:#95a5a6;'; hideBtn.innerText = '🚫隐藏选取'; actionsBar.appendChild(hideBtn); } if (!shadow.getElementById('btn-undo-node')) { const undoBtn = document.createElement('button'); undoBtn.className = 'edit-btn'; undoBtn.id = 'btn-undo-node'; undoBtn.style.cssText = 'color:#2ecc71; border-color:#2ecc71;'; undoBtn.innerText = '↩️撤销'; actionsBar.appendChild(undoBtn); } const btnHtml = shadow.getElementById('btn-edit-html'); const btnEdit = shadow.getElementById('btn-edit-node'); const btnImg = shadow.getElementById('btn-edit-img'); const btnDel = shadow.getElementById('btn-del-node'); const btnUndo = shadow.getElementById('btn-undo-node'); const btnHide = shadow.getElementById('btn-hide-inspect'); const selectedRow = shadow.querySelector('.node-row.selected'); const editArea = selectedRow ? selectedRow.querySelector('.html-edit-area') : null; const isHtmlEditing = editArea && editArea.style.display === 'block'; btnHtml.innerText = isHtmlEditing ? '✅同步代码' : '🏗️代码模式'; btnHtml.classList.toggle('active', isHtmlEditing); btnEdit.innerText = currentTarget.contentEditable === 'true' ? '✅完成文字' : '📝文字模式'; btnEdit.classList.toggle('active', currentTarget.contentEditable === 'true'); btnUndo.style.display = historyStack.length > 0 ? 'block' : 'none'; const isImg = currentTarget.tagName === 'IMG'; const hasBg = window.getComputedStyle(currentTarget).backgroundImage !== 'none'; btnImg.style.display = (isImg || hasBg) ? 'block' : 'none'; const saveHistory = () => { const parent = currentTarget.parentElement; if (!parent) return; const index = Array.from(parent.children).indexOf(currentTarget); historyStack.push({ parent: parent, index: index, outerHTML: currentTarget.outerHTML }); }; const finishTextEdit = () => { if (currentTarget.contentEditable === 'true') { currentTarget.contentEditable = 'false'; currentTarget.style.outline = ''; currentTarget.style.backgroundColor = ''; currentTarget.onblur = null; currentTarget.onkeydown = null; isCollapsed = false; updateFoldState(); startPicking(); clearAllHighlights(); renderDOM(); highlight(currentTarget); updateNodeActions(); } }; btnHide.onclick = (e) => { e.stopPropagation(); clearAllHighlights(); currentTarget = null; renderDOM(); }; btnHtml.onclick = (e) => { e.stopPropagation(); if (!editArea) return; if (editArea.style.display !== 'block') { saveHistory(); editArea.style.display = 'block'; const contentSpan = selectedRow.querySelector('.node-content'); if (contentSpan) contentSpan.style.display = 'none'; editArea.contentEditable = 'true'; editArea.innerText = currentTarget.innerHTML; setTimeout(() => editArea.focus(), 50); } else { currentTarget.innerHTML = editArea.innerText; clearAllHighlights(); renderDOM(); highlight(currentTarget); } updateNodeActions(); }; btnEdit.onclick = (e) => { e.stopPropagation(); if (currentTarget.contentEditable !== 'true') { saveHistory(); stopPicking(); isCollapsed = true; updateFoldState(); currentTarget.contentEditable = 'true'; currentTarget.style.outline = '2px dashed #ff4757'; currentTarget.focus(); try { const range = document.createRange(); const sel = window.getSelection(); range.selectNodeContents(currentTarget); range.collapse(false); sel.removeAllRanges(); sel.addRange(range); } catch (err) {} currentTarget.onblur = () => finishTextEdit(); currentTarget.onkeydown = (ev) => { if (ev.key === 'Enter' && !ev.shiftKey) { ev.preventDefault(); currentTarget.blur(); } }; } else { finishTextEdit(); } updateNodeActions(); }; btnDel.onclick = (e) => { e.stopPropagation(); if (confirm('确定删除该元素?')) { saveHistory(); clearAllHighlights(); const p = currentTarget.parentElement; const nextTarget = currentTarget.nextElementSibling || currentTarget.previousElementSibling || p; currentTarget.remove(); currentTarget = (nextTarget && nextTarget !== document.documentElement) ? nextTarget : null; renderDOM(); if (currentTarget) highlight(currentTarget); } }; btnImg.onclick = (e) => { e.stopPropagation(); const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = ev => { const file = ev.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (rev) => { saveHistory(); clearAllHighlights(); if (currentTarget.tagName === 'IMG') { currentTarget.src = rev.target.result; } else { currentTarget.style.backgroundImage = `url(${rev.target.result})`; } renderDOM(); highlight(currentTarget); }; reader.readAsDataURL(file); }; input.click(); }; btnUndo.onclick = (e) => { e.stopPropagation(); const last = historyStack.pop(); if (!last || !last.parent) return; if (currentTarget) { currentTarget.contentEditable = 'false'; currentTarget.onblur = null; currentTarget.onkeydown = null; currentTarget.style.outline = ''; } clearAllHighlights(); const temp = document.createElement('div'); temp.innerHTML = last.outerHTML; const restoredNode = temp.firstElementChild; const existingNode = last.parent.children[last.index]; if (existingNode) { existingNode.replaceWith(restoredNode); } else { last.parent.appendChild(restoredNode); } currentTarget = restoredNode; isCollapsed = false; updateFoldState(); startPicking(); renderDOM(); highlight(currentTarget); updateNodeActions(); }; } function updateIconStyle() { if (!trigger) return; const size = api.getValue('mb_icon_size', 32); const bottom = api.getValue('mb_icon_bottom', 95); const right = api.getValue('mb_icon_right', 16); const svgCode = api.getValue('mb_icon_svg', DEFAULT_SVG); trigger.style.width = size + 'px'; trigger.style.height = size + 'px'; trigger.style.bottom = bottom + 'px'; trigger.style.right = right + 'px'; trigger.innerHTML = svgCode; const svgEl = trigger.querySelector('svg'); if (svgEl) { svgEl.style.width = (size * 0.7) + 'px'; svgEl.style.height = (size * 0.7) + 'px'; } } function buildTree(el, isRoot = false) { if (!el) return null; if (el.nodeType === 3) { const text = el.textContent.trim(); if (!text) return null; const textDiv = document.createElement('div'); textDiv.className = 'node-row'; textDiv.style.cssText = "margin-left: 18px; white-space: pre-wrap; cursor: default;"; textDiv.innerText = text.length > 8000 ? text.substring(0, 8000) + "..." : text; return textDiv; } if (el.nodeType !== 1) return null; const wrapper = document.createElement('div'); wrapper.className = 'node-wrapper'; const row = document.createElement('div'); const isSelected = el === currentTarget; row.className = 'node-row' + (isSelected ? ' selected' : ''); const hasChildren = el.childNodes.length > 0; const arrow = document.createElement('span'); arrow.className = 'toggle-btn'; arrow.innerText = (hasChildren && (isRoot || isSelected)) ? '▼' : (hasChildren ? '▶' : ' '); row.appendChild(arrow); let html = `<${el.tagName.toLowerCase()}`; for (let attr of el.attributes) { let val = attr.value; if (attr.name === 'class') { val = val.replace('mb-inspect-hl', '').trim(); if (!val) continue; } html += ` ${attr.name}="${val}"`; } html += `>`; const label = document.createElement('span'); label.className = 'node-content'; label.innerHTML = html; row.appendChild(label); if (isSelected) { const editArea = document.createElement('div'); editArea.className = 'html-edit-area'; editArea.innerText = el.innerHTML; editArea.onclick = (e) => e.stopPropagation(); editArea.onkeydown = (e) => e.stopPropagation(); row.appendChild(editArea); if (el.contentEditable === 'true') { el.style.outline = '2px dashed #ff4757'; el.style.backgroundColor = 'rgba(255,71,87,0.1)'; } } const isInternalScript = el.tagName === 'SCRIPT' && !el.hasAttribute('src') && el.textContent.trim().length > 0; const isInternalStyle = el.tagName === 'STYLE' && !el.hasAttribute('href') && el.textContent.trim().length > 0; if (isInternalScript || isInternalStyle) { const viewBtn = document.createElement('span'); viewBtn.innerText = ' [查看代码]'; viewBtn.style.cssText = "color:#007aff; cursor:pointer; font-weight:bold; margin-left:8px;"; viewBtn.onclick = (e) => { e.stopPropagation(); const display = shadow.getElementById('mb-code-display'); const title = shadow.getElementById('mb-code-title'); const isJS = el.tagName === 'SCRIPT'; title.innerText = isJS ? 'JavaScript 格式化查看' : 'CSS 格式化查看'; display.innerHTML = formatAndHighlight(el.textContent, isJS ? 'js' : 'css'); shadow.getElementById('mb-btn-code-copy').onclick = () => api.setClipboard(el.textContent); switchToPage(5); }; row.appendChild(viewBtn); } wrapper.appendChild(row); const cBox = document.createElement('div'); if (hasChildren && (isRoot || isSelected)) { cBox.style.display = 'block'; Array.from(el.childNodes).forEach(c => { const childNode = buildTree(c, false); if (childNode) cBox.appendChild(childNode); }); } else { cBox.style.display = 'none'; } wrapper.appendChild(cBox); arrow.onclick = (e) => { e.stopPropagation(); if (cBox.style.display === 'none') { if (cBox.innerHTML === '') { Array.from(el.childNodes).forEach(c => { const childNode = buildTree(c, false); if (childNode) cBox.appendChild(childNode); }); } cBox.style.display = 'block'; arrow.innerText = '▼'; } else { cBox.style.display = 'none'; arrow.innerText = '▶'; } }; row.onclick = (e) => { e.stopPropagation(); highlight(el); renderDOM(); }; return wrapper; } function renderDOM() { const treeContainer = shadow.getElementById('mb-dom-tree'); if (!treeContainer) return; treeContainer.innerHTML = ''; if (!currentTarget) { const bar = shadow.getElementById('mb-node-actions'); if (bar) bar.style.display = 'none'; return; } const parent = currentTarget.parentElement || currentTarget; treeContainer.appendChild(buildTree(parent, true)); updateNodeActions(); setTimeout(() => { const selected = treeContainer.querySelector('.node-row.selected'); if (selected) selected.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50); } function highlight(el) { if (currentTarget) currentTarget.classList.remove('mb-inspect-hl'); currentTarget = el; currentTarget.classList.add('mb-inspect-hl'); } function updateTriggerVisibility() { if (trigger) { trigger.style.display = api.getValue('mb_icon_visible', true) ? 'flex' : 'none'; } } function toggleIconVisible() { const currentState = api.getValue('mb_icon_visible', true); api.setValue('mb_icon_visible', !currentState); updateTriggerVisibility(); } function createDebugTrigger() { updateIconStyle(); updateTriggerVisibility(); } btnPick.onclick = (e) => { e.stopPropagation(); isPicking ? stopPicking() : startPicking(); }; btnFold.onclick = (e) => { e.stopPropagation(); isCollapsed = !isCollapsed; updateFoldState(); }; shadow.getElementById('mb-btn-to-js').onclick = () => switchToPage(1); shadow.getElementById('mb-btn-to-ad').onclick = () => { if (!currentTarget) return; renderAdPage(); switchToPage(2); }; shadow.getElementById('mb-btn-to-data').onclick = () => { renderDataPage(); switchToPage(3); }; shadow.getElementById('mb-btn-to-icon').onclick = () => { renderIconPage(); switchToPage(4); }; shadow.getElementById('mb-btn-back').onclick = () => switchToPage(0); shadow.getElementById('mb-btn-code-back').onclick = () => switchToPage(0); shadow.getElementById('mb-btn-close').onclick = () => togglePanel(false); shadow.getElementById('mb-btn-copy-html').onclick = () => { if (currentTarget) {const clone = currentTarget.cloneNode(true);clone.classList.remove('mb-inspect-hl');if (clone.classList.length === 0) clone.removeAttribute('class');api.setClipboard(clone.outerHTML);}}; shadow.getElementById('mb-btn-parent').onclick = () => { if (currentTarget && currentTarget.parentElement) { highlight(currentTarget.parentElement); renderDOM(); } }; shadow.getElementById('mb-btn-restore').onclick = () => { if (activePreviewStyle) { activePreviewStyle.remove(); activePreviewStyle = null; } }; shadow.getElementById('btn-js-clear').onclick = () => { jsLog.innerHTML = ''; }; shadow.getElementById('btn-js-run').onclick = () => { const code = jsInput.value.trim(); if (!code) return; try { const result = window.eval(code); if (result !== undefined) addLog(result, 'log-result'); } catch (e) { addLog(e.stack || e.message, 'log-error');}}; shadow.getElementById('btn-js-copy-all').onclick = () => { const logs = Array.from(jsLog.querySelectorAll('.log-item')); if (logs.length === 0) { alert('没有可复制的日志'); return;} const text = logs.map(el => el.innerText).reverse().join('\n'); api.setClipboard(text);}; api.registerMenu("开启/关闭审查面板", () => togglePanel(!isDebugMode)); api.registerMenu("显示/隐藏悬浮图标", () => toggleIconVisible()); if (document.readyState === 'complete') createDebugTrigger(); else window.addEventListener('load', createDebugTrigger); trigger.onclick = () => togglePanel(!isDebugMode); let startX, startY; const handler = (e) => { if (!isDebugMode || !isPicking || host.contains(e.target)) return; if (e.type === 'mousedown' || e.type === 'touchstart' || e.type === 'pointerdown') { const touch = e.touches ? e.touches[0] : e; startX = touch.clientX; startY = touch.clientY; return; } if (e.type === 'click' || e.type === 'pointerup' || e.type === 'touchend') { const touch = e.changedTouches ? e.changedTouches[0] : e; const diffX = Math.abs(touch.clientX - startX); const diffY = Math.abs(touch.clientY - startY); if (diffX < 10 && diffY < 10) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); highlight(e.target); if (e.target.tagName === 'SCRIPT' || e.target.tagName === 'STYLE') { if (window.mbFormatTimer) clearTimeout(window.mbFormatTimer); window.mbFormatTimer = setTimeout(() => { if (currentTarget === e.target) renderDOM(); }, 600); } renderDOM(); const transform = stage.style.transform; const isAtAdPage = transform.includes('translateX(-200%)'); if (isAtAdPage) { if (adUpdateTimer) clearTimeout(adUpdateTimer); adUpdateTimer = setTimeout(() => { renderAdPage(); }, 500); } if (isCollapsed) { isCollapsed = false; updateFoldState(); } return false; } } }; const events = ['mousedown', 'touchstart', 'pointerdown', 'click', 'pointerup', 'touchend']; events.forEach(type => { window.addEventListener(type, handler, { capture: true, passive: false }); }); })();