- '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 <i@gerald.top>
- // @icon http://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80
- // @description Format JSON data in a beautiful way.
- // @description:zh-CN 更加漂亮地显示JSON数据。
- // @version 1.4.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', {
- 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 res.concat([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-val {\n color: dodgerblue;\n}* {\n margin: 0;\n padding: 0;\n}\n\nhtml, body {\n font-family: Menlo, \"Microsoft YaHei\", Tahoma;\n font-size: 14px;\n}\n\n#root {\n position: relative;\n margin: 0;\n padding: 1rem;\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");
- 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;
- formatter.root = createElement('div', { id: 'root' });
- document.body.innerHTML = '';
- document.body.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`;
- tips.innerHTML = `<span class="tips-key">type</span>: <span class="tips-val">${safeHTML(range.startContainer.dataset.type)}</span>`;
- 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);
- }