// ==UserScript==
// @name JSON Formatter
// @namespace https://github.com/askmiw
// @author mrzhou@miw.cn
// @description 类名添加miw_前缀 + 样式作用域隔离 + 修复Chromium主题 + 避免普通网页误解析JSON
// @version 1.0.0
// @match *://*/*
// @match file:///*
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @run-at document-end
// @downloadURL https://update.greasyfork.icu/scripts/570175/JSON%20Formatter.user.js
// @updateURL https://update.greasyfork.icu/scripts/570175/JSON%20Formatter.meta.js
// ==/UserScript==
(function() {
'use strict';
// 配置项
let CONFIG = {
AUTO_FORMAT: true,
DARK_MODE: true,
COLLAPSE_LEVEL: Infinity,
SHOW_LINE_NUMBERS: false,
ENABLE_COPY: true,
MIN_JSON_LENGTH: 10,
MAX_RENDER_SIZE: 50000,
SHOW_NODE_COUNT: true
};
// 动态样式管理(所有类名带miw_前缀)
let styleElement = null;
let globalFormatter = null;
let mutationObserver = null;
// ========== 核心修改:所有样式类名添加miw_前缀 ==========
function updateStyles() {
// 移除旧样式
if (styleElement && styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
styleElement = document.createElement('style');
const lineNumberStyles = CONFIG.SHOW_LINE_NUMBERS
? `
.miw_json-formatter-wrapper .miw_line-number {
display: inline-block !important;
min-width: 30px !important;
width: 30px !important;
text-align: right !important;
margin-right: 10px !important;
color: ${CONFIG.DARK_MODE ? '#6a9955' : '#999'} !important;
user-select: none !important;
padding: 0 !important;
}
`
: `
.miw_json-formatter-wrapper .miw_line-number {
display: none !important;
min-width: 0px !important;
width: 0px !important;
margin-right: 0px !important;
padding: 0 !important;
visibility: hidden !important;
opacity: 0 !important;
}
`;
// 主题样式(类名带miw_前缀)
const themeStyles = CONFIG.DARK_MODE
? `
.miw_json-formatter-wrapper {
background-color: #1e1e1e !important;
color: #e0e0e0 !important;
}
.miw_json-formatter-wrapper .miw_json-toolbar {
background: rgba(30, 30, 30, 0.95) !important;
border-color: #444 !important;
}
.miw_json-formatter-wrapper .miw_toolbar-button {
background: #2d2d2d !important;
border-color: #555 !important;
color: #e0e0e0 !important;
}
.miw_json-formatter-wrapper .miw_search-box {
background: #2d2d2d !important;
border-color: #555 !important;
color: #e0e0e0 !important;
}
.miw_json-formatter-wrapper .miw_search-nav-btn {
background: #2d2d2d !important;
border-color: #555 !important;
color: #e0e0e0 !important;
}
.miw_json-formatter-wrapper .miw_json-raw-view {
background: #2d2d2d !important;
border-color: #444 !important;
color: #e0e0e0 !important;
}
`
: `
.miw_json-formatter-wrapper {
background-color: #ffffff !important;
color: #333333 !important;
}
.miw_json-formatter-wrapper .miw_json-toolbar {
background: rgba(255, 255, 255, 0.95) !important;
border-color: #ddd !important;
}
.miw_json-formatter-wrapper .miw_toolbar-button {
background: #f5f5f5 !important;
border-color: #ddd !important;
color: #333 !important;
}
.miw_json-formatter-wrapper .miw_search-box {
background: #fff !important;
border-color: #ddd !important;
color: #333 !important;
}
.miw_json-formatter-wrapper .miw_search-nav-btn {
background: #f5f5f5 !important;
border-color: #ddd !important;
color: #333 !important;
}
.miw_json-formatter-wrapper .miw_json-raw-view {
background: #f9f9f9 !important;
border-color: #ddd !important;
color: #333 !important;
}
`;
// 所有样式类名均添加miw_前缀,且通过后代选择器限定作用域
styleElement.textContent = `
/* 核心容器(带miw_前缀) */
.miw_json-formatter-wrapper {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace !important;
min-height: 100vh !important;
padding: 20px !important;
line-height: 1.4 !important;
box-sizing: border-box !important;
position: relative !important;
margin: 0 !important;
width: 100% !important;
}
${themeStyles}
.miw_json-formatter-wrapper .miw_json-toolbar {
position: fixed !important;
top: 10px !important;
right: 10px !important;
border-radius: 6px !important;
padding: 8px 12px !important;
z-index: 99999 !important;
display: flex !important;
gap: 6px !important;
align-items: center !important;
backdrop-filter: blur(10px) !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2) !important;
flex-wrap: wrap !important;
}
.miw_json-formatter-wrapper .miw_toolbar-button {
padding: 4px 8px !important;
border-radius: 4px !important;
cursor: pointer !important;
font-size: 12px !important;
transition: all 0.2s !important;
}
.miw_json-formatter-wrapper .miw_toolbar-button:hover {
background: ${CONFIG.DARK_MODE ? '#3d3d3d' : '#e8e8e8'} !important;
border-color: #007acc !important;
}
.miw_json-formatter-wrapper .miw_toolbar-button.active {
background: #007acc !important;
color: white !important;
border-color: #007acc !important;
}
/* 搜索区域(带miw_前缀) */
.miw_json-formatter-wrapper .miw_search-container {
display: flex !important;
align-items: center !important;
gap: 4px !important;
}
.miw_json-formatter-wrapper .miw_search-box {
padding: 4px 8px !important;
border-radius: 4px !important;
font-size: 12px !important;
width: 180px !important;
outline: none !important;
}
.miw_json-formatter-wrapper .miw_search-nav-btn {
padding: 4px 6px !important;
border-radius: 4px !important;
cursor: pointer !important;
font-size: 11px !important;
width: 24px !important;
height: 24px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.2s !important;
}
.miw_json-formatter-wrapper .miw_search-nav-btn:hover {
background: ${CONFIG.DARK_MODE ? '#3d3d3d' : '#e8e8e8'} !important;
border-color: #007acc !important;
}
.miw_json-formatter-wrapper .miw_search-count {
font-size: 11px !important;
color: ${CONFIG.DARK_MODE ? '#aaa' : '#666'} !important;
min-width: 40px !important;
text-align: center !important;
}
.miw_json-formatter-wrapper .miw_json-notification {
position: fixed !important;
bottom: 20px !important;
left: 50% !important;
transform: translateX(-50%) !important;
background: #007acc !important;
color: white !important;
padding: 8px 16px !important;
border-radius: 4px !important;
opacity: 0 !important;
transition: opacity 0.3s !important;
pointer-events: none !important;
z-index: 99999 !important;
}
.miw_json-formatter-wrapper .miw_json-notification.show {
opacity: 1 !important;
}
/* JSON布局样式(带miw_前缀) */
.miw_json-formatter-wrapper .miw_json-item {
margin: 2px 0 !important;
}
.miw_json-formatter-wrapper .miw_json-line {
display: flex !important;
margin: 1px 0 !important;
position: relative !important;
align-items: flex-start !important;
}
/* 动态行号样式(带miw_前缀) */
${lineNumberStyles}
.miw_json-formatter-wrapper .miw_line-content {
flex: 1 !important;
width: 100% !important;
}
.miw_json-formatter-wrapper .miw_json-collapsible {
cursor: pointer !important;
position: relative !important;
padding-left: 16px !important;
user-select: none !important;
display: inline-block !important;
}
.miw_json-formatter-wrapper .miw_json-collapsible::before {
content: '▶' !important;
position: absolute !important;
left: 0 !important;
font-size: 10px !important;
transition: transform 0.2s !important;
}
.miw_json-formatter-wrapper .miw_json-collapsible.expanded::before {
content: '▼' !important;
}
/* 节点数样式(带miw_前缀) */
.miw_json-formatter-wrapper .miw_json-node-count {
color: ${CONFIG.DARK_MODE ? '#888' : '#999'} !important;
font-style: italic !important;
margin-left: 5px !important;
font-size: 11px !important;
}
.miw_json-formatter-wrapper .miw_json-children {
margin-left: 20px !important;
border-left: 1px dashed ${CONFIG.DARK_MODE ? '#444' : '#ddd'} !important;
padding-left: 10px !important;
}
.miw_json-formatter-wrapper .miw_json-key {
color: ${CONFIG.DARK_MODE ? '#9cdcfe' : '#881391'} !important;
font-weight: bold !important;
}
.miw_json-formatter-wrapper .miw_json-string {
color: ${CONFIG.DARK_MODE ? '#ce9178' : '#c41a16'} !important;
}
.miw_json-formatter-wrapper .miw_json-number {
color: ${CONFIG.DARK_MODE ? '#b5cea8' : '#1c00cf'} !important;
}
.miw_json-formatter-wrapper .miw_json-boolean {
color: ${CONFIG.DARK_MODE ? '#569cd6' : '#0b22b9'} !important;
}
.miw_json-formatter-wrapper .miw_json-null {
color: ${CONFIG.DARK_MODE ? '#569cd6' : '#0b22b9'} !important;
}
.miw_json-formatter-wrapper .miw_json-punctuation {
color: ${CONFIG.DARK_MODE ? '#d4d4d4' : '#333'} !important;
margin: 0 2px !important;
}
.miw_json-formatter-wrapper .miw_json-ellipsis {
color: ${CONFIG.DARK_MODE ? '#888' : '#999'} !important;
font-style: italic !important;
margin-left: 5px !important;
}
/* 搜索高亮(带miw_前缀) */
.miw_json-formatter-wrapper .miw_search-highlight {
background-color: rgba(255, 255, 0, 0.4) !important;
border-radius: 2px !important;
padding: 1px 0 !important;
}
.miw_json-formatter-wrapper .miw_search-highlight.current {
background-color: rgba(255, 165, 0, 0.6) !important;
color: #000 !important;
font-weight: bold !important;
}
.miw_json-formatter-wrapper .miw_error-container {
background: ${CONFIG.DARK_MODE ? 'rgba(255, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.05)'} !important;
border: 1px solid #ff4444 !important;
border-radius: 6px !important;
padding: 20px !important;
margin: 20px !important;
font-family: monospace !important;
}
.miw_json-formatter-wrapper .miw_error-title {
color: #ff4444 !important;
font-size: 16px !important;
margin-bottom: 10px !important;
font-weight: bold !important;
}
/* 原文显示样式(带miw_前缀) */
.miw_json-formatter-wrapper .miw_json-raw-view {
border-radius: 4px !important;
padding: 20px !important;
margin: 20px 0 !important;
font-family: 'Courier New', monospace !important;
white-space: pre-wrap !important;
word-break: break-all !important;
max-height: calc(100vh - 80px) !important;
overflow-y: auto !important;
font-size: 13px !important;
line-height: 1.5 !important;
}
/* 视图切换容器(带miw_前缀) */
.miw_json-formatter-wrapper .miw_view-container {
width: 100% !important;
height: 100% !important;
}
`;
// 仅在脚本激活时添加样式
if (globalFormatter?.isActive) {
document.head.appendChild(styleElement);
}
}
// JSON提取类(逻辑不变)
class JSONExtractor {
static extractJSON(content) {
if (!content || content.length < CONFIG.MIN_JSON_LENGTH) return null;
try {
const parsed = JSON.parse(content);
if (parsed && (typeof parsed === 'object' || Array.isArray(parsed))) {
return content.trim();
}
} catch (e) {}
const bestMatch = this.findCompleteJson(content);
if (bestMatch) {
try {
const parsed = JSON.parse(bestMatch);
if (parsed && (typeof parsed === 'object' || Array.isArray(parsed))) {
return bestMatch;
}
} catch (e) {}
}
try {
const repaired = this.safeRepair(content);
const parsed = JSON.parse(repaired);
if (parsed && (typeof parsed === 'object' || Array.isArray(parsed))) {
return repaired;
}
} catch (e) {}
return null;
}
static findCompleteJson(content) {
const jsonStartIndices = [];
for (let i = 0; i < content.length; i++) {
if (content[i] === '{' || content[i] === '[') jsonStartIndices.push(i);
}
for (const start of jsonStartIndices) {
let stack = [], inString = false, escaped = false, end = start;
for (let i = start; i < content.length; i++) {
const char = content[i];
if (escaped) { escaped = false; continue; }
if (char === '\\') { escaped = true; continue; }
if (char === '"') { inString = !inString; continue; }
if (!inString) {
if (char === '{' || char === '[') stack.push(char);
else if (char === '}' || char === ']') {
if (stack.length === 0) break;
const last = stack.pop();
if ((last === '{' && char !== '}') || (last === '[' && char !== ']')) {
stack.length = 0; break;
}
}
}
if (stack.length === 0 && !inString) {
end = i;
const candidate = content.substring(start, end + 1).trim();
try {
const parsed = JSON.parse(candidate);
if (parsed && (typeof parsed === 'object' || Array.isArray(parsed))) {
return candidate;
}
} catch (e) { continue; }
}
}
}
return null;
}
static safeRepair(content) {
let repaired = content.trim();
repaired = repaired.replace(/^\ufeff/, '')
.replace(//g, '')
.replace(/\/\/[^\r\n]*/g, '')
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/,\s*([}\]])/g, '$1')
.replace(/[\x00-\x1F\x7F]/g, '')
.replace(/^[a-zA-Z_$][a-zA-Z0-9_$]*\(\s*([\s\S]*?)\s*\);?$/, '$1');
return repaired;
}
static isJsonResponse() {
const contentType = (document.contentType || '').toLowerCase();
if (contentType.includes('text/html')) return false;
const validTypes = ['application/json', 'text/json', 'application/javascript', 'text/javascript', 'application/ld+json', 'text/plain'];
const hasValidType = validTypes.some(type => contentType.includes(type));
const bodyTrimmed = document.body.textContent?.trim() || '';
const hasJsonStructure = (bodyTrimmed.startsWith('{') || bodyTrimmed.startsWith('[')) &&
!bodyTrimmed.startsWith(' url.endsWith(ext));
}
}
// JSON格式化核心类(所有元素类名添加miw_前缀)
class LocalJSONFormatter {
constructor() {
this.container = null;
this.jsonData = null;
this.originalContent = null;
this.searchTerm = '';
this.lineCounter = 1;
this.isActive = false;
this.isRendering = false;
this.matchNodes = [];
this.currentMatchIndex = -1;
this.contentWrapper = null;
this.isRawView = false;
}
isJsonContent() {
if (this.isActive || document.querySelector('.miw_json-formatter-wrapper')) return false;
return JSONExtractor.isJsonResponse();
}
getPageContent() {
if (document.querySelector('.miw_json-formatter-wrapper')) return null;
let content = '';
const preCodeTags = document.querySelectorAll('pre, code');
for (const el of preCodeTags) {
const text = el.textContent?.trim() || '';
if (text.length >= CONFIG.MIN_JSON_LENGTH) {
content = text; break;
}
}
if (!content) {
content = document.body.textContent?.trim() || '';
}
this.originalContent = content;
const jsonContent = JSONExtractor.extractJSON(content);
return jsonContent || content;
}
parseJsonSafely(content) {
if (!content) throw new Error('待解析内容为空');
try {
const parsed = JSON.parse(content);
if (parsed && (typeof parsed === 'object' || Array.isArray(parsed))) {
return parsed;
}
return null;
} catch (e) {
return null;
}
}
getLineNumber() {
const num = this.lineCounter++;
if (!CONFIG.SHOW_LINE_NUMBERS) {
return '';
}
return `${num}`;
}
formatJson(data, depth = 0) {
let html = '';
if (this.isRendering && this.lineCounter > CONFIG.MAX_RENDER_SIZE) {
return `数据过大,已截断显示`;
}
switch (true) {
case data === null:
html = `null`;
break;
case typeof data === 'boolean':
html = `${data}`;
break;
case typeof data === 'number':
html = `${data}`;
break;
case typeof data === 'string': {
const escaped = this.escapeHtml(data);
html = `"${escaped}"`;
break;
}
case Array.isArray(data):
html = this.formatArray(data, depth);
break;
case typeof data === 'object':
html = this.formatObject(data, depth);
break;
default:
html = `${this.escapeHtml(String(data))}`;
}
return html;
}
formatArray(data, depth) {
if (data.length === 0) return `[]`;
const isCollapsed = depth >= CONFIG.COLLAPSE_LEVEL;
const nodeCount = CONFIG.SHOW_NODE_COUNT ? `${data.length} 项` : '';
let html = `
${this.getLineNumber()}
`;
html += `
`;
html += `[${nodeCount}`;
html += `
`;
html += ``;
data.forEach((item, idx) => {
html += `
${this.getLineNumber()}
`;
html += this.formatJson(item, depth + 1);
if (idx < data.length - 1) html += `,`;
html += `
`;
});
html += `
`;
html += `${this.getLineNumber()}
`;
html += `]`;
html += `
`;
return html;
}
formatObject(data, depth) {
const keys = Object.keys(data);
if (keys.length === 0) return `{}`;
const isCollapsed = depth >= CONFIG.COLLAPSE_LEVEL;
const nodeCount = CONFIG.SHOW_NODE_COUNT ? `${keys.length} 个属性` : '';
let html = `${this.getLineNumber()}
`;
html += `
`;
html += `{${nodeCount}`;
html += `
`;
html += ``;
keys.forEach((key, idx) => {
html += `
${this.getLineNumber()}
`;
html += `"${this.escapeHtml(key)}": `;
html += this.formatJson(data[key], depth + 1);
if (idx < keys.length - 1) html += `,`;
html += `
`;
});
html += `
`;
html += `${this.getLineNumber()}
`;
html += `}`;
html += `
`;
return html;
}
escapeHtml(str) {
return str.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// ========== 核心修改:工具栏类名添加miw_前缀 ==========
createToolbar() {
const toolbar = document.createElement('div');
toolbar.className = 'miw_json-toolbar'; // 添加miw_前缀
const lineBtnActive = CONFIG.SHOW_LINE_NUMBERS ? 'active' : '';
const lineBtnText = CONFIG.SHOW_LINE_NUMBERS ? '行号:开' : '行号:关';
const rawBtnText = this.isRawView ? '显示格式化' : '显示原文';
const searchContainer = `
0/0
`;
toolbar.innerHTML = `
${searchContainer}
`;
return toolbar;
}
// ========== 核心修改:通知元素类名添加miw_前缀 ==========
createNotification() {
const note = document.createElement('div');
note.className = 'miw_json-notification'; // 添加miw_前缀
return note;
}
showNotification(message, duration = 2000) {
// 选择器添加miw_前缀
const note = document.querySelector('.miw_json-formatter-wrapper .miw_json-notification');
if (!note) return;
note.textContent = message;
note.classList.add('show');
setTimeout(() => note.classList.remove('show'), duration);
}
async copyToClipboard(text, msg = '已复制到剪贴板') {
try {
await navigator.clipboard.writeText(text);
this.showNotification(msg);
} catch (err) {
this.showNotification('复制失败,请手动复制', 3000);
}
}
escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
expandAllCollapsibles() {
// 选择器添加miw_前缀
const allCollapsibles = this.contentWrapper.querySelectorAll('.miw_json-collapsible');
allCollapsibles.forEach(el => {
el.classList.add('expanded');
const childContainer = el.closest('.miw_json-line')?.nextElementSibling; // 添加miw_前缀
if (childContainer?.classList.contains('miw_json-children')) { // 添加miw_前缀
childContainer.style.display = 'block';
}
});
}
collectAllTextNodes(rootNode) {
const textNodes = [];
const traverse = (node) => {
if (!node) return;
const childNodes = node.childNodes;
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
if (child.nodeType === Node.TEXT_NODE) {
textNodes.push(child);
} else if (child.nodeType === Node.ELEMENT_NODE && !child.classList.contains('miw_json-ellipsis')) { // 添加miw_前缀
traverse(child);
}
}
};
traverse(rootNode);
return textNodes;
}
searchJSON(term) {
if (this.isRawView) return;
this.searchTerm = term.trim();
this.matchNodes = [];
this.currentMatchIndex = -1;
// 选择器添加miw_前缀
const highlights = this.contentWrapper.querySelectorAll('.miw_search-highlight');
highlights.forEach(hl => {
const parent = hl.parentNode;
while (hl.firstChild) parent.insertBefore(hl.firstChild, hl);
parent.removeChild(hl);
parent.normalize();
});
this.updateSearchCount();
if (!this.searchTerm) return;
this.expandAllCollapsibles();
const escapedTerm = this.escapeRegExp(this.searchTerm);
const regex = new RegExp(escapedTerm, 'gi');
const allTextNodes = this.collectAllTextNodes(this.contentWrapper);
allTextNodes.forEach(textNode => {
const originalText = textNode.textContent;
if (!originalText) return;
regex.lastIndex = 0;
const matches = [];
let match;
while ((match = regex.exec(originalText)) !== null) {
matches.push({
start: match.index,
end: match.index + match[0].length,
value: match[0]
});
}
if (matches.length === 0) return;
const fragment = document.createDocumentFragment();
let lastPos = 0;
matches.forEach(m => {
if (m.start > lastPos) {
fragment.appendChild(document.createTextNode(originalText.slice(lastPos, m.start)));
}
const hlSpan = document.createElement('span');
hlSpan.className = 'miw_search-highlight'; // 添加miw_前缀
hlSpan.textContent = m.value;
fragment.appendChild(hlSpan);
this.matchNodes.push(hlSpan);
lastPos = m.end;
});
if (lastPos < originalText.length) {
fragment.appendChild(document.createTextNode(originalText.slice(lastPos)));
}
textNode.parentNode.replaceChild(fragment, textNode);
});
this.updateSearchCount();
if (this.matchNodes.length > 0) {
this.currentMatchIndex = 0;
this.highlightCurrentMatch();
this.scrollToCurrentMatch();
this.showNotification(`找到 ${this.matchNodes.length} 个匹配项`, 2000);
} else {
this.showNotification('未找到匹配内容', 2000);
}
}
updateSearchCount() {
// 选择器添加miw_前缀
const countEl = this.container.querySelector('.miw_search-count');
if (!countEl) return;
const total = this.matchNodes.length;
const current = this.currentMatchIndex >= 0 ? this.currentMatchIndex + 1 : 0;
countEl.textContent = `${current}/${total}`;
}
highlightCurrentMatch() {
// 类名添加miw_前缀
this.matchNodes.forEach(node => node.classList.remove('current'));
if (this.currentMatchIndex >= 0 && this.currentMatchIndex < this.matchNodes.length) {
this.matchNodes[this.currentMatchIndex].classList.add('current');
}
}
scrollToCurrentMatch() {
if (this.currentMatchIndex < 0 || this.currentMatchIndex >= this.matchNodes.length) return;
const target = this.matchNodes[this.currentMatchIndex];
target.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });
}
prevMatch() {
if (this.isRawView) {
this.showNotification('原文视图不支持搜索', 1500);
return;
}
if (this.matchNodes.length === 0) { this.showNotification('无匹配内容', 1500); return; }
this.currentMatchIndex = this.currentMatchIndex <= 0 ? this.matchNodes.length - 1 : this.currentMatchIndex - 1;
this.highlightCurrentMatch();
this.scrollToCurrentMatch();
this.updateSearchCount();
}
nextMatch() {
if (this.isRawView) {
this.showNotification('原文视图不支持搜索', 1500);
return;
}
if (this.matchNodes.length === 0) { this.showNotification('无匹配内容', 1500); return; }
this.currentMatchIndex = this.currentMatchIndex >= this.matchNodes.length - 1 ? 0 : this.currentMatchIndex + 1;
this.highlightCurrentMatch();
this.scrollToCurrentMatch();
this.updateSearchCount();
}
toggleRawView() {
this.isRawView = !this.isRawView;
// 选择器添加miw_前缀
const viewContainer = this.container.querySelector('.miw_view-container');
if (this.isRawView) {
// 类名添加miw_前缀
viewContainer.innerHTML = `${this.escapeHtml(this.originalContent)}
`;
this.showNotification('已切换至原文视图', 1500);
} else {
this.lineCounter = 1;
const formattedContent = this.jsonData ? this.formatJson(this.jsonData) : this.escapeHtml(this.originalContent);
viewContainer.innerHTML = formattedContent;
this.contentWrapper = viewContainer;
if (this.searchTerm) this.searchJSON(this.searchTerm);
this.showNotification('已切换至格式化视图', 1500);
}
// 选择器添加miw_前缀
const rawBtn = this.container.querySelector('.miw_raw-view-btn');
rawBtn.textContent = this.isRawView ? '显示格式化' : '显示原文';
}
bindEvents() {
// 选择器添加miw_前缀
const toolbar = this.container.querySelector('.miw_json-toolbar');
if (!toolbar) return;
// 选择器添加miw_前缀
const searchBox = toolbar.querySelector('.miw_search-box');
searchBox.addEventListener('input', (e) => this.searchJSON(e.target.value));
searchBox.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
e.shiftKey ? this.prevMatch() : this.nextMatch();
}
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_search-nav-btn.prev-match-btn').addEventListener('click', () => this.prevMatch());
toolbar.querySelector('.miw_search-nav-btn.next-match-btn').addEventListener('click', () => this.nextMatch());
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.expand-btn').addEventListener('click', () => {
if (this.isRawView) {
this.showNotification('原文视图不支持展开', 1500);
return;
}
this.expandAllCollapsibles();
this.showNotification('已展开全部节点');
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.collapse-btn').addEventListener('click', () => {
if (this.isRawView) {
this.showNotification('原文视图不支持折叠', 1500);
return;
}
// 选择器添加miw_前缀
this.container.querySelectorAll('.miw_json-collapsible').forEach(el => {
el.classList.remove('expanded');
// 选择器添加miw_前缀
const child = el.closest('.miw_json-line')?.nextElementSibling;
if (child?.classList.contains('miw_json-children')) child.style.display = 'none'; // 添加miw_前缀
});
this.showNotification('已折叠全部节点');
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.copy-btn').addEventListener('click', async () => {
const copyContent = this.isRawView || !this.jsonData
? this.originalContent
: JSON.stringify(this.jsonData, null, 2);
await this.copyToClipboard(copyContent, '已复制内容到剪贴板');
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.theme-btn').addEventListener('click', () => {
CONFIG.DARK_MODE = !CONFIG.DARK_MODE;
updateStyles();
this.reRender();
this.showNotification(`已切换至${CONFIG.DARK_MODE ? '暗色' : '亮色'}主题`);
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.line-numbers-btn').addEventListener('click', () => {
if (this.isRawView) {
this.showNotification('原文视图不支持行号切换', 1500);
return;
}
CONFIG.SHOW_LINE_NUMBERS = !CONFIG.SHOW_LINE_NUMBERS;
updateStyles();
this.lineCounter = 1;
// 选择器添加miw_前缀
const lineBtn = toolbar.querySelector('.miw_toolbar-button.line-numbers-btn');
lineBtn.textContent = CONFIG.SHOW_LINE_NUMBERS ? '行号:开' : '行号:关';
lineBtn.classList.toggle('active', CONFIG.SHOW_LINE_NUMBERS);
this.reRender();
this.showNotification(`行号${CONFIG.SHOW_LINE_NUMBERS ? '开启' : '关闭'}`);
});
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.raw-view-btn').addEventListener('click', () => this.toggleRawView());
// 选择器添加miw_前缀
toolbar.querySelector('.miw_toolbar-button.close-btn').addEventListener('click', () => this.closeFormatter());
this.container.addEventListener('click', e => {
if (this.isRawView) return;
// 选择器添加miw_前缀
const target = e.target.closest('.miw_json-collapsible');
if (!target) return;
const isExpanded = target.classList.contains('expanded');
// 选择器添加miw_前缀
const childContainer = target.closest('.miw_json-line')?.nextElementSibling;
if (!childContainer?.classList.contains('miw_json-children')) return; // 添加miw_前缀
if (isExpanded) {
target.classList.remove('expanded');
childContainer.style.display = 'none';
} else {
target.classList.add('expanded');
childContainer.style.display = 'block';
}
});
}
closeFormatter() {
this.isActive = false;
// 移除脚本样式
if (styleElement && styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
window.history.length > 1 ? window.history.back() : window.location.reload();
}
showError(error) {
// 所有类名添加miw_前缀
this.container.innerHTML = `
内容处理失败
${this.escapeHtml(error.message)}
${this.originalContent ? `
${this.escapeHtml(this.originalContent)}
` : ''}
`;
setTimeout(() => {
document.getElementById('retry-btn')?.addEventListener('click', () => this.apply());
document.getElementById('raw-btn')?.addEventListener('click', () => {
this.container.innerHTML = `${this.escapeHtml(this.originalContent)}
`;
});
document.getElementById('back-btn')?.addEventListener('click', () => this.closeFormatter());
}, 0);
}
reRender() {
if (!this.isActive || !this.contentWrapper) return;
if (this.isRawView) {
updateStyles();
return;
}
this.lineCounter = 1;
const formattedContent = this.jsonData ? this.formatJson(this.jsonData) : this.escapeHtml(this.originalContent);
this.contentWrapper.innerHTML = formattedContent;
if (this.searchTerm) this.searchJSON(this.searchTerm);
}
apply() {
if (this.isActive || this.isRendering) return;
this.isRendering = true;
try {
if (!this.isJsonContent()) {
this.isRendering = false;
return;
}
const rawContent = this.getPageContent();
if (!rawContent) {
this.isRendering = false;
return;
}
this.jsonData = this.parseJsonSafely(rawContent);
// 核心修改:容器类名添加miw_前缀
if (!this.container) {
this.container = document.createElement('div');
this.container.className = 'miw_json-formatter-wrapper'; // 添加miw_前缀
}
document.body.replaceChildren(this.container);
this.isActive = true;
// 激活后加载样式
updateStyles();
// 核心修改:内容容器类名添加miw_前缀
this.contentWrapper = document.createElement('div');
this.contentWrapper.className = 'miw_view-container'; // 添加miw_前缀
this.isRawView = !this.jsonData || JSONExtractor.isNonJsonFile();
if (this.isRawView) {
// 类名添加miw_前缀
this.contentWrapper.innerHTML = `${this.escapeHtml(this.originalContent)}
`;
} else {
this.lineCounter = 1;
const jsonContent = this.formatJson(this.jsonData);
this.contentWrapper.innerHTML = jsonContent;
}
this.container.innerHTML = '';
this.container.appendChild(this.createToolbar());
this.container.appendChild(this.contentWrapper);
this.container.appendChild(this.createNotification());
this.bindEvents();
if (this.isRawView) {
this.showNotification('非JSON文件,默认显示原文', 2000);
} else {
this.showNotification('JSON 格式化完成', 1500);
}
} catch (err) {
console.error('内容处理错误:', err);
if (!this.container) {
this.container = document.createElement('div');
this.container.className = 'miw_json-formatter-wrapper'; // 添加miw_前缀
document.body.replaceChildren(this.container);
this.isActive = true;
updateStyles();
}
this.showError(err);
} finally {
this.isRendering = false;
}
}
}
// 监听页面动态内容
function setupContentObserver() {
if (mutationObserver) return;
mutationObserver = new MutationObserver(mutations => {
if (globalFormatter?.isActive) return;
for (const mut of mutations) {
if (mut.addedNodes.length && JSONExtractor.isJsonResponse()) {
setTimeout(() => {
globalFormatter?.apply();
}, 300);
break;
}
}
});
mutationObserver.observe(document.body, { childList: true, subtree: true, characterData: true });
}
// 注册脚本菜单
function registerMenuCommands() {
if (typeof GM_registerMenuCommand !== 'function') return;
GM_registerMenuCommand('🔍 格式化/查看原文', () => {
if (!globalFormatter) globalFormatter = new LocalJSONFormatter();
globalFormatter.apply();
});
GM_registerMenuCommand('📋 复制当前内容', () => {
if (globalFormatter?.isActive) {
const copyContent = globalFormatter.isRawView || !globalFormatter.jsonData
? globalFormatter.originalContent
: JSON.stringify(globalFormatter.jsonData, null, 2);
globalFormatter.copyToClipboard(copyContent);
}
});
GM_registerMenuCommand('🌓 切换明暗主题', () => {
if (!globalFormatter?.isActive) return;
CONFIG.DARK_MODE = !CONFIG.DARK_MODE;
updateStyles();
if (globalFormatter?.isActive) globalFormatter.reRender();
});
GM_registerMenuCommand('🔢 切换行号显示', () => {
if (!globalFormatter?.isActive || globalFormatter?.isRawView) return;
CONFIG.SHOW_LINE_NUMBERS = !CONFIG.SHOW_LINE_NUMBERS;
updateStyles();
if (globalFormatter?.isActive) {
globalFormatter.lineCounter = 1;
globalFormatter.reRender();
}
});
}
// 初始化
function init() {
globalFormatter = new LocalJSONFormatter();
registerMenuCommands();
setupContentObserver();
if (CONFIG.AUTO_FORMAT) {
setTimeout(() => {
globalFormatter.apply();
}, 500);
}
}
init();
})();