/** * Json Page Automatic Format Via FeHelper * @author zhaoxianlie */ // 留100ms时间给静态文件加载,当然,这个代码只是留给未开发过程中用的 let pleaseLetJsLoaded = 0; let __importScript = (filename) => { pleaseLetJsLoaded = 100; let url = filename; if (location.protocol === 'chrome-extension:' || chrome.runtime && chrome.runtime.getURL) { url = chrome.runtime.getURL('json-format/' + filename); } fetch(url).then(resp => resp.text()).then(jsText => { if(window.evalCore && window.evalCore.getEvalInstance){ return window.evalCore.getEvalInstance(window)(jsText); } let el = document.createElement('script'); el.textContent = jsText; document.head.appendChild(el); }); }; __importScript('json-bigint.js'); __importScript('format-lib.js'); __importScript('json-abc.js'); __importScript('json-decode.js'); window.JsonAutoFormat = (() => { "use strict"; const JSON_SORT_TYPE_KEY = 'json_sort_type_key'; const JSON_AUTO_DECODE = 'json_auto_decode'; const JSON_TOOL_BAR_ALWAYS_SHOW = 'JSON_TOOL_BAR_ALWAYS_SHOW'; // 用于记录最原始的json串 let originalJsonStr = ''; let curSortType = 0; // JSONP形式下的callback name let funcName = null; let jsonObj = null; let fnTry = null; let fnCatch = null; let autoDecode = false; let _getHtmlFragment = () => { return [ '', '
格式化中...
', '
', '
', '
', '
',
            '
', '
' ].join('') }; /** * 从页面提取JSON文本 * @returns {string} * @private */ let _getJsonText = function () { let pre = document.querySelectorAll('body>pre')[0] || {textContent: ""}; let source = pre.textContent.trim(); if (!source) { source = (document.body.textContent || '').trim() } if (!source) { return false; } // 1、如果body的内容还包含HTML标签,肯定不是合法的json了 // 2、如果是合法的json,也只可能有一个text节点 // 3、但是要兼容一下其他插件对页面的破坏情况 // 4、对于content-type是application/json的页面可以做宽松处理 let nodes = document.body.childNodes; let jsonText = ''; let isJsonContentType = document.contentType === 'application/json'; for (let i = 0, len = nodes.length; i < len; i++) { let elm = nodes[i]; if (elm.nodeType === Node.TEXT_NODE) { jsonText += (elm.textContent || '').trim(); } else if (isJsonContentType) { if ((elm.offsetHeight + elm.offsetWidth !== 0) && elm.textContent.length > jsonText.length) { jsonText = elm.textContent; } } else { if (nodes[i].nodeType === Node.ELEMENT_NODE) { let tagName = elm.tagName.toLowerCase(); let text = (elm.textContent || '').trim(); // 如果是pre标签,则看内容是不是和source一样,一样则continue if (!((tagName === 'pre' && text === source) || ((elm.offsetWidth + elm.offsetHeight === 0 || !text) && !['script', 'link'].includes(tagName)))) { return false; } } else { return false; } } } return (jsonText || '').trim() || source; }; /** * 获取一个JSON的所有Key数量 * @param json * @returns {number} * @private */ let _getAllKeysCount = function (json) { let count = 0; if (typeof json === 'object') { let keys = Object.keys(json); count += keys.length; keys.forEach(key => { if (json[key] && typeof json[key] === 'object') { count += _getAllKeysCount(json[key]); } }); } return count; }; /** * 执行format操作 * @private */ let _format = function (options) { let source = _getJsonText(); if (!source) { return; } // 下面校验给定字符串是否为一个合法的json try { // 再看看是不是jsonp的格式 let reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/gm; let reTry = /^(try\s*\{\s*)?/g; let reCatch = /([;\s]*\}\s*catch\s*\(\s*\S+\s*\)\s*\{([\s\S])*\})?[;\s]*$/g; // 检测是否有try-catch包裹 let sourceReplaced = source.replace(reTry, function () { fnTry = fnTry ? fnTry : arguments[1]; return ''; }).replace(reCatch, function () { fnCatch = fnCatch ? fnCatch : arguments[1]; return ''; }).trim(); let matches = reg.exec(sourceReplaced); if (matches != null && (fnTry && fnCatch || !fnTry && !fnCatch)) { funcName = matches[1]; source = matches[2]; } else { reg = /^([\{\[])/; if (!reg.test(source)) { return; } } // 这里可能会throw exception jsonObj = JSON.parse(source); } catch (ex) { // new Function的方式,能自动给key补全双引号,但是不支持bigint,所以是下下策,放在try-catch里搞 try { jsonObj = new Function("return " + source)(); } catch (exx) { try { // 再给你一次机会,是不是下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}" jsonObj = new Function("return '" + source + "'")(); if (typeof jsonObj === 'string') { try { // 确保bigint不会失真 jsonObj = JSON.parse(jsonObj); } catch (ie) { // 最后给你一次机会,是个字符串,老夫给你再转一次 jsonObj = new Function("return " + jsonObj)(); } } } catch (exxx) { return; } } } // 是json格式,可以进行JSON自动格式化 if (jsonObj != null && typeof jsonObj === "object") { try { // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理 source = JSON.stringify(jsonObj); } catch (ex) { // 通过JSON反解不出来的,一定有问题 return; } // JSON的所有key不能超过预设的值,比如 10000 个,要不然自动格式化会比较卡 if (options && options['MAX_JSON_KEYS_NUMBER']) { let keysCount = _getAllKeysCount(jsonObj); if (keysCount > options['MAX_JSON_KEYS_NUMBER']) { let msg = '当前JSON共 ' + keysCount + ' 个Key,大于预设值' + options['MAX_JSON_KEYS_NUMBER'] + ',已取消自动格式化;可到FeHelper设置页调整此配置!'; return toast(msg); } } if (window.jsonformatContentScriptCssInject) { window.jsonformatContentScriptCssInject(); } else { // 注入css and html fragment chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing' },(params) => { let injectFn = (cssText) => { chrome.tabs.insertCSS({ code: cssText }); }; let cssText = Awesome.getContentScript('json-format', true); if (typeof cssText === 'string' && cssText.length) { injectFn(cssText); } else if (cssText instanceof Promise) { cssText.then(css => { if (css) { injectFn(css) } else { fetch('../json-format/content-script.css').then(resp => resp.text()).then(css => injectFn(css)); } }); } else if (!cssText) { fetch('../json-format/content-script.css').then(resp => resp.text()).then(css => injectFn(css)); } return true; }); } let preLength = $('body>pre').hide().length; $('body').prepend(_getHtmlFragment()); if (!preLength) { Array.prototype.slice.call(document.body.childNodes).forEach(node => { (node.nodeType === Node.TEXT_NODE) && node.remove(); }); } originalJsonStr = source; // 获取上次记录的排序方式 curSortType = parseInt(localStorage.getItem(JSON_SORT_TYPE_KEY) || 0); _didFormat(curSortType); // 排序选项初始化 $('[name=jsonsort][value=' + curSortType + ']').attr('checked', 1); // 自动解码选项初始化 autoDecode = localStorage.getItem(JSON_AUTO_DECODE); if (autoDecode === null) { autoDecode = (options && options['AUTO_TEXT_DECODE']); } else { autoDecode = autoDecode === 'true'; } $('#json_endecode').prop('checked', autoDecode); _bindEvent(); } }; let _didFormat = function (sortType) { sortType = sortType || 0; let source = originalJsonStr; if (sortType !== 0) { let jsonObj = JsonABC.sortObj(JSON.parse(originalJsonStr), parseInt(sortType), true); source = JSON.stringify(jsonObj); } if (autoDecode) { (async () => { let txt = await JsonEnDecode.urlDecodeByFetch(source); source = JsonEnDecode.uniDecode(txt); // 格式化 Formatter.format(source); $('.x-toolbar').fadeIn(500); })(); } else { // 格式化 Formatter.format(source); $('.x-toolbar').fadeIn(500); } // 如果是JSONP格式的,需要把方法名也显示出来 if (funcName != null) { if (fnTry && fnCatch) { $('#jfCallbackName_start').html('
' + fnTry + '
' + funcName + '('); $('#jfCallbackName_end').html(')
' + fnCatch + '
'); } else { $('#jfCallbackName_start').html(funcName + '('); $('#jfCallbackName_end').html(')'); } } }; let _getCorrectContent = function () { fetch(location.href).then(res => res.text()).then(text => { originalJsonStr = text; _didFormat(curSortType); }); }; let _bindEvent = function () { $('[name=jsonsort]').click(function (e) { let sortType = parseInt(this.value); if (sortType !== curSortType) { _didFormat(sortType); curSortType = sortType; } localStorage.setItem(JSON_SORT_TYPE_KEY, sortType); }); $('#json_endecode').click(function (e) { autoDecode = this.checked; localStorage.setItem(JSON_AUTO_DECODE, autoDecode); _didFormat(curSortType); }); let tgBtn = $('.fe-feedback #toggleBtn').click(function (e) { e.preventDefault(); e.stopPropagation(); chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing' }, (params) => { let show = String(localStorage.getItem(JSON_TOOL_BAR_ALWAYS_SHOW)) !== 'false'; localStorage.setItem(JSON_TOOL_BAR_ALWAYS_SHOW, !show); let toolBarClassList = document.querySelector('div.x-toolbar').classList; if (!show) { toolBarClassList.remove('t-collapse'); tgBtn.html('隐藏>>'); } else { toolBarClassList.add('t-collapse'); tgBtn.html('<<'); } }); }); chrome.runtime.sendMessage({ type: 'fh-dynamic-any-thing' }, params => { let show = String(localStorage.getItem(JSON_TOOL_BAR_ALWAYS_SHOW)) !== 'false'; let toolBarClassList = document.querySelector('div.x-toolbar').classList; if (show) { toolBarClassList.remove('t-collapse'); tgBtn.html('隐藏>>'); } else { toolBarClassList.add('t-collapse'); tgBtn.html('<<'); } }); $('#jsonGetCorrectCnt').click(function (e) { _getCorrectContent(); }); $('#capturePage').click(function (e) { chrome.runtime.sendMessage({ type: 'capture-visible-page' }, uri => { window.open(uri); }); e.preventDefault(); e.stopPropagation(); }); }; return { format: (options) => { setTimeout(() => { _format(options); }, pleaseLetJsLoaded); } }; })();