您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Format JSON data in a beautiful way.
当前为
'use strict'; function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } // ==UserScript== // @name JSON formatter // @namespace http://gerald.top // @author Gerald <[email protected]> // @icon http://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80 // @description Format JSON data in a beautiful way. // @description:zh-CN 更加漂亮地显示JSON数据。 // @version 1.5.0 // @match *://*/* // @match file:///* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // ==/UserScript== var id = 0; var getId = function getId() { return id += 1; }; var SINGLELINE = getId(); var MULTILINE = getId(); var KEY = getId(); var gap = 5; var createQuote = function createQuote() { return createElement('span', { className: 'subtle quote', textContent: '"' }); }; var createComma = function createComma() { return createElement('span', { className: 'subtle comma', textContent: ',' }); }; var createSpace = function createSpace() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return createElement('span', { className: 'space', textContent: ' '.repeat(n) }); }; var createIndent = function createIndent() { var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; return createSpace(2 * n); }; var createBr = function createBr() { return createElement('br'); }; var formatter = { options: [{ key: 'hide-quotes', title: '"', def: false }, { key: 'hide-commas', title: ',', def: false }] }; var config = GM_getValue('config', formatter.options.reduce(function (res, item) { res[item.key] = item.def; return res; }, {})); if (['application/json', 'text/plain', 'application/javascript', 'text/javascript'].includes(document.contentType)) formatJSON(); GM_registerMenuCommand('Toggle JSON format', formatJSON); function safeHTML(html) { return String(html).replace(/[<&"]/g, function (key) { return { '<': '<', '&': '&', '"': '"' }[key]; }); } function createElement(tag, props) { var el = document.createElement(tag); if (props) { Object.keys(props).forEach(function (key) { el[key] = props[key]; }); } return el; } function join(rendered) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var arr = []; for (var i = 0; i < rendered.length; i += 1) { var item = rendered[i]; var next = rendered[i + 1]; if (item.data) arr.push.apply(arr, _toConsumableArray(item.data)); if (next) { if (item.separator) arr.push.apply(arr, _toConsumableArray(item.separator)); if (next.type === KEY || item.type !== KEY && (item.type === SINGLELINE || next.type === SINGLELINE)) { arr.push(createBr(), createIndent(level)); } else { arr.push(createSpace(1)); } } } return arr; } function createNodes(data) { var valueType = typeof data.value; var type = data.type || valueType; var el = createElement('span', { className: data.cls || `item ${type}`, textContent: `${data.value}` }); el.dataset.type = valueType; el.dataset.value = data.value; var els = [el]; if (data.type === 'key' || !data.cls && type === 'string') { els.unshift(createQuote()); els.push(createQuote()); } return els; } function render(data) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (Array.isArray(data)) { var arr = []; var ret = { type: MULTILINE, separator: [createComma()] }; arr.push.apply(arr, _toConsumableArray(createNodes({ value: '[', cls: 'bracket' }))); if (data.length) { var rendered = data.reduce(function (res, item) { return [].concat(_toConsumableArray(res), [render(item, level + 1)]); }, []); arr.push.apply(arr, [createBr(), createIndent(level + 1)].concat(_toConsumableArray(join(rendered, level + 1)), [createBr(), createIndent(level)])); } else { arr.push.apply(arr, _toConsumableArray(createNodes({ value: '', cls: 'subtle' }))); ret.type = SINGLELINE; } arr.push.apply(arr, _toConsumableArray(createNodes({ value: ']', cls: 'bracket' }))); ret.data = arr; return ret; } if (data === null) { return { type: SINGLELINE, separator: [createComma()], data: createNodes({ value: data, type: 'null' }) }; } if (typeof data === 'object') { var _arr = []; var _ret = { type: MULTILINE, separator: [createComma()] }; _arr.push.apply(_arr, _toConsumableArray(createNodes({ value: '{', cls: 'bracket' }))); var _rendered = Object.keys(data).reduce(function (res, key) { return res.concat([{ type: KEY, data: createNodes({ value: key, type: 'key' }), separator: createNodes({ value: ':', cls: 'subtle' }) }, render(data[key], level + 1)]); }, []); if (_rendered.length) { _arr.push.apply(_arr, [createBr(), createIndent(level + 1)].concat(_toConsumableArray(join(_rendered, level + 1)), [createBr(), createIndent(level)])); } else { _arr.push.apply(_arr, _toConsumableArray(createNodes({ value: '', cls: 'subtle' }))); _ret.type = SINGLELINE; } _arr.push.apply(_arr, _toConsumableArray(createNodes({ value: '}', cls: 'bracket' }))); _ret.data = _arr; return _ret; } return { type: SINGLELINE, separator: [createComma()], data: createNodes({ value: data }) }; } function loadJSON() { var text = document.body.innerText; try { // JSON var content = JSON.parse(text); return { prefix: '', suffix: '', content }; } catch (e) { // not JSON } try { // JSONP var parts = text.match(/^(.*?\w\s*\()(.+)(\)[;\s]*)$/); var _content = JSON.parse(parts[2]); var prefix = parts[1]; var suffix = parts[3]; return { prefix, content: _content, suffix }; } catch (e) { // not JSONP } } function formatJSON() { if (formatter.formatted) { formatter.tips.hide(); formatter.menu.detach(); document.body.innerHTML = formatter.raw; formatter.formatted = false; } else { if (!('raw' in formatter)) { formatter.raw = document.body.innerHTML; formatter.data = loadJSON(); if (!formatter.data) return; // formatter.style = GM_addStyle(".tips-link {\n color: slateblue;\n}.tips-val {\n color: dodgerblue;\n}* {\n margin: 0;\n padding: 0;\n}\n\n#root {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n margin: 0;\n padding: 16px;\n font-family: Menlo, \"Microsoft YaHei\", Tahoma;\n font-size: 14px;\n overflow: auto;\n}\n\n#root > pre {\n white-space: pre-wrap;\n}\n\n.subtle {\n color: #999;\n}\n.number {\n color: darkorange;\n}\n.null {\n color: gray;\n}\n.key {\n color: brown;\n}\n.string {\n color: green;\n}\n.boolean {\n color: dodgerblue;\n}\n.bracket {\n color: blue;\n}\n.item {\n cursor: pointer;\n}\n\n.tips {\n position: absolute;\n padding: .5em;\n border-radius: .5em;\n box-shadow: 0 0 1em gray;\n background: white;\n z-index: 1;\n white-space: nowrap;\n color: black\n}\n\n.tips-key {\n font-weight: bold;\n}\n.menu {\n position: fixed;\n top: 0;\n right: 0;\n background: white;\n padding: 5px;\n user-select: none;\n}\n.menu > span {\n margin-right: 5px;\n}\n.menu .btn {\n display: inline-block;\n width: 18px;\n height: 18px;\n line-height: 18px;\n text-align: center;\n background: #ddd;\n border-radius: 4px;\n cursor: pointer\n}\n.menu .btn.active {\n color: white;\n background: #444;\n}\n\n.hide-quotes .quote, .hide-commas .comma {\n font-size: 0;\n}\n\n.space {\n letter-spacing: 8px;\n}\n"); initTips(); initMenu(); formatter.render = function () { var pre = formatter.pre; var _formatter$data = formatter.data, prefix = _formatter$data.prefix, content = _formatter$data.content, suffix = _formatter$data.suffix; pre.innerHTML = ''; [createElement('span', { className: 'subtle', textContent: prefix })].concat(_toConsumableArray(render(content).data), [createElement('span', { className: 'subtle', textContent: suffix })]).forEach(function (el) { pre.appendChild(el); }); formatter.update(); }; formatter.update = function () { formatter.options.forEach(function (_ref) { var key = _ref.key; formatter.pre.classList[config[key] ? 'add' : 'remove'](key); }); }; } formatter.formatted = true; var hostRoot = createElement('div'); document.body.innerHTML = ''; document.body.appendChild(hostRoot); var shadow = hostRoot.attachShadow({ mode: 'open' }); formatter.style = createElement('style', { textContent: ".tips-link {\n color: slateblue;\n}.tips-val {\n color: dodgerblue;\n}* {\n margin: 0;\n padding: 0;\n}\n\n#root {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n margin: 0;\n padding: 16px;\n font-family: Menlo, \"Microsoft YaHei\", Tahoma;\n font-size: 14px;\n overflow: auto;\n}\n\n#root > pre {\n white-space: pre-wrap;\n}\n\n.subtle {\n color: #999;\n}\n.number {\n color: darkorange;\n}\n.null {\n color: gray;\n}\n.key {\n color: brown;\n}\n.string {\n color: green;\n}\n.boolean {\n color: dodgerblue;\n}\n.bracket {\n color: blue;\n}\n.item {\n cursor: pointer;\n}\n\n.tips {\n position: absolute;\n padding: .5em;\n border-radius: .5em;\n box-shadow: 0 0 1em gray;\n background: white;\n z-index: 1;\n white-space: nowrap;\n color: black\n}\n\n.tips-key {\n font-weight: bold;\n}\n.menu {\n position: fixed;\n top: 0;\n right: 0;\n background: white;\n padding: 5px;\n user-select: none;\n}\n.menu > span {\n margin-right: 5px;\n}\n.menu .btn {\n display: inline-block;\n width: 18px;\n height: 18px;\n line-height: 18px;\n text-align: center;\n background: #ddd;\n border-radius: 4px;\n cursor: pointer\n}\n.menu .btn.active {\n color: white;\n background: #444;\n}\n\n.hide-quotes .quote, .hide-commas .comma {\n font-size: 0;\n}\n\n.space {\n letter-spacing: 8px;\n}\n" }); shadow.appendChild(formatter.style); formatter.root = createElement('div', { id: 'root' }); shadow.appendChild(formatter.root); formatter.pre = createElement('pre'); formatter.root.appendChild(formatter.pre); formatter.menu.attach(); bindEvents(); formatter.render(); } } function removeEl(el) { if (el && el.parentNode) el.parentNode.removeChild(el); } function initMenu() { var menu = createElement('div', { className: 'menu' }); formatter.options.forEach(function (item) { var span = createElement('span', { className: `btn${config[item.key] ? ' active' : ''}`, innerHTML: item.title }); span.dataset.key = item.key; menu.appendChild(span); }); menu.addEventListener('click', function (e) { var el = e.target; var key = el.dataset.key; if (key) { config[key] = !config[key]; GM_setValue('config', config); el.classList.toggle('active'); formatter.update(); } }, false); formatter.menu = { node: menu, attach() { formatter.root.appendChild(menu); }, detach() { removeEl(menu); } }; } function initTips() { var tips = createElement('div', { className: 'tips' }); var hide = function hide() { return removeEl(tips); }; tips.addEventListener('click', function (e) { e.stopPropagation(); }, false); document.addEventListener('click', hide, false); formatter.tips = { node: tips, hide, show(range) { var scrollTop = document.body.scrollTop; var rects = range.getClientRects(); var rect = void 0; if (rects[0].top < 100) { rect = rects[rects.length - 1]; tips.style.top = `${rect.bottom + scrollTop + gap}px`; tips.style.bottom = ''; } else { rect = rects[0]; tips.style.top = ''; tips.style.bottom = `${formatter.root.offsetHeight - rect.top - scrollTop + gap}px`; } tips.style.left = `${rect.left}px`; var _range$startContainer = range.startContainer.dataset, type = _range$startContainer.type, value = _range$startContainer.value; var html = [`<span class="tips-key">type</span>: <span class="tips-val">${safeHTML(type)}</span>`]; if (type === 'string' && /^(https?|ftps?):\/\/\S+/.test(value)) { html.push('<br>', `<a class="tips-link" href="${encodeURI(value)}" target="_blank">Open link</a>`); } tips.innerHTML = html.join(''); formatter.root.appendChild(tips); } }; } function selectNode(node) { var selection = window.getSelection(); selection.removeAllRanges(); var range = document.createRange(); range.setStartBefore(node.firstChild); range.setEndAfter(node.firstChild); selection.addRange(range); return range; } function bindEvents() { formatter.root.addEventListener('click', function (e) { e.stopPropagation(); var target = e.target; if (target.classList.contains('item')) { formatter.tips.show(selectNode(target)); } else { formatter.tips.hide(); } }, false); }