// ==UserScript==
// @license MIT
// @name JSON Viewer
// @namespace http://tampermonkey.net/
// @version 0.5.4
// @note v0.5.4 单击复制修改为CTRL+单击复制JSONPath功能,JSON格式化风格增加table格式
// @note v0.5.3 增加暗黑主题色
// @note v0.5.2 单击JSON格式化的key可复制JSONPath
// @note v0.5.1 修复JSONPath提示有误
// @note v0.5.0 解决chrome 120+以上内核JSON格式化不执行和引入layer报错问题
// @note v0.4.9 布局修改,增加保存JSON/脑图为文件,增加JSON过滤,鼠标移入key提示JSONPath
// @note v0.4.8 代码优化
// @note v0.4.7 增加对JSONP的判断,代码优化
// @note v0.4.6 增加复制按钮,JSON脑图CSS样式细节优化,JSON脑图增加收起/展开子节点按钮
// @note v0.4.5 在json-viewer-updated原基础上进行了一些修改,主要有CSS样式修改,新增折叠/展开全部功能,新增JSON脑图功能,脑图节点点击显示调用路径
// @description 格式化显示JSON使数据看起来更加漂亮,支持折叠/展开格式化后的数据,支持JSON脑图让调用层级看着更清晰,支持复制JSON脑图节点路径
// @author Feny
// @match *://*/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant unsafeWindow
// @grant GM_setClipboard
// @grant GM_getResourceText
// @icon data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAeAB4AAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAgACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9wvjF8bLX4ZrHZx+XNqlwnmKjH5YUyRvb6kEAd8H058d1b47XV5Ir3eoN++bagaXYrN6KOBn2FfPPx5/aEutX8a+KNWhIubhruZLSNj8u1WMcIOP4QoXOOwNS/wDBM79lDTfjh8YNe+IXj63TxRdeHfKjsE1KMTxtdSbmMmxvlAiVV2IBsUybgAyKR9ZTy6lhsM69Xovm2+iPmKmOqYjEKjT6v5WXU9e8beKrjxZpp/s/xFrfhjWIwWs9X0q42z2kmPlZo2zDcxjqYZ0eNv7obDDf/YL/AG8L745eJNc+G3xCttP0n4qeEWZZ2sVZNP8AENsu0rd2ysS0ZKPG7RMSQsisCcukWd+3x4RtfhpqGk+JNPjS0g1mV7a8iT5UM4XesgHYsofdjglQepJPxwPE1x4V/au8LePtNkeK40ma0lmdDgyorvHMhPo9uxjP+y1bUcHRxmGbS1adn1TXT0/4fcipiqmExCi3pfVdGn19f+GLni/w7ceGf2wPFXga+Vo5rW/vDbI3WWI/v4HA/wBqBg3tk+lfWX7AXia1+F/iDV9B1CRbWHXjFLayyHannpuUxk+rqy4zxlMdWAPQft7fsL33x91nQfiD4DutP0r4oeDXVrT7cWWx1y3UsTZ3LKCyAh5FEigkLLIpHzBk5vwj4JuvE2kLJfeHdY8N6lGAl5pepwBZrOT+JRIuYp0ByBNCzxPg4bIICqYyljMKot62Sa6prr6P/gBDC1MLiHJLS90+jT6ev/Dnmn/BVP8Aaw0nxv4/8P8Aw18KXK69qmk3MlxqEdiRMwuivlpbrtPLopkMnZNy5IIYLyPwa/Z8vvEmv+HtHu4/Ovr+5iS6KDcqAtukwe6om7nuEJxX0XoH7PK/bJG0vRoY5rr/AFslvbLGZf8AfcAf+PGvafgv8CbX4byNqFyI5tWmXYCoytsh6qvqx7t+A4yWiWYUsLhlRpbr72318kVHA1MTiHVqbP7kl0P/2Q==
// @require https://code.jquery.com/jquery.min.js
// @require https://unpkg.com/jsmind@0.8.5/es6/jsmind.js
// @require https://unpkg.com/dom-to-image@2.6.0/dist/dom-to-image.min.js
// @require https://unpkg.com/jsmind@0.8.5/es6/jsmind.screenshot.js
// @require https://cdn.jsdelivr.net/npm/@kanety/jquery-simple-tree-table@0.5.1/dist/jquery-simple-tree-table.min.js
// @resource swalStyle https://unpkg.com/jsmind@0.8.5/style/jsmind.css
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
const Utils = {
// 检查字符串是否为URL
isUrl: function (string) {
var regexp = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
return regexp.test(string);
},
// 检查是否是图片链接
isImg: function (pathImg) {
// var regexp = /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?\/([\w#!:.?+=&%@!\-\/])*\.(gif|jpg|jpeg|png|GIF|JPG|PNG)([\w#!:.?+=&%@!\-\/])?/;
let regexp = /\.(ico|bmp|gif|jpg|jpeg|png|svg|webp|GIF|JPG|PNG|WEBP|SVG)([\w#!:.?+=&%@!\-\/])?/i
return regexp.test(pathImg);
},
// 检验内容是否是json格式的内容
isJSON: function (str) {
try {
JSON.parse(str)
return true
} catch(e) {
console.log("is not json", e)
return false
}
},
// 获取数据类型
getType: function (value){
return Object.prototype.toString.call(value).match(/\s(.+)]/)[1].toLowerCase();
},
// JSON 过滤
filterJson: function(json, filter) {
if(!filter){
return json
}
filter = filter.toLowerCase()
let newJSON = Utils.getType(json) == 'array' ? [] : {}
for (let key in json) {
let val = json[key]
if (typeof val === 'object') {
let subJSON = this.filterJson(val, filter);
if (Object.keys(subJSON).length > 0) {
newJSON[key] = subJSON;
}
}else{
if(key.toLowerCase().includes(filter.toLowerCase())){
newJSON[key] = val
}
if(val !== null && val.toString().toLowerCase().includes(filter.toLowerCase())){
newJSON[key] = val
}
}
}
return newJSON;
}
}
// jquery.json-viewer 插件 开始
// 解决和原网页jquery版本冲突
var _jQuery = jQuery.noConflict(true);
(function($){
/**
* 检查 arg 是否为至少包含 1 个元素的数组或至少包含 1 个键的字典
*/
function isCollapsable(arg) {
return arg instanceof Object && Object.keys(arg).length > 0;
}
/**
* 将 JSON 对象转换为 HTML 表示形式
* @return string
*/
function json2html(json, parentPath = '') {
let html = '', type = Utils.getType(json)
switch(type){
case 'array':
case 'object':
let len = json.length || Object.keys(json).length;
if (len > 0) {
html += ``;
html += type === 'array' ? '[
' : '{';
for (var key in json) {
if (json.hasOwnProperty(key)) {
let comma = --len > 0 ? ',' : '',
jsonPath = parentPath + '.' + key,
collapse = isCollapsable(json[key]) ? '' : '',
res = json2html(json[key], jsonPath)
let toHtml = type === 'array' ? res : `"${key}": ${res}`
html += [`- `, collapse, toHtml, comma, '
'].join('')
}
}
if(type === 'array'){
html += `
]`
}else{
html += `}`
}
}else{
html += ``
html += (type === 'array') ? '[]' : '{}'
html += ''
}
break
default:
/* Escape tags */
json = type === 'string' ? json.replace(/&/g, '&').replace(//g, '>') : json
if (Utils.isUrl(json)){
html += `"${json}"`;
}else{
json = type === 'string' ? `"${json}"` : json
html += `${json}`;
}
break
}
return html;
}
$.fn.jsonViewer = function(json, jsonpFun) {
return this.each(function() {
/* Transform to HTML */
var html = json2html(json);
/** is JSONP */
if(jsonpFun !== undefined && jsonpFun !== null){
html = `${jsonpFun}(
${html})
`
}
/* Insert HTML in target DOM element */
$(this).html(html);
/* Bind click on toggle buttons */
$(this).off('click');
$(this).on('click', 'a.json-toggle', function() {
var target = $(this).toggleClass('collapsed').siblings('ul.json-object, ol.json-array');
target.toggle();
if (target.is(':visible')) {
target.siblings('.json-placeholder').remove();
}else {
var count = target.children('li:not([class*="hidden"])').length;
var placeholder = count + (count > 1 ? ' items' : ' item');
target.after('' + placeholder + '');
}
return false;
});
/* Simulate click on toggle button when placeholder is clicked */
$(this).on('click', 'a.json-placeholder', function() {
$(this).siblings('a.json-toggle').click();
$(this).siblings('a.json-placeholder').remove();
return false;
});
});
};
})(_jQuery);
// jquery.json-viewer 插件 结束
(function($){
var source = $('pre').first();
if(source.length === 0){
return
}
let rawText = source.text()
if(!rawText){
return
}
// 判断是否为jsonp格式
let jsonpFun = null, tokens = rawText.match(/^([^\s(]*)\s*\(([\s\S]*)\)\s*;?$/)
if (tokens && tokens[1] && tokens[2]) {
jsonpFun = tokens[1]
rawText= tokens[2]
}
if(!Utils.isJSON(rawText)){
return
}
// 随机rgb颜色
let rgbaColor = `${Math.random()*256}, ${Math.random()*256}, ${Math.random()*256}`
// 添加样式
GM_addStyle(GM_getResourceText('swalStyle'))
$("head").append(``)
.append('')
GM_addStyle(`
body, html{
margin: 0;
padding: 0;
font-size: 14px;
}
td{
font-size: 14px;
}
li::marker {
content: '';
}
.hidden{
display: none !important;
}
.scroll-top{
width: 48px;
height: 48px;
z-index: 999;
position: fixed;
right: 30px;
bottom: 30px;
display: none;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAA39JREFUaEPtmV2ITVEUx39rJjT5KPKC5DPKK6UGZZ7Eq0whNUiKFPfcKW9mnmjm7ssLaSLygEx5xItMESnKC4V8FV4IJUkyS+fMHZ07zjl3733PvT6a+3S6Z629/r+99sfa+wgpPw1YDtxLe9/k/1eI4X5STMkSokXWNlloYjgpMZSmIxPgbxBfS8M4QK0eavT78Qw0uodrtd+wDGjAMqBcEVAQw6NaYnzeNwRAi2xBOQlMrYj6jLJTygz6iMzyyR1Ai+xCGUgMqnTmDZErgBYoIJjMXhY2S4mLeWUiNwAt0INwyEqY0iNleq1saxjlAqAB64BrToKEHVLijJNPgnHdALqPaUziOsoKRzGfaKFD+nng6FdlXj+Ay9AZq1Q5L2W2/jEA3c8sWqOSe3aCiK/A3cr/K4G2FKEbxXDZF6KuDKROXKWXFvqlxJdQmBaZzDDdKZP8thhW/RmAgKfA4qrgSq+U6UkSlAostEuJOz4Q3hnQgPXAld+CCh1pB5DogKTcSBAalhpHmw1wGtgxJuhbMczJEqIBbxLmzFUxbGg2gCYE9AVADF6jwctJDzKd73xI7DG/IQQTmCFH+OiaBT+AAyykhWcpBZv7JA4bGmaRHOV5cwBqX7mc5Qe9coyX0TK6n/m0RnVSV4bA1KuTLCi/DBTYhHCpRm+Fe8Bo/b8JmJxp71lqOwNowB7guGuqLe33iuGEpW1k5gTgVDK7qIjbZmyESU1aA1TOuA99dTn5CQulxAsbH3uAeqpOGyXVNrvEcMrGzR4gIGnjsonhYzMkhg4bx3EAm17ysGlIBh4DSzzE+LgMiGG3jaPLEApv2Q7YNFq3jcOmZg9gt/u6aH8PzEx0aGWu9PHapjFrgKimCaIDTHiQqf4pNxHW2AT8ZaMEiZdgSreUKdm25QRQgRi7nIZzI7xZcPmeNkAL5xjmVpVQx1049HUGiCAKFBH6K8GjFUP3MIW2CGJpjd4bFEPnmM54grA761tYWpteAJXgy1A6w6p09Opcu1nED7ZlAcQP/BqwGZjORAblMO9sh03czhsgLZgW6EL4JoYL0b0RTArPBfFnH6G5ZyAVYKTkGBlWQVR2bxDDgvjzvwQQXqGsDQ/sGkTXKdHzOECsB3LtjdjKMjqE/qMMjJwn5olh+98+hM6gvAqXy0aN+4Yuo/HG/weAPmC1GNrzHDbxtn4Coc0pQNdM3UAAAAAASUVORK5CYII=)
}
/** 工具栏样式 START **/
.flex-container{
z-index: 10;
position: fixed;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.tabs, .toolbar{
display: flex;
line-height: 28px;
background: #f3f3f3;
border-bottom: 1px solid #e0e0e2;
}
.toolbar{
line-height: 23px;
}
.searchbox{
display: flex;
flex-grow: 1;
}
.toolbar input{
flex-grow: 1;
border: none;
outline: none;
font-size: 12px;
padding-left: 23px;
background-size: 12px;
background-repeat: no-repeat;
background-position: 7px center;
background-image: url(data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIGZpbGw9InJnYmEoMTM1LCAxMzUsIDEzNywgMC45KSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTIgMTIiPgogIDxwYXRoIGZpbGw9ImNvbnRleHQtZmlsbCIgb3BhY2l0eT0iLjQiIGQ9Ik01IDkuMmwyIDEuNlY2LjFMOC41NSA0aC01LjFMNSA2LjF2My4xeiIvPgogIDxwYXRoIGZpbGw9ImNvbnRleHQtZmlsbCIgZD0iTTEuMTggMi42QTEgMSAwIDAgMSAyIDFIMTBhMSAxIDAgMCAxIC44IDEuNkw4IDYuNHY0LjgyYzAgLjYzLS43Mi45OC0xLjIyLjZsLTIuNS0xLjk5QS43NS43NSAwIDAgMSA0IDkuMjVWNi40MUwxLjE4IDIuNnpNMiAyTDUgNi4wOXYzLjA0bDIgMS41OVY2LjA5TDEwLjAxIDJIMnoiLz4KPC9zdmc+Cg==);
}
.clear {
flex: 0 0 auto;
align-self: center;
margin: 0 4px;
padding: 0;
border: 0;
width: 16px;
height: 16px;
background-color: transparent;
background-image: url(data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIGZpbGw9ImNvbnRleHQtZmlsbCIgZmlsbC1vcGFjaXR5PSJjb250ZXh0LWZpbGwtb3BhY2l0eSI+CiAgPHBhdGggZD0iTTYuNTg2IDhsLTIuMjkzIDIuMjkzYTEgMSAwIDAgMCAxLjQxNCAxLjQxNEw4IDkuNDE0bDIuMjkzIDIuMjkzYTEgMSAwIDAgMCAxLjQxNC0xLjQxNEw5LjQxNCA4bDIuMjkzLTIuMjkzYTEgMSAwIDEgMC0xLjQxNC0xLjQxNEw4IDYuNTg2IDUuNzA3IDQuMjkzYTEgMSAwIDAgMC0xLjQxNCAxLjQxNEw2LjU4NiA4ek04IDBhOCA4IDAgMSAxIDAgMTZBOCA4IDAgMCAxIDggMHoiLz4KPC9zdmc+Cg==);
}
.tabs-item{
border-top: 2px solid #f3f3f3;
}
.tabs-item:hover{
background: #e9e9e9;
border-top: 2px solid #c3c3c6;
}
.tabs-item, .toolbar-item{
cursor: pointer;
padding: 0 10px;
font-size: 12px;
}
.tabs-item.active{
color: #0060df;
border-top: 2px solid #0060df !important;
background: #e9e9e9;
}
.tabs-item:hover, .toolbar-item:hover{
background: #e9e9e9;
}
/** 工具栏样式 END **/
/** JSON 格式化样式 START **/
ul.json-object,
ul.json-array {
list-style-type: none;
margin: 0 0 0 2px;
border-left: 1px dotted #5D6D7E;
padding-left: 24px;
}
.json-brackets {
font-weight: 700;
}
.json-key {
/* color: #A31515;*/
color: #910F93;
cursor: pointer;
}
.json-string, .json-string a{
/* color: #0b7500;*/
color: #4B8A4C;
}
.json-number{
/* color: #164FF0;*/
color: #1a01cc;
font-weight: 600;
}
.json-boolean{
color: #905;
font-weight: 600;
}
.json-null {
/* color: #F1592A;*/
color: #0031BC;
font-weight: 600;
}
a.json-toggle {
position: rElative;
color: inherit;
opacity: 0.2;
text-decoration: none;
}
a.json-toggle:hover {
opacity: 0.35;
}
a.json-toggle:active {
opacity: 0.5;
}
a.json-toggle:focus {
outline: none;
}
a.json-toggle:before {
top: 2.5px;
left: -15px;
position: absolute;
content: "";
display: block;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 0 5px 8px;
border-color: transparent transparent transparent currentColor;
transform: rotate(90deg);
}
a.json-toggle.collapsed:before {
transform: rotate(0deg);
}
a.json-placeholder {
color: #aaa;
font-size: 12px;
padding: 0 1em;
text-decoration: none;
}
a.json-placeholder:hover {
text-decoration: underline;
}
.json-curly-brackets {
color: #6D9331;
}
.json-square-brackets{
color: #8E9331;
}
/** 暗黑主题 START **/
body.dark{
background: #333333;
}
.dark li, .dark pre{
color: #CCC;
}
.dark .json-toggle {
opacity: 0.35;
}
.dark .json-toggle:hover {
opacity: 0.5;
}
.dark .json-curly-brackets {
color: #CE70D6;
}
.dark .json-square-brackets{
color: #F1D700;
}
.dark .json-key {
color: #7CDCFE;
}
.dark .json-string, .dark .json-string a{
color: #CE9178;
}
.dark .json-number{
color: #B5CEA8;
}
.dark .json-boolean{
color: #569CD6;
}
.dark .json-null {
color: #2D7AD6;
}
.dark jmnode {
color: #7CDCFE !important;
}
/** 暗黑主题 END **/
/** JSON 格式化样式 END **/
/** 脑图样式 START **/
#jmContainer{
width: 100vw;
height: calc(100vh - 57px);
/* background:#F7F7F7 */
}
jmnode{
display: flex;
align-items: center;
padding: 0 7px 0 22px;
}
jmnode{
color: #475872 !important;
box-shadow: none !important;
background-color: transparent !important;
}
jmnode:hover{
text-shadow: 1px 1px 1px currentColor;
}
jmnode.root {
padding: 0;
color: transparent !important;
}
jmnode:not(.root)::before, jmnode.root::before{
content: " ";
top: 50%;
position: absolute;
border-radius: 50%;
transform: translateY(-50%);
}
jmnode:not(.root)::before{
left: 0;
width: 15px;
height: 15px;
background: rgba(${rgbaColor}, 0.5);
}
jmnode.root::before{
left: 50%;
width: 18px;
height: 18px;
transform: translate(-18px, -50%);
background: rgba(${rgbaColor}, 0.7);
}
jmexpander{
margin-top: 1px;
line-height: 9px;
background: #dfdfdf;
}
.mind-array{
opacity: 0.5;
font-size: 12px;
padding-left: 5px;
}
/** 脑图样式 END **/
/** 容器样式 START **/
.tabs-container{
overflow: auto;
line-height: 1.5;
font-family: monospace;
}
.tabs-container > div{
display: none;
}
.tabs-container > div.active{
display: block;
}
.tabs-container #formatContainer{
padding: 10px;
}
.tabs-container #rawTextContainer{
padding: 0 10px;
}
.tabs-container #rawTextContainer pre{
display: block !important;
overflow-wrap: break-word;
white-space: pre-wrap;
}
/** 容器样式 END **/
.layui-layer-tips{
width: auto !important;
}
.tabs .selectbox{
position: absolute;
right: 200px;
display: flex;
}
/** 表格 START **/
table {
width: 100%;
margin-left: 20px;
border-collapse: collapse;
}
table tr:hover{
background: #f0f9fe;
}
.dark table tr:hover{
background: #353B48
}
table tr.selected td, table tr.selected td a{
color: #fff !important;
background-color: #3875d7;
}
table tbody tr td:first-child{
width: 120px;
}
.simple-tree-table-icon{
color: #000;
opacity: 0.2;
width: 0 !important;
margin: 0 !important;
line-height: 0 !important;
}
.simple-tree-table-icon:hover {
opacity: 0.35;
}
.simple-tree-table-icon:active {
opacity: 0.5;
}
.simple-tree-table-icon:after{
content: "" !important;
}
.simple-tree-table-icon:before {
top: 0.5px;
left: -15px;
position: relative;
content: "";
width: 0;
height: 0;
display: none;
border-style: solid;
border-width: 5px 0 5px 8px;
border-color: transparent transparent transparent currentColor;
transform: rotate(90deg);
}
.simple-tree-table-opened .simple-tree-table-icon:before {
display: block;
}
.simple-tree-table-closed .simple-tree-table-icon:before {
display: block;
transform: rotate(0deg);
}
.dark .simple-tree-table-icon{
color: #FFF;
opacity: 0.5;
}
/** 表格 END **/
`)
source.hide()
// 将内容用eval函数处理下
const jsonObject = eval(`(${rawText})`);
$("body").addClass('dark').append(`
`)
let btnEvent = {
isFormater: false,
$rawText: $('#rawTextContainer'),
/**
* 保存为文件
*/
download: {
download: function(content, filename) {
const link = document.createElement("a")
link.href = content
link.download = filename
link.click()
},
saveJSON: function (text) {
// 创建一个 Blob 对象,包含要下载的文本内容
const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob)
let filename = new Date().getTime() + '.json';
this.download(url, filename)
URL.revokeObjectURL(url);
},
savePNG: () => jm.shoot(),
},
saveJson:function(){
if($('#jmContainer').is(':visible')){
this.download.savePNG()
}else{
this.download.saveJSON(this.$rawText.text())
}
},
// 复制JSON文本内容
copyJson: function(){
GM_setClipboard(this.$rawText.text())
layer.msg('复制成功', {time: 1500})
},
// 全部折叠
collapseAll: function(){
if($('#formatContainer').is(':visible')){
try{
$('.json-toggle').not('.collapsed').click()
}catch(e){}
}else{
jm.collapse_all()
}
},
// 全部展开
expandAll: function(){
if($('#formatContainer').is(':visible')){
try{
$('a.json-placeholder').click().remove()
}catch(e){}
}else{
jm.expand_all()
jm.scroll_node_to_center(jm.get_root())
}
},
format: function(){},
// 显示JSON脑图
viewJsonMind: function(){},
// 查看原始JSON内容
viewRawText: function(){
this.$rawText.html(source.clone())
},
// 美化
beautify: function(){
this.isFormater = !this.isFormater
if(this.isFormater){
this.$rawText.find('pre').text(JSON.stringify(jsonObject, null, 2))
}else{
this.viewRawText()
}
},
init: function(){
this.viewRawText()
// 按钮点击事件
$('.btn').click(e => {
const target = e.target, id = target.id
if(target.classList.contains('tabs-item')){
let index = $(target).index()
$(target).addClass('active').siblings().removeClass("active")
$('.tabs-container > div').removeClass("active").eq(index).addClass('active')
let beautifyEl = $('#beautify'),
searchEl= $('.searchbox'),
copyJsonEl= $('#copyJson'),
aEl = $('#collapseAll, #expandAll')
id === 'format' ? searchEl.show(): searchEl.hide()
id === 'viewJsonMind' ? copyJsonEl.hide(): copyJsonEl.show()
id === 'viewRawText' ? (beautifyEl.show() && aEl.hide()) : (beautifyEl.hide() && aEl.show())
}
this[id](target)
})
return this
}
},
jsonMind = {
// JSON数据转换为jsMind所需要的数据结构
convert: function(json){
let children = []
if(typeof json === 'object'){
for(let key in json){
let val = json[key],
isArray = Array.isArray(val)
children.push({
isArray,
chain: key,
id: key + '_' + Math.random(),
topic: isArray ? `${key}[${val.length}]` : `${key}`,
// children: this.convert(val)
children: isArray ? this.convert(val[0]) : this.convert(val)
})
}
}
return children;
},
// 脑图节点调用链
mindChain: function (node){
let chain = node.data.chain
if(!node.parent){
return chain
}
let parent = node.parent, parentChain = this.mindChain(parent)
chain = parent.data.isArray ? `${parentChain}[0].${chain}` : `${parentChain}.${chain}`
return chain
},
// 显示脑图
show: function(json, isArr){
jm.show({
"meta":{
"name":"JSON脑图",
"author":"1220301855@qq.com",
"version":"1.0"
},
"format":"node_tree",
/* 数据内容 */
"data": {
"id": "root",
"topic": 'Response',
"direction": "left",
"children": this.convert(json),
"chain": isArr ? 'Response[0]' : 'Response'
}
})
setTimeout(() => jm.scroll_node_to_center(jm.get_root()), 300)
return this
},
// 脑图节点事件
event:function(){
$("jmnode").on('dblclick mouseover mouseout', function(event){
let that = $(this), node = jm.get_node(that.attr('nodeid'))
if(!node.parent){
return
}
switch(event.type){
case 'dblclick':
GM_setClipboard(jsonMind.mindChain(node))
layer.msg('节点路径复制成功', {time: 1500})
break;
case 'mouseover':
let s = `节点路径(双击复制)
${jsonMind.mindChain(node)}`
layer.tips(s, that, {
time: 0,
tips: [2, '#2e59a7']
});
break;
default:
layer.closeAll()
break;
}
})
return this
},
collapseOrExpand: function(){
$("jmnode").on('click', function(){
let node = jm.get_node($(this).attr('nodeid'))
jm.toggle_node(node)
})
return this
},
init: function(json){
let isArr = Array.isArray(json);
if(isArr){
if(typeof json[0] !== 'object'){
layer.msg('数据结构无法生成脑图', {time: 1000})
return
}
json = json[0]
}
if(!window.jm){
window.jm = new jsMind({
mode :'side',
editable: false,
container:'jmContainer',
view: {
hmargin: 50, // 思维导图距容器外框的最小水平距离
vmargin: 50, // 思维导图距容器外框的最小垂直距离
engine: 'svg', // 思维导图各节点之间线条的绘制引擎
draggable: true, // 当容器不能完全容纳思维导图时,是否允许拖动画布代替鼠标滚动
support_html : false,
line_color: '#C4C9D0',
},
layout: {
vspace: 7, // 节点之间的垂直间距
hspace: 150, // 节点之间的水平空间
},
});
}
this.show(json, isArr).event().collapseOrExpand()
}
},
otherOperate = {
// 过滤 JSON
filterJSON: function(filter) {
let style = GM_getValue('formatStyle') || 'default'
if(!filter){
style == 'default' ? $('#formatContainer li').removeClass('hidden') : $('.json-key').parent().removeClass('hidden')
return
}
let chainSet= new Set()
/**
* 模糊匹配 JSON key
* 假如`filter`值为`id`, querySelectorAll得到DOM节点
* 得到:['.feedList.0.images.0.user_id', '.feedList.0.images.0', '.feedList.0.images', '.feedList.0', '.feedList']
*/
document.querySelectorAll('#formatContainer *[json-path]').forEach(el => {
let chain = $(el).attr('json-path')
if(!chain){
return
}
let newChain = chain.substr(chain.lastIndexOf('.'))
if(!newChain.toLowerCase().includes(filter.toLowerCase())){
return
}
chainSet.add(chain)
while(chain = chain.substr(0, chain.lastIndexOf('.'))){
chainSet.add(chain)
}
})
/**
* 模糊匹配 JSON value
*/
document.querySelectorAll("#formatContainer *[class*='json-']:not([class*='json-key']):not([class*='json-brackets'])")
.forEach(el =>{
let target = $(el),
chain = style == 'default' ? target.parent().attr('json-path') : target.siblings('.json-key').attr('json-path')
if(!chain){
return
}
let text = target.text()
if(!text.toLowerCase().includes(filter.toLowerCase())){
return
}
chainSet.add(chain)
while(chain = chain.substr(0, chain.lastIndexOf('.'))){
chainSet.add(chain)
}
})
console.log(chainSet)
style == 'default' ? $('#formatContainer li').addClass('hidden') : $('.json-key').parent().addClass('hidden')
chainSet.forEach(chain => {
style == 'default' ? $(`#formatContainer *[json-path="${chain}"]`).removeClass('hidden')
: $(`#formatContainer *[json-path="${chain}"]`).parent().removeClass('hidden')
})
},
// JSON 过滤
input: function(){
let that = this
$('input').on('input', function(){
let val = $(this).val()
val === '' ? $('.clear').attr('hidden', true) : $('.clear').attr('hidden', false)
that.filterJSON(val)
})
return that
},
// 清空输入框内容
clear: function(){
let that = this
$('.clear').click(function(){
that.filterJSON()
$('input').val('')
$(this).attr('hidden', true)
})
return this
},
// 返回顶部
scrollTop: function(){
$('.scroll-top').click(function(){
$('.tabs-container').animate({
scrollTop: '0'
}, 1000);
})
$('.tabs-container').scroll(function(e) {
let scrollTop = $(this).scrollTop()
scrollTop > 500 ? $('.scroll-top').fadeIn() : $('.scroll-top').fadeOut()
});
return this
},
// 所有a标签,看是否是图片,是图片生成预览图
urlHover:function(){
$("#formatContainer").on({
mouseenter: function(){
var that = $(this),
href = that.attr('href')
if(Utils.isImg(href)){
layer.tips(`
`, that, {
time: 0,
anim: 5,
maxWidth: 500,
tips: [3, '#d9d9d9']
});
}
},
mouseleave: () => layer.closeAll()
}, 'a[href]')
return this
},
// 提示key的JSONPath
tipsJsonPath: function(){
var that = this
$("#formatContainer").on({
mouseenter: function(){
let jsonPath = that.getJsonPath(this)
layer.tips(jsonPath, this, {
time: 0,
anim: 5,
maxWidth: 500,
tips: [1, '#2e59a7']
})
},
mouseleave: () => layer.closeAll()
}, '.json-key')
return this
},
// 单击key复制JSONPath
copyJsonPath: function(){
var that = this
$("#formatContainer").on('click', '.json-key', function(event){
if(event.ctrlKey){
let jsonPath = that.getJsonPath(this)
GM_setClipboard(jsonPath)
layer.msg('复制成功', {time: 1500})
}
})
return this
},
getJsonPath: function(element){
let style = GM_getValue('formatStyle') || 'default'
let jsonPath = style == 'default' ? $(element).parent().attr('json-path') : $(element).attr('json-path')
return jsonPath.split('.').reduce((prev, next) => /^\d+$/.test(next) ? prev + `[${next}]` : prev + '.' + next)
},
init:function(){
this.input().clear().scrollTop().urlHover().tipsJsonPath().copyJsonPath()
}
},
theme = {
// 切换主题
changeTheme: function(){
let that = this
$('.theme select').change(function(e){
let val = $(e.target).val()
GM_setValue('theme', val)
that.setTheme()
})
return this
},
// 设置主题
setTheme: function(){
let theme = GM_getValue('theme') || 'default'
$('body').removeClass().addClass(theme)
$('.theme select').val(theme)
return this
},
init:function(){
this.setTheme().changeTheme()
}
},
formatStyle = {
// 切换风格
changeStyle: function(json){
let that = this
$('.formatStyle select').change(function(e){
let val = $(e.target).val()
GM_setValue('formatStyle', val)
that.setStyle(json)
})
return this
},
// 设置风格
setStyle: function(json){
let style = GM_getValue('formatStyle') || 'default'
$('.formatStyle select').val(style)
$('input').val('')
$('#formatContainer').html('')
if(style === 'default'){
$('#formatContainer').jsonViewer(json, jsonpFun)
}else{
let trHtml= treeTableHtml(json)
$('#formatContainer').append(``)
$('#treeTable').simpleTreeTable({
expander: '#expandAll',
collapser: '#collapseAll',
})
// Highlight selected row
$('#treeTable').on("mousedown", "tr", function() {
$(".selected").not(this).removeClass("selected");
$(this).toggleClass("selected")
})
}
return this
},
init:function(json){
this.setStyle(json).changeStyle(json)
theme.init()
btnEvent.init()
otherOperate.init()
jsonMind.init(json)
}
}
formatStyle.init(jsonObject)
/**
* 表格
*/
function treeTableHtml(json, level = 0, pId = '', pChain = ''){
let tr = ''
for(let key in json){
let val = json[key],
type = Utils.getType(val),
tId = key + '_' + Math.random(),
chain = pChain + "." + key
if(['array', 'object'].includes(type)){
let brackets = '',
len = val.length,
res = treeTableHtml(val, level + 1, tId, chain)
if(!res){
if(type ==='array'){
brackets = `[]`
}else{
brackets = `{}`
}
}
tr += `
${key}: |
${brackets} |
`
tr += res
}else{
val = (type === 'string') ? val.replace(/&/g, '&').replace(//g, '>') : val
tr += `
${key}: | `
if (Utils.isUrl(val)){
tr += `"${val}" | `
}else{
val = (type === 'string') ? `"${val}"`: val
tr += `${val} | `
}
tr += '
'
}
}
return tr;
}
})(_jQuery)
})();