JSON Viewer

格式化显示JSON使数据看起来更加漂亮,支持折叠/展开格式化后的数据,支持JSON脑图让调用层级看着更清晰,支持复制JSON脑图节点路径

当前为 2024-10-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @license MIT
  3. // @name JSON Viewer
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.5.5
  6. // @note v0.5.5 解决@require jquery-simple-tree-table.min.js依赖加载失败问题
  7. // @note v0.5.4 单击复制修改为CTRL+单击复制JSONPath功能,JSON格式化风格增加table格式
  8. // @note v0.5.3 增加暗黑主题色
  9. // @note v0.5.2 单击JSON格式化的key可复制JSONPath
  10. // @note v0.5.1 修复JSONPath提示有误
  11. // @note v0.5.0 解决chrome 120+以上内核JSON格式化不执行和引入layer报错问题
  12. // @note v0.4.9 布局修改,增加保存JSON/脑图为文件,增加JSON过滤,鼠标移入key提示JSONPath
  13. // @note v0.4.8 代码优化
  14. // @note v0.4.7 增加对JSONP的判断,代码优化
  15. // @note v0.4.6 增加复制按钮,JSON脑图CSS样式细节优化,JSON脑图增加收起/展开子节点按钮
  16. // @note v0.4.5 在json-viewer-updated原基础上进行了一些修改,主要有CSS样式修改,新增折叠/展开全部功能,新增JSON脑图功能,脑图节点点击显示调用路径
  17. // @description 格式化显示JSON使数据看起来更加漂亮,支持折叠/展开格式化后的数据,支持JSON脑图让调用层级看着更清晰,支持复制JSON脑图节点路径
  18. // @author Feny
  19. // @match *://*/*
  20. // @grant GM_addStyle
  21. // @grant GM_getValue
  22. // @grant GM_setValue
  23. // @grant unsafeWindow
  24. // @grant GM_setClipboard
  25. // @grant GM_getResourceText
  26. // @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==
  27. // @require https://code.jquery.com/jquery.min.js
  28. // @require https://unpkg.com/jsmind@0.8.5/es6/jsmind.js
  29. // @require https://unpkg.com/dom-to-image@2.6.0/dist/dom-to-image.min.js
  30. // @require https://unpkg.com/jsmind@0.8.5/es6/jsmind.screenshot.js
  31. // @resource swalStyle https://unpkg.com/jsmind@0.8.5/style/jsmind.css
  32. // ==/UserScript==
  33.  
  34.  
  35. /**
  36. * jquery-simple-tree-table.min.js
  37. * https://cdn.jsdelivr.net/npm/@kanety/jquery-simple-tree-table@0.5.1/dist/jquery-simple-tree-table.min.js
  38. */
  39. !function(e){var t={};function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(o,i,function(t){return e[t]}.bind(null,i));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=5)}([function(e,t){e.exports=jQuery},function(e,t,n){var o=n(2),i=n(3);"string"==typeof(i=i.__esModule?i.default:i)&&(i=[[e.i,i,""]]);var a={insert:"head",singleton:!1};o(i,a);e.exports=i.locals||{}},function(e,t,n){"use strict";var o,i=function(){return void 0===o&&(o=Boolean(window&&document&&document.all&&!window.atob)),o},a=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),r=[];function s(e){for(var t=-1,n=0;n<r.length;n++)if(r[n].identifier===e){t=n;break}return t}function c(e,t){for(var n={},o=[],i=0;i<e.length;i++){var a=e[i],c=t.base?a[0]+t.base:a[0],l=n[c]||0,u="".concat(c," ").concat(l);n[c]=l+1;var d=s(u),f={css:a[1],media:a[2],sourceMap:a[3]};-1!==d?(r[d].references++,r[d].updater(f)):r.push({identifier:u,updater:y(f,t),references:1}),o.push(u)}return o}function l(e){var t=document.createElement("style"),o=e.attributes||{};if(void 0===o.nonce){var i=n.nc;i&&(o.nonce=i)}if(Object.keys(o).forEach((function(e){t.setAttribute(e,o[e])})),"function"==typeof e.insert)e.insert(t);else{var r=a(e.insert||"head");if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}return t}var u,d=(u=[],function(e,t){return u[e]=t,u.filter(Boolean).join("\n")});function f(e,t,n,o){var i=n?"":o.media?"@media ".concat(o.media," {").concat(o.css,"}"):o.css;if(e.styleSheet)e.styleSheet.cssText=d(t,i);else{var a=document.createTextNode(i),r=e.childNodes;r[t]&&e.removeChild(r[t]),r.length?e.insertBefore(a,r[t]):e.appendChild(a)}}function h(e,t,n){var o=n.css,i=n.media,a=n.sourceMap;if(i?e.setAttribute("media",i):e.removeAttribute("media"),a&&btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleSheet)e.styleSheet.cssText=o;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(o))}}var p=null,v=0;function y(e,t){var n,o,i;if(t.singleton){var a=v++;n=p||(p=l(t)),o=f.bind(null,n,a,!1),i=f.bind(null,n,a,!0)}else n=l(t),o=h.bind(null,n,t),i=function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)};return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else i()}}e.exports=function(e,t){(t=t||{}).singleton||"boolean"==typeof t.singleton||(t.singleton=i());var n=c(e=e||[],t);return function(e){if(e=e||[],"[object Array]"===Object.prototype.toString.call(e)){for(var o=0;o<n.length;o++){var i=s(n[o]);r[i].references--}for(var a=c(e,t),l=0;l<n.length;l++){var u=s(n[l]);0===r[u].references&&(r[u].updater(),r.splice(u,1))}n=a}}}},function(e,t,n){(t=n(4)(!1)).push([e.i,".simple-tree-table-icon{display:inline-block;width:1.5em;line-height:1.5em;margin:0.1em;background-color:#eee;text-align:center;cursor:pointer}.simple-tree-table-opened .simple-tree-table-icon:after{content:'-'}.simple-tree-table-closed .simple-tree-table-icon:after{content:'+'}\n",""]),e.exports=t},function(e,t,n){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"==typeof btoa){var i=(r=o,s=btoa(unescape(encodeURIComponent(JSON.stringify(r)))),c="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(s),"/*# ".concat(c," */")),a=o.sources.map((function(e){return"/*# sourceURL=".concat(o.sourceRoot||"").concat(e," */")}));return[n].concat(a).concat([i]).join("\n")}var r,s,c;return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n})).join("")},t.i=function(e,n,o){"string"==typeof e&&(e=[[null,e,""]]);var i={};if(o)for(var a=0;a<this.length;a++){var r=this[a][0];null!=r&&(i[r]=!0)}for(var s=0;s<e.length;s++){var c=[].concat(e[s]);o&&i[c[0]]||(n&&(c[2]?c[2]="".concat(n," and ").concat(c[2]):c[2]=n),t.push(c))}},t}},function(e,t,n){"use strict";n.r(t);var o=n(0),i=n.n(o);function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}function s(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}var c=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a(this,e),this.opts={type:t.type||"session",key:t.key},this.inst=new l(this.opts)}return s(e,[{key:"get",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.inst.get(this.opts.key)||e}},{key:"set",value:function(e){this.inst.set(this.opts.key,e)}},{key:"remove",value:function(){this.inst.remove(this.opts.key)}}]),e}(),l=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a(this,e),this.storage={local:window.localStorage,session:window.sessionStorage}[t.type]}return s(e,[{key:"get",value:function(e){try{var t=this.storage.getItem(e);return t?JSON.parse(t):null}catch(e){return console.log(e),null}}},{key:"set",value:function(e,t){try{this.storage.setItem(e,JSON.stringify(t))}catch(e){console.log(e)}}},{key:"remove",value:function(e){this.storage.removeItem(e)}}]),e}(),u=(n(1),"simple-tree-table");function d(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function f(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}var h={expander:null,collapser:null,opened:"all",margin:20,iconPosition:"> :first-child",iconTemplate:"<span />",store:null,storeKey:null},p=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};d(this,e),this.options=i.a.extend({},h,n),this.$table=i()(t),this.$expander=i()(this.options.expander),this.$collapser=i()(this.options.collapser),this.options.store&&this.options.storeKey&&(this.store=new c({type:this.options.store,key:this.options.storeKey})),this.init(),this.load()}var t,n,o;return t=e,o=[{key:"getDefaults",value:function(){return h}},{key:"setDefaults",value:function(e){return i.a.extend(h,e)}}],(n=[{key:"init",value:function(){this.$table.addClass(u),this.build(),this.unbind(),this.bind()}},{key:"destroy",value:function(){var e=function(e,t){var n=new RegExp("".concat(u,"(-\\S+)?"),"g");return(t.match(n)||[]).join(" ")};this.$table.removeClass(e),this.nodes().removeClass(e),this.$table.find(".".concat(u,"-icon")).remove(),this.unbind()}},{key:"build",value:function(){var e=this;this.nodes().not("[data-node-depth]").each((function(t,n){var o=i()(n),a=e.depth(o);o.data("node-depth",a),1==a&&o.addClass("".concat(u,"-root"))})),this.nodes().filter((function(t,n){return 0==i()(n).find(e.options.iconPosition).find(".".concat(u,"-handler")).length})).each((function(t,n){var o=i()(n),a=e.depth(o),r=e.options.margin*(a-1),s=i()(e.options.iconTemplate).addClass("".concat(u,"-handler ").concat(u,"-icon")).css("margin-left","".concat(r,"px"));o.find(e.options.iconPosition).prepend(s)})),this.nodes().not(".".concat(u,"-empty, .").concat(u,"-opened, .").concat(u,"-closed")).each((function(t,n){var o=i()(n);e.hasChildren(o)?e.opensDefault(o)?o.addClass("".concat(u,"-opened")):o.addClass("".concat(u,"-closed")):o.addClass("".concat(u,"-empty"))})),this.nodes().filter(".".concat(u,"-opened")).each((function(t,n){e.show(i()(n))})),this.nodes().filter(".".concat(u,"-closed")).each((function(t,n){e.hide(i()(n))}))}},{key:"opensDefault",value:function(e){var t=this.options.opened;return t&&("all"==t||-1!=t.indexOf(e.data("node-id")))}},{key:"bind",value:function(){var e=this;this.$expander.on("click.".concat(u),(function(t){e.expand()})),this.$collapser.on("click.".concat(u),(function(t){e.collapse()})),this.$table.on("click.".concat(u),"tr .".concat(u,"-handler"),(function(t){var n=i()(t.currentTarget).closest("tr");n.hasClass("".concat(u,"-opened"))?e.close(n):e.open(n)}))}},{key:"unbind",value:function(){this.$expander.off(".".concat(u)),this.$collapser.off(".".concat(u)),this.$table.off(".".concat(u," node:open node:close"))}},{key:"expand",value:function(){var e=this;this.nodes().each((function(t,n){e.show(i()(n))})),this.save()}},{key:"collapse",value:function(){var e=this;this.nodes().each((function(t,n){e.hide(i()(n))})),this.save()}},{key:"nodes",value:function(){return this.$table.find("tr[data-node-id]")}},{key:"depth",value:function(e){var t=e.data("node-depth");if(t)return t;var n=this.findByID(e.data("node-pid"));return 0!=n.length?this.depth(n)+1:1}},{key:"open",value:function(e){this.show(e),this.save(),e.trigger("node:open",[e])}},{key:"show",value:function(e){e.hasClass("".concat(u,"-empty"))||(e.removeClass("".concat(u,"-closed")).addClass("".concat(u,"-opened")),this.showDescs(e))}},{key:"showDescs",value:function(e){var t=this;this.findChildren(e).each((function(e,n){var o=i()(n);o.show(),o.hasClass("".concat(u,"-opened"))&&t.showDescs(o)}))}},{key:"close",value:function(e){this.hide(e),this.save(),e.trigger("node:close",[e])}},{key:"hide",value:function(e){e.hasClass("".concat(u,"-empty"))||(e.removeClass("".concat(u,"-opened")).addClass("".concat(u,"-closed")),this.hideDescs(e))}},{key:"hideDescs",value:function(e){var t=this;this.findChildren(e).each((function(e,n){var o=i()(n);o.hide(),t.hideDescs(o)}))}},{key:"hasChildren",value:function(e){return 0!=this.findChildren(e).length}},{key:"findChildren",value:function(e){var t=e.data("node-id");return this.$table.find('tr[data-node-pid="'.concat(t,'"]'))}},{key:"findDescendants",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],o=this.findChildren(e);return n.push(o),o.each((function(e,o){t.findDescendants(i()(o),n)})),n}},{key:"findByID",value:function(e){return this.$table.find('tr[data-node-id="'.concat(e,'"]'))}},{key:"openByID",value:function(e){this.open(this.findByID(e))}},{key:"closeByID",value:function(e){this.close(this.findByID(e))}},{key:"load",value:function(){var e=this;if(this.store){var t=this.store.get();t&&(this.nodes().each((function(t,n){e.show(i()(n))})),this.nodes().filter((function(e,n){return-1!=t.indexOf(i()(n).data("node-id"))})).each((function(t,n){e.hide(i()(n))})))}}},{key:"save",value:function(){if(this.store){var e=this.nodes().filter(".".concat(u,"-closed")).map((function(e,t){return i()(t).data("node-id")})).get();this.store.set(e)}}}])&&f(t.prototype,n),o&&f(t,o),e}();i.a.fn.simpleTreeTable=function(e){return this.each((function(t,n){var o=i()(n);o.data(u)&&o.data(u).destroy(),o.data(u,new p(o,e))}))},i.a.SimpleTreeTable=p}]);
  40.  
  41. (function() {
  42. 'use strict';
  43.  
  44. const Utils = {
  45. // 检查字符串是否为URL
  46. isUrl: function (string) {
  47. var regexp = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
  48. return regexp.test(string);
  49. },
  50.  
  51. // 检查是否是图片链接
  52. isImg: function (pathImg) {
  53. // var regexp = /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?\/([\w#!:.?+=&%@!\-\/])*\.(gif|jpg|jpeg|png|GIF|JPG|PNG)([\w#!:.?+=&%@!\-\/])?/;
  54. let regexp = /\.(ico|bmp|gif|jpg|jpeg|png|svg|webp|GIF|JPG|PNG|WEBP|SVG)([\w#!:.?+=&%@!\-\/])?/i
  55. return regexp.test(pathImg);
  56. },
  57. // 检验内容是否是json格式的内容
  58. isJSON: function (str) {
  59. try {
  60. JSON.parse(str)
  61. return true
  62. } catch(e) {
  63. console.log("is not json", e)
  64. return false
  65. }
  66. },
  67. // 获取数据类型
  68. getType: function (value){
  69. return Object.prototype.toString.call(value).match(/\s(.+)]/)[1].toLowerCase();
  70. },
  71. // JSON 过滤
  72. filterJson: function(json, filter) {
  73. if(!filter){
  74. return json
  75. }
  76.  
  77. filter = filter.toLowerCase()
  78. let newJSON = Utils.getType(json) == 'array' ? [] : {}
  79. for (let key in json) {
  80. let val = json[key]
  81. if (typeof val === 'object') {
  82. let subJSON = this.filterJson(val, filter);
  83. if (Object.keys(subJSON).length > 0) {
  84. newJSON[key] = subJSON;
  85. }
  86. }else{
  87. if(key.toLowerCase().includes(filter.toLowerCase())){
  88. newJSON[key] = val
  89. }
  90.  
  91. if(val !== null && val.toString().toLowerCase().includes(filter.toLowerCase())){
  92. newJSON[key] = val
  93. }
  94. }
  95. }
  96. return newJSON;
  97. }
  98. }
  99.  
  100. // jquery.json-viewer 插件 开始
  101. // 解决和原网页jquery版本冲突
  102. var _jQuery = jQuery.noConflict(true);
  103. (function($){
  104. /**
  105. * 检查 arg 是否为至少包含 1 个元素的数组或至少包含 1 个键的字典
  106. */
  107. function isCollapsable(arg) {
  108. return arg instanceof Object && Object.keys(arg).length > 0;
  109. }
  110.  
  111. /**
  112. * 将 JSON 对象转换为 HTML 表示形式
  113. * @return string
  114. */
  115. function json2html(json, parentPath = '') {
  116. let html = '', type = Utils.getType(json)
  117. switch(type){
  118. case 'array':
  119. case 'object':
  120. let len = json.length || Object.keys(json).length;
  121. if (len > 0) {
  122. html += `<span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">`;
  123. html += type === 'array' ? '[</span><ol class="json-array">' : '{</span><ul class="json-object">';
  124. for (var key in json) {
  125. if (json.hasOwnProperty(key)) {
  126. let comma = --len > 0 ? ',' : '',
  127. jsonPath = parentPath + '.' + key,
  128. collapse = isCollapsable(json[key]) ? '<a href class="json-toggle"></a>' : '',
  129. res = json2html(json[key], jsonPath)
  130. let toHtml = type === 'array' ? res : `<span class="json-key">"${key}"</span>: ${res}`
  131. html += [`<li json-path="${jsonPath}">`, collapse, toHtml, comma, '</li>'].join('')
  132. }
  133. }
  134.  
  135. if(type === 'array'){
  136. html += `</ol><span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">]</span>`
  137. }else{
  138. html += `</ul><span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">}</span>`
  139. }
  140. }else{
  141. html += `<span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">`
  142. html += (type === 'array') ? '[]' : '{}'
  143. html += '</span>'
  144. }
  145. break
  146. default:
  147. /* Escape tags */
  148. json = type === 'string' ? json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') : json
  149. if (Utils.isUrl(json)){
  150. html += `<a target="_blank" href="${json}" class="json-string">"${json}"</a>`;
  151. }else{
  152. json = type === 'string' ? `"${json}"` : json
  153. html += `<span class="json-${type}">${json}</span>`;
  154. }
  155. break
  156. }
  157. return html;
  158. }
  159.  
  160. $.fn.jsonViewer = function(json, jsonpFun) {
  161. return this.each(function() {
  162. /* Transform to HTML */
  163. var html = json2html(json);
  164. /** is JSONP */
  165. if(jsonpFun !== undefined && jsonpFun !== null){
  166. html = `<div>${jsonpFun}(</div>${html}<div>)</div>`
  167. }
  168. /* Insert HTML in target DOM element */
  169. $(this).html(html);
  170.  
  171. /* Bind click on toggle buttons */
  172. $(this).off('click');
  173. $(this).on('click', 'a.json-toggle', function() {
  174. var target = $(this).toggleClass('collapsed').siblings('ul.json-object, ol.json-array');
  175. target.toggle();
  176. if (target.is(':visible')) {
  177. target.siblings('.json-placeholder').remove();
  178. }else {
  179. var count = target.children('li:not([class*="hidden"])').length;
  180. var placeholder = count + (count > 1 ? ' items' : ' item');
  181. target.after('<a href class="json-placeholder">' + placeholder + '</a>');
  182. }
  183. return false;
  184. });
  185.  
  186. /* Simulate click on toggle button when placeholder is clicked */
  187. $(this).on('click', 'a.json-placeholder', function() {
  188. $(this).siblings('a.json-toggle').click();
  189. $(this).siblings('a.json-placeholder').remove();
  190. return false;
  191. });
  192. });
  193. };
  194. })(_jQuery);
  195. // jquery.json-viewer 插件 结束
  196.  
  197. (function($){
  198. var source = $('pre').first();
  199. if(source.length === 0){
  200. return
  201. }
  202. let rawText = source.text()
  203. if(!rawText){
  204. return
  205. }
  206.  
  207. // 判断是否为jsonp格式
  208. let jsonpFun = null, tokens = rawText.match(/^([^\s(]*)\s*\(([\s\S]*)\)\s*;?$/)
  209. if (tokens && tokens[1] && tokens[2]) {
  210. jsonpFun = tokens[1]
  211. rawText= tokens[2]
  212. }
  213.  
  214. if(!Utils.isJSON(rawText)){
  215. return
  216. }
  217.  
  218. // 随机rgb颜色
  219. let rgbaColor = `${Math.random()*256}, ${Math.random()*256}, ${Math.random()*256}`
  220. // 添加样式
  221. GM_addStyle(GM_getResourceText('swalStyle'))
  222. $("head").append(`<link rel="stylesheet" type="text/css" href="https://unpkg.com/layer-src@3.5.1/dist/theme/default/layer.css"/>`)
  223. .append('<script src="https://unpkg.com/layui@2.9.17/dist/layui.js" type="text/javascript" ></script>')
  224.  
  225. GM_addStyle(`
  226. body, html{
  227. margin: 0;
  228. padding: 0;
  229. font-size: 14px;
  230. }
  231. td{
  232. font-size: 14px;
  233. }
  234. li::marker {
  235. content: '';
  236. }
  237. .hidden{
  238. display: none !important;
  239. }
  240. .scroll-top{
  241. width: 48px;
  242. height: 48px;
  243. z-index: 999;
  244. position: fixed;
  245. right: 30px;
  246. bottom: 30px;
  247. display: none;
  248. 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=)
  249. }
  250. /** 工具栏样式 START **/
  251. .flex-container{
  252. z-index: 10;
  253. position: fixed;
  254. width: 100vw;
  255. height: 100vh;
  256. display: flex;
  257. flex-direction: column;
  258. }
  259. .tabs, .toolbar{
  260. display: flex;
  261. line-height: 28px;
  262. background: #f3f3f3;
  263. border-bottom: 1px solid #e0e0e2;
  264. }
  265. .toolbar{
  266. line-height: 23px;
  267. }
  268. .searchbox{
  269. display: flex;
  270. flex-grow: 1;
  271. }
  272. .toolbar input{
  273. flex-grow: 1;
  274. border: none;
  275. outline: none;
  276. font-size: 12px;
  277. padding-left: 23px;
  278. background-size: 12px;
  279. background-repeat: no-repeat;
  280. background-position: 7px center;
  281. background-image: url(data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIGZpbGw9InJnYmEoMTM1LCAxMzUsIDEzNywgMC45KSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTIgMTIiPgogIDxwYXRoIGZpbGw9ImNvbnRleHQtZmlsbCIgb3BhY2l0eT0iLjQiIGQ9Ik01IDkuMmwyIDEuNlY2LjFMOC41NSA0aC01LjFMNSA2LjF2My4xeiIvPgogIDxwYXRoIGZpbGw9ImNvbnRleHQtZmlsbCIgZD0iTTEuMTggMi42QTEgMSAwIDAgMSAyIDFIMTBhMSAxIDAgMCAxIC44IDEuNkw4IDYuNHY0LjgyYzAgLjYzLS43Mi45OC0xLjIyLjZsLTIuNS0xLjk5QS43NS43NSAwIDAgMSA0IDkuMjVWNi40MUwxLjE4IDIuNnpNMiAyTDUgNi4wOXYzLjA0bDIgMS41OVY2LjA5TDEwLjAxIDJIMnoiLz4KPC9zdmc+Cg==);
  282. }
  283. .clear {
  284. flex: 0 0 auto;
  285. align-self: center;
  286. margin: 0 4px;
  287. padding: 0;
  288. border: 0;
  289. width: 16px;
  290. height: 16px;
  291. background-color: transparent;
  292. background-image: url(data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIGZpbGw9ImNvbnRleHQtZmlsbCIgZmlsbC1vcGFjaXR5PSJjb250ZXh0LWZpbGwtb3BhY2l0eSI+CiAgPHBhdGggZD0iTTYuNTg2IDhsLTIuMjkzIDIuMjkzYTEgMSAwIDAgMCAxLjQxNCAxLjQxNEw4IDkuNDE0bDIuMjkzIDIuMjkzYTEgMSAwIDAgMCAxLjQxNC0xLjQxNEw5LjQxNCA4bDIuMjkzLTIuMjkzYTEgMSAwIDEgMC0xLjQxNC0xLjQxNEw4IDYuNTg2IDUuNzA3IDQuMjkzYTEgMSAwIDAgMC0xLjQxNCAxLjQxNEw2LjU4NiA4ek04IDBhOCA4IDAgMSAxIDAgMTZBOCA4IDAgMCAxIDggMHoiLz4KPC9zdmc+Cg==);
  293. }
  294. .tabs-item{
  295. border-top: 2px solid #f3f3f3;
  296. }
  297. .tabs-item:hover{
  298. background: #e9e9e9;
  299. border-top: 2px solid #c3c3c6;
  300. }
  301. .tabs-item, .toolbar-item{
  302. cursor: pointer;
  303. padding: 0 10px;
  304. font-size: 12px;
  305. }
  306. .tabs-item.active{
  307. color: #0060df;
  308. border-top: 2px solid #0060df !important;
  309. background: #e9e9e9;
  310. }
  311.  
  312. .tabs-item:hover, .toolbar-item:hover{
  313. background: #e9e9e9;
  314. }
  315. /** 工具栏样式 END **/
  316. /** JSON 格式化样式 START **/
  317. ul.json-object,
  318. ul.json-array {
  319. list-style-type: none;
  320. margin: 0 0 0 2px;
  321. border-left: 1px dotted #5D6D7E;
  322. padding-left: 24px;
  323. }
  324. .json-brackets {
  325. font-weight: 700;
  326. }
  327. .json-key {
  328. /* color: #A31515;*/
  329. color: #910F93;
  330. cursor: pointer;
  331. }
  332. .json-string, .json-string a{
  333. /* color: #0b7500;*/
  334. color: #4B8A4C;
  335. }
  336. .json-number{
  337. /* color: #164FF0;*/
  338. color: #1a01cc;
  339. font-weight: 600;
  340. }
  341. .json-boolean{
  342. color: #905;
  343. font-weight: 600;
  344. }
  345. .json-null {
  346. /* color: #F1592A;*/
  347. color: #0031BC;
  348. font-weight: 600;
  349. }
  350. a.json-toggle {
  351. position: rElative;
  352. color: inherit;
  353. opacity: 0.2;
  354. text-decoration: none;
  355. }
  356. a.json-toggle:hover {
  357. opacity: 0.35;
  358. }
  359. a.json-toggle:active {
  360. opacity: 0.5;
  361. }
  362. a.json-toggle:focus {
  363. outline: none;
  364. }
  365. a.json-toggle:before {
  366. top: 2.5px;
  367. left: -15px;
  368. position: absolute;
  369. content: "";
  370. display: block;
  371. width: 0;
  372. height: 0;
  373. border-style: solid;
  374. border-width: 5px 0 5px 8px;
  375. border-color: transparent transparent transparent currentColor;
  376. transform: rotate(90deg);
  377. }
  378. a.json-toggle.collapsed:before {
  379. transform: rotate(0deg);
  380. }
  381. a.json-placeholder {
  382. color: #aaa;
  383. font-size: 12px;
  384. padding: 0 1em;
  385. text-decoration: none;
  386. }
  387. a.json-placeholder:hover {
  388. text-decoration: underline;
  389. }
  390. .json-curly-brackets {
  391. color: #6D9331;
  392. }
  393. .json-square-brackets{
  394. color: #8E9331;
  395. }
  396. /** 暗黑主题 START **/
  397. body.dark{
  398. background: #333333;
  399. }
  400. .dark li, .dark pre{
  401. color: #CCC;
  402. }
  403. .dark .json-toggle {
  404. opacity: 0.35;
  405. }
  406. .dark .json-toggle:hover {
  407. opacity: 0.5;
  408. }
  409. .dark .json-curly-brackets {
  410. color: #CE70D6;
  411. }
  412. .dark .json-square-brackets{
  413. color: #F1D700;
  414. }
  415. .dark .json-key {
  416. color: #7CDCFE;
  417. }
  418. .dark .json-string, .dark .json-string a{
  419. color: #CE9178;
  420. }
  421. .dark .json-number{
  422. color: #B5CEA8;
  423. }
  424. .dark .json-boolean{
  425. color: #569CD6;
  426. }
  427. .dark .json-null {
  428. color: #2D7AD6;
  429. }
  430. .dark jmnode {
  431. color: #7CDCFE !important;
  432. }
  433. /** 暗黑主题 END **/
  434. /** JSON 格式化样式 END **/
  435. /** 脑图样式 START **/
  436. #jmContainer{
  437. width: 100vw;
  438. height: calc(100vh - 57px);
  439. /* background:#F7F7F7 */
  440. }
  441. jmnode{
  442. display: flex;
  443. align-items: center;
  444. padding: 0 7px 0 22px;
  445. }
  446. jmnode{
  447. color: #475872 !important;
  448. box-shadow: none !important;
  449. background-color: transparent !important;
  450. }
  451. jmnode:hover{
  452. text-shadow: 1px 1px 1px currentColor;
  453. }
  454. jmnode.root {
  455. padding: 0;
  456. color: transparent !important;
  457. }
  458. jmnode:not(.root)::before, jmnode.root::before{
  459. content: " ";
  460. top: 50%;
  461. position: absolute;
  462. border-radius: 50%;
  463. transform: translateY(-50%);
  464. }
  465. jmnode:not(.root)::before{
  466. left: 0;
  467. width: 15px;
  468. height: 15px;
  469. background: rgba(${rgbaColor}, 0.5);
  470. }
  471. jmnode.root::before{
  472. left: 50%;
  473. width: 18px;
  474. height: 18px;
  475. transform: translate(-18px, -50%);
  476. background: rgba(${rgbaColor}, 0.7);
  477. }
  478. jmexpander{
  479. margin-top: 1px;
  480. line-height: 9px;
  481. background: #dfdfdf;
  482. }
  483. .mind-array{
  484. opacity: 0.5;
  485. font-size: 12px;
  486. padding-left: 5px;
  487. }
  488. /** 脑图样式 END **/
  489. /** 容器样式 START **/
  490. .tabs-container{
  491. overflow: auto;
  492. line-height: 1.5;
  493. font-family: monospace;
  494. }
  495. .tabs-container > div{
  496. display: none;
  497. }
  498. .tabs-container > div.active{
  499. display: block;
  500. }
  501. .tabs-container #formatContainer{
  502. padding: 10px;
  503. }
  504. .tabs-container #rawTextContainer{
  505. padding: 0 10px;
  506. }
  507. .tabs-container #rawTextContainer pre{
  508. display: block !important;
  509. overflow-wrap: break-word;
  510. white-space: pre-wrap;
  511. }
  512. /** 容器样式 END **/
  513. .layui-layer-tips{
  514. width: auto !important;
  515. }
  516. .tabs .selectbox{
  517. position: absolute;
  518. right: 200px;
  519. display: flex;
  520. }
  521. /** 表格 START **/
  522. table {
  523. width: -webkit-fill-available;
  524. margin-left: 20px;
  525. border-collapse: collapse;
  526. }
  527. table tr:hover{
  528. background: #f0f9fe;
  529. }
  530. .dark table tr:hover{
  531. background: #353B48
  532. }
  533. table tr.selected td, table tr.selected td a{
  534. color: #fff !important;
  535. background-color: #3875d7;
  536. }
  537. table tbody tr td:first-child{
  538. width: 120px;
  539. }
  540. .simple-tree-table-icon{
  541. color: #000;
  542. opacity: 0.2;
  543. width: 0 !important;
  544. margin: 0 !important;
  545. line-height: 0 !important;
  546. }
  547. .simple-tree-table-icon:hover {
  548. opacity: 0.35;
  549. }
  550. .simple-tree-table-icon:active {
  551. opacity: 0.5;
  552. }
  553. .simple-tree-table-icon:after{
  554. content: "" !important;
  555. }
  556. .simple-tree-table-icon:before {
  557. top: 0.5px;
  558. left: -15px;
  559. position: relative;
  560. content: "";
  561. width: 0;
  562. height: 0;
  563. display: none;
  564. border-style: solid;
  565. border-width: 5px 0 5px 8px;
  566. border-color: transparent transparent transparent currentColor;
  567. transform: rotate(90deg);
  568. }
  569. .simple-tree-table-opened .simple-tree-table-icon:before {
  570. display: block;
  571. }
  572. .simple-tree-table-closed .simple-tree-table-icon:before {
  573. display: block;
  574. transform: rotate(0deg);
  575. }
  576. .dark .simple-tree-table-icon{
  577. color: #FFF;
  578. opacity: 0.5;
  579. }
  580. /** 表格 END **/
  581. `)
  582.  
  583. source.hide()
  584. // 将内容用eval函数处理下
  585. const jsonObject = eval(`(${rawText})`);
  586.  
  587. $("body").addClass('dark').append(`
  588. <div class="scroll-top"></div>
  589. <div class="flex-container">
  590. <div class="panel">
  591. <div class="tabs">
  592. <div class="tabs-item btn active" id="format">JSON格式化</div>
  593. <div class="tabs-item btn" id="viewJsonMind">JSON脑图</div>
  594. <div class="tabs-item btn" id="viewRawText">原始数据</div>
  595. <div class="selectbox">
  596. <div class="formatStyle">
  597. <label>风格:</label>
  598. <select>
  599. <option value="default">默认</option>
  600. <option value="treaTable">表格</option>
  601. </select>
  602. </div>
  603. <div class="theme" style="margin-left: 10px">
  604. <label>主题: </label>
  605. <select>
  606. <option value="default">默认</option>
  607. <option value="dark">暗黑</option>
  608. </select>
  609. </div>
  610. </div>
  611. </div>
  612. <div class="toolbar">
  613. <div class="toolbar-item btn" id="saveJson">保存</div>
  614. <div class="toolbar-item btn" id="copyJson">复制</div>
  615. <div class="toolbar-item btn" id="collapseAll">全部折叠</div>
  616. <div class="toolbar-item btn" id="expandAll">全部展开</div>
  617. <div class="toolbar-item btn" id="beautify" style="display: none">美化输出</div>
  618. <div class="searchbox">
  619. <input type="text" placeholder="过滤 JSON "/>
  620. <button class="clear" hidden></button>
  621. </div>
  622. </div>
  623. </div>
  624. <div class="tabs-container">
  625. <div class="active" id="formatContainer"></div>
  626. <div id="jmContainer"></div>
  627. <div id="rawTextContainer"><pre></pre></div>
  628. </div>
  629. </div>`)
  630.  
  631. let btnEvent = {
  632. isFormater: false,
  633. $rawText: $('#rawTextContainer'),
  634. /**
  635. * 保存为文件
  636. */
  637. download: {
  638. download: function(content, filename) {
  639. const link = document.createElement("a")
  640. link.href = content
  641. link.download = filename
  642. link.click()
  643. },
  644. saveJSON: function (text) {
  645. // 创建一个 Blob 对象,包含要下载的文本内容
  646. const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
  647. const url = URL.createObjectURL(blob)
  648. let filename = new Date().getTime() + '.json';
  649. this.download(url, filename)
  650. URL.revokeObjectURL(url);
  651. },
  652. savePNG: () => jm.shoot(),
  653. },
  654. saveJson:function(){
  655. if($('#jmContainer').is(':visible')){
  656. this.download.savePNG()
  657. }else{
  658. this.download.saveJSON(this.$rawText.text())
  659. }
  660. },
  661. // 复制JSON文本内容
  662. copyJson: function(){
  663. GM_setClipboard(this.$rawText.text())
  664. layer.msg('复制成功', {time: 1500})
  665. },
  666. // 全部折叠
  667. collapseAll: function(){
  668. if($('#formatContainer').is(':visible')){
  669. try{
  670. $('.json-toggle').not('.collapsed').click()
  671. }catch(e){}
  672. }else{
  673. jm.collapse_all()
  674. }
  675. },
  676. // 全部展开
  677. expandAll: function(){
  678. if($('#formatContainer').is(':visible')){
  679. try{
  680. $('a.json-placeholder').click().remove()
  681. }catch(e){}
  682. }else{
  683. jm.expand_all()
  684. jm.scroll_node_to_center(jm.get_root())
  685. }
  686. },
  687. format: function(){},
  688. // 显示JSON脑图
  689. viewJsonMind: function(){},
  690. // 查看原始JSON内容
  691. viewRawText: function(){
  692. this.$rawText.html(source.clone())
  693. },
  694. // 美化
  695. beautify: function(){
  696. this.isFormater = !this.isFormater
  697. if(this.isFormater){
  698. this.$rawText.find('pre').text(JSON.stringify(jsonObject, null, 2))
  699. }else{
  700. this.viewRawText()
  701. }
  702. },
  703. init: function(){
  704. this.viewRawText()
  705.  
  706. // 按钮点击事件
  707. $('.btn').click(e => {
  708. const target = e.target, id = target.id
  709. if(target.classList.contains('tabs-item')){
  710. let index = $(target).index()
  711. $(target).addClass('active').siblings().removeClass("active")
  712. $('.tabs-container > div').removeClass("active").eq(index).addClass('active')
  713.  
  714. let beautifyEl = $('#beautify'),
  715. searchEl= $('.searchbox'),
  716. copyJsonEl= $('#copyJson'),
  717. aEl = $('#collapseAll, #expandAll')
  718. id === 'format' ? searchEl.show(): searchEl.hide()
  719. id === 'viewJsonMind' ? copyJsonEl.hide(): copyJsonEl.show()
  720. id === 'viewRawText' ? (beautifyEl.show() && aEl.hide()) : (beautifyEl.hide() && aEl.show())
  721. }
  722. this[id](target)
  723. })
  724.  
  725. return this
  726. }
  727. },
  728. jsonMind = {
  729. // JSON数据转换为jsMind所需要的数据结构
  730. convert: function(json){
  731. let children = []
  732. if(typeof json === 'object'){
  733. for(let key in json){
  734. let val = json[key],
  735. isArray = Array.isArray(val)
  736.  
  737. children.push({
  738. isArray,
  739. chain: key,
  740. id: key + '_' + Math.random(),
  741. topic: isArray ? `${key}<span class="mind-array">[${val.length}]</span>` : `${key}`,
  742. // children: this.convert(val)
  743. children: isArray ? this.convert(val[0]) : this.convert(val)
  744. })
  745. }
  746. }
  747. return children;
  748. },
  749. // 脑图节点调用链
  750. mindChain: function (node){
  751. let chain = node.data.chain
  752. if(!node.parent){
  753. return chain
  754. }
  755.  
  756. let parent = node.parent, parentChain = this.mindChain(parent)
  757. chain = parent.data.isArray ? `${parentChain}[0].${chain}` : `${parentChain}.${chain}`
  758. return chain
  759. },
  760. // 显示脑图
  761. show: function(json, isArr){
  762. jm.show({
  763. "meta":{
  764. "name":"JSON脑图",
  765. "author":"1220301855@qq.com",
  766. "version":"1.0"
  767. },
  768. "format":"node_tree",
  769. /* 数据内容 */
  770. "data": {
  771. "id": "root",
  772. "topic": 'Response',
  773. "direction": "left",
  774. "children": this.convert(json),
  775. "chain": isArr ? 'Response[0]' : 'Response'
  776. }
  777. })
  778.  
  779. setTimeout(() => jm.scroll_node_to_center(jm.get_root()), 300)
  780. return this
  781. },
  782. // 脑图节点事件
  783. event:function(){
  784. $("jmnode").on('dblclick mouseover mouseout', function(event){
  785. let that = $(this), node = jm.get_node(that.attr('nodeid'))
  786. if(!node.parent){
  787. return
  788. }
  789.  
  790. switch(event.type){
  791. case 'dblclick':
  792. GM_setClipboard(jsonMind.mindChain(node))
  793. layer.msg('节点路径复制成功', {time: 1500})
  794. break;
  795. case 'mouseover':
  796. let s = `<b>节点路径(双击复制)</b><br/>${jsonMind.mindChain(node)}`
  797. layer.tips(s, that, {
  798. time: 0,
  799. tips: [2, '#2e59a7']
  800. });
  801. break;
  802. default:
  803. layer.closeAll()
  804. break;
  805. }
  806. })
  807. return this
  808. },
  809. collapseOrExpand: function(){
  810. $("jmnode").on('click', function(){
  811. let node = jm.get_node($(this).attr('nodeid'))
  812. jm.toggle_node(node)
  813. })
  814. return this
  815. },
  816. init: function(json){
  817. let isArr = Array.isArray(json);
  818. if(isArr){
  819. if(typeof json[0] !== 'object'){
  820. layer.msg('数据结构无法生成脑图', {time: 1000})
  821. return
  822. }
  823. json = json[0]
  824. }
  825.  
  826. if(!window.jm){
  827. window.jm = new jsMind({
  828. mode :'side',
  829. editable: false,
  830. container:'jmContainer',
  831. view: {
  832. hmargin: 50, // 思维导图距容器外框的最小水平距离
  833. vmargin: 50, // 思维导图距容器外框的最小垂直距离
  834. engine: 'svg', // 思维导图各节点之间线条的绘制引擎
  835. draggable: true, // 当容器不能完全容纳思维导图时,是否允许拖动画布代替鼠标滚动
  836. support_html : false,
  837. line_color: '#C4C9D0',
  838. },
  839. layout: {
  840. vspace: 7, // 节点之间的垂直间距
  841. hspace: 150, // 节点之间的水平空间
  842. },
  843. });
  844. }
  845.  
  846. this.show(json, isArr).event().collapseOrExpand()
  847. }
  848. },
  849. otherOperate = {
  850. // 过滤 JSON
  851. filterJSON: function(filter) {
  852. let style = GM_getValue('formatStyle') || 'default'
  853. if(!filter){
  854. style == 'default' ? $('#formatContainer li').removeClass('hidden') : $('.json-key').parent().removeClass('hidden')
  855. return
  856. }
  857.  
  858. let chainSet= new Set()
  859. /**
  860. * 模糊匹配 JSON key
  861. * 假如`filter`值为`id`, querySelectorAll得到DOM节点
  862. * 得到:['.feedList.0.images.0.user_id', '.feedList.0.images.0', '.feedList.0.images', '.feedList.0', '.feedList']
  863. */
  864. document.querySelectorAll('#formatContainer *[json-path]').forEach(el => {
  865. let chain = $(el).attr('json-path')
  866. if(!chain){
  867. return
  868. }
  869. let newChain = chain.substr(chain.lastIndexOf('.'))
  870. if(!newChain.toLowerCase().includes(filter.toLowerCase())){
  871. return
  872. }
  873. chainSet.add(chain)
  874. while(chain = chain.substr(0, chain.lastIndexOf('.'))){
  875. chainSet.add(chain)
  876. }
  877. })
  878.  
  879. /**
  880. * 模糊匹配 JSON value
  881. */
  882. document.querySelectorAll("#formatContainer *[class*='json-']:not([class*='json-key']):not([class*='json-brackets'])")
  883. .forEach(el =>{
  884. let target = $(el),
  885. chain = style == 'default' ? target.parent().attr('json-path') : target.siblings('.json-key').attr('json-path')
  886. if(!chain){
  887. return
  888. }
  889. let text = target.text()
  890. if(!text.toLowerCase().includes(filter.toLowerCase())){
  891. return
  892. }
  893. chainSet.add(chain)
  894. while(chain = chain.substr(0, chain.lastIndexOf('.'))){
  895. chainSet.add(chain)
  896. }
  897. })
  898. console.log(chainSet)
  899. style == 'default' ? $('#formatContainer li').addClass('hidden') : $('.json-key').parent().addClass('hidden')
  900. chainSet.forEach(chain => {
  901. style == 'default' ? $(`#formatContainer *[json-path="${chain}"]`).removeClass('hidden')
  902. : $(`#formatContainer *[json-path="${chain}"]`).parent().removeClass('hidden')
  903. })
  904. },
  905. // JSON 过滤
  906. input: function(){
  907. let that = this
  908. $('input').on('input', function(){
  909. let val = $(this).val()
  910. val === '' ? $('.clear').attr('hidden', true) : $('.clear').attr('hidden', false)
  911. that.filterJSON(val)
  912. })
  913. return that
  914. },
  915. // 清空输入框内容
  916. clear: function(){
  917. let that = this
  918. $('.clear').click(function(){
  919. that.filterJSON()
  920. $('input').val('')
  921. $(this).attr('hidden', true)
  922. })
  923. return this
  924. },
  925. // 返回顶部
  926. scrollTop: function(){
  927. $('.scroll-top').click(function(){
  928. $('.tabs-container').animate({
  929. scrollTop: '0'
  930. }, 1000);
  931. })
  932. $('.tabs-container').scroll(function(e) {
  933. let scrollTop = $(this).scrollTop()
  934. scrollTop > 500 ? $('.scroll-top').fadeIn() : $('.scroll-top').fadeOut()
  935. });
  936. return this
  937. },
  938. // 所有a标签,看是否是图片,是图片生成预览图
  939. urlHover:function(){
  940. $("#formatContainer").on({
  941. mouseenter: function(){
  942. var that = $(this),
  943. href = that.attr('href')
  944. if(Utils.isImg(href)){
  945. layer.tips(`<img src="${href}" />`, that, {
  946. time: 0,
  947. anim: 5,
  948. maxWidth: 500,
  949. tips: [3, '#d9d9d9']
  950. });
  951. }
  952. },
  953. mouseleave: () => layer.closeAll()
  954. }, 'a[href]')
  955. return this
  956. },
  957. // 提示key的JSONPath
  958. tipsJsonPath: function(){
  959. var that = this
  960. $("#formatContainer").on({
  961. mouseenter: function(){
  962. let jsonPath = that.getJsonPath(this)
  963. layer.tips(jsonPath, this, {
  964. time: 0,
  965. anim: 5,
  966. maxWidth: 500,
  967. tips: [1, '#2e59a7']
  968. })
  969. },
  970. mouseleave: () => layer.closeAll()
  971. }, '.json-key')
  972. return this
  973. },
  974. // 单击key复制JSONPath
  975. copyJsonPath: function(){
  976. var that = this
  977. $("#formatContainer").on('click', '.json-key', function(event){
  978. if(event.ctrlKey){
  979. let jsonPath = that.getJsonPath(this)
  980. GM_setClipboard(jsonPath)
  981. layer.msg('复制成功', {time: 1500})
  982. }
  983. })
  984. return this
  985. },
  986. getJsonPath: function(element){
  987. let style = GM_getValue('formatStyle') || 'default'
  988. let jsonPath = style == 'default' ? $(element).parent().attr('json-path') : $(element).attr('json-path')
  989. return jsonPath.split('.').reduce((prev, next) => /^\d+$/.test(next) ? prev + `[${next}]` : prev + '.' + next)
  990. },
  991. init:function(){
  992. this.input().clear().scrollTop().urlHover().tipsJsonPath().copyJsonPath()
  993. }
  994. },
  995. theme = {
  996. // 切换主题
  997. changeTheme: function(){
  998. let that = this
  999. $('.theme select').change(function(e){
  1000. let val = $(e.target).val()
  1001. GM_setValue('theme', val)
  1002. that.setTheme()
  1003. })
  1004. return this
  1005. },
  1006. // 设置主题
  1007. setTheme: function(){
  1008. let theme = GM_getValue('theme') || 'default'
  1009. $('body').removeClass().addClass(theme)
  1010. $('.theme select').val(theme)
  1011. return this
  1012. },
  1013. init:function(){
  1014. this.setTheme().changeTheme()
  1015. }
  1016. },
  1017. formatStyle = {
  1018. // 切换风格
  1019. changeStyle: function(json){
  1020. let that = this
  1021. $('.formatStyle select').change(function(e){
  1022. let val = $(e.target).val()
  1023. GM_setValue('formatStyle', val)
  1024. that.setStyle(json)
  1025. })
  1026. return this
  1027. },
  1028. // 设置风格
  1029. setStyle: function(json){
  1030. let style = GM_getValue('formatStyle') || 'default'
  1031. $('.formatStyle select').val(style)
  1032.  
  1033. $('input').val('')
  1034. $('#formatContainer').html('')
  1035. if(style === 'default'){
  1036. $('#formatContainer').jsonViewer(json, jsonpFun)
  1037. }else{
  1038. let trHtml= treeTableHtml(json)
  1039. $('#formatContainer').append(`<table id="treeTable">${trHtml}</table>`)
  1040. $('#treeTable').simpleTreeTable({
  1041. expander: '#expandAll',
  1042. collapser: '#collapseAll',
  1043. })
  1044.  
  1045. // Highlight selected row
  1046. $('#treeTable').on("mousedown", "tr", function() {
  1047. $(".selected").not(this).removeClass("selected");
  1048. $(this).toggleClass("selected")
  1049. })
  1050. }
  1051. return this
  1052. },
  1053. init:function(json){
  1054. this.setStyle(json).changeStyle(json)
  1055. theme.init()
  1056. btnEvent.init()
  1057. otherOperate.init()
  1058. jsonMind.init(json)
  1059. }
  1060. }
  1061.  
  1062. formatStyle.init(jsonObject)
  1063.  
  1064. /**
  1065. * 表格
  1066. */
  1067. function treeTableHtml(json, level = 0, pId = '', pChain = ''){
  1068. let tr = ''
  1069. for(let key in json){
  1070. let val = json[key],
  1071. type = Utils.getType(val),
  1072. tId = key + '_' + Math.random(),
  1073. chain = pChain + "." + key
  1074. if(['array', 'object'].includes(type)){
  1075. let brackets = '',
  1076. len = val.length,
  1077. res = treeTableHtml(val, level + 1, tId, chain)
  1078.  
  1079. if(!res){
  1080. if(type ==='array'){
  1081. brackets = `<span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">[]</span>`
  1082. }else{
  1083. brackets = `<span class="json-brackets ${type == 'array' ? 'json-square-brackets' : 'json-curly-brackets'}">{}</span>`
  1084. }
  1085. }
  1086.  
  1087. tr += `
  1088. <tr data-node-id="${tId}" data-node-pid="${pId}" type="${type}">
  1089. <td colspan="${len > 0 ? 2 : 0}" class="json-key" json-path="${chain}" style="padding-left: ${ level * 19 }px">${key}:</td>
  1090. <td>${brackets}</td>
  1091. </tr>`
  1092. tr += res
  1093. }else{
  1094. val = (type === 'string') ? val.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') : val
  1095. tr += `<tr data-node-id="${tId}" data-node-pid="${pId}" type="${type}">
  1096. <td class="json-key" json-path="${chain}" style="padding-left: ${ level * 19 }px">${key}:</td>`
  1097. if (Utils.isUrl(val)){
  1098. tr += `<td class="json-${type}"><a target="_blank" href="${val}">"${val}"</a></td>`
  1099. }else{
  1100. val = (type === 'string') ? `"${val}"`: val
  1101. tr += `<td class="json-${type}">${val}</td>`
  1102. }
  1103. tr += '</tr>'
  1104. }
  1105. }
  1106. return tr;
  1107. }
  1108.  
  1109. })(_jQuery)
  1110. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址