/** * content_scripts中如果被检测到当前页面内容为json数据,则自动进行JSON格式化 */ baidu.csJsonFormat = (function () { "use strict"; var _htmlFragment = [ '
', '
', '', '', '加载中...', '
', '
', '
', '
',
        '
', '
' ].join(''); var _loadCss = function () { var fcpCss = chrome.extension.getURL('static/css/fe-jsonformat-content.css'); jQuery('').appendTo('head'); }; /** * 从页面提取JSON文本 * @returns {string} * @private */ var _getJsonText = function () { var pre = $('body>pre:eq(0)')[0] || {textContent: ""}; var source = $.trim(pre.textContent); if (!source) { source = $.trim(document.body.textContent || '') } if (!source) { return false; } // 如果body的内容还包含HTML标签,肯定不是合法的json了 // 如果是合法的json,也只可能有一个text节点 var nodes = document.body.childNodes; var newSource = ''; for (var i = 0, len = nodes.length; i < len; i++) { if (nodes[i].nodeType == Node.TEXT_NODE) { newSource += nodes[i].textContent; } else if (nodes[i].nodeType == Node.ELEMENT_NODE) { var tagName = nodes[i].tagName.toLowerCase(); var html = $.trim(nodes[i].textContent); // 如果是pre标签,则看内容是不是和source一样,一样则continue if (tagName === 'pre' && html === source) { continue; } else if ((nodes[i].offsetWidth === 0 || nodes[i].offsetHeight === 0 || !html) && ['script', 'link'].indexOf(tagName) == -1) { // 如果用户安装迅雷或者其他的插件,也回破坏页面结构,需要兼容一下 continue; } else { return false; } } else { return false; } } return $.trim(newSource || '') || source; }; /** * 此方法用于将Unicode码解码为正常字符串 * @param {Object} text */ var _uniDecode = function (text) { text = text.replace(/\\/g, "%").replace('%U', '%u').replace('%u0025', '%25'); text = unescape(text.toString().replace(/%2B/g, "+")); var matches = text.match(/(%u00([0-9A-F]{2}))/gi); if (matches) { for (var matchid = 0; matchid < matches.length; matchid++) { var code = matches[matchid].substring(1, 3); var x = Number("0x" + code); if (x >= 128) { text = text.replace(matches[matchid], code); } } } text = unescape(text.toString().replace(/%2B/g, "+")); return text; }; /** * chrome 下复制到剪贴板 * @param text */ var _copyToClipboard = function (text) { var input = document.createElement('textarea'); input.style.position = 'fixed'; input.style.opacity = 0; input.value = text; document.body.appendChild(input); input.select(); document.execCommand('Copy'); document.body.removeChild(input); alert('Json片段复制成功,随处粘贴可用!') }; /** * 给某个节点增加操作项 * @param el * @private */ var _addOptForItem = function (el) { // 下载json片段 var fnDownload = function (ec) { var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim(); if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) { txt = '{' + txt + '}'; } try { txt = JSON.stringify(JSON.parse(txt), null, 4); } catch (err) { } // 下载片段 var dt = (new Date()).format('yyyyMMddHHmmss'); var blob = new Blob([txt], {type: 'application/octet-stream'}); $(this).attr('download', 'FeHelper-' + dt + '.json').attr('href', URL.createObjectURL(blob)); }; // 复制json片段 var fnCopy = function (ec) { var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim(); if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) { txt = '{' + txt + '}'; } try { txt = JSON.stringify(JSON.parse(txt), null, 4); } catch (err) { } _copyToClipboard(txt); }; // 删除json片段 var fnDel = function (ed) { if (el.parent().is('#formattedJson')) { alert('如果连最外层的Json也删掉的话,就没啥意义了哦!'); return false; } alert('节点已删除成功!'); el.remove(); boxOpt.css('top', -1000).hide(); }; var boxOpt = $('#boxOpt'); if (!boxOpt.length) { boxOpt = $('
下载|复制|删除
').appendTo('body'); } boxOpt.find('a.opt-download').unbind('click').bind('click', fnDownload); boxOpt.find('a.opt-copy').unbind('click').bind('click', fnCopy); boxOpt.find('a.opt-del').unbind('click').bind('click', fnDel); boxOpt.css({ left: el.offset().left + el.width() - 90, top: el.offset().top }).show(); }; /** * 事件绑定 * @private */ var _bindEvent = function () { // 点击区块高亮 $('#jfContent').delegate('.kvov', 'click', function (e) { if($(this).hasClass('x-outline')) { $('#boxOpt').remove(); $(this).removeClass('x-outline'); return false; } $('#jfContent .kvov').removeClass('x-outline'); var el = $(this).removeClass('x-hover').addClass('x-outline'); // 增加复制、删除功能 _addOptForItem(el); if (!$(e.target).is('.kvov .e')) { e.stopPropagation(); } else { $(e.target).parent().trigger('click'); } }).delegate('.kvov', 'mouseover', function (e) { $(this).addClass('x-hover'); return false; }).delegate('.kvov', 'mouseout', function (e) { $(this).removeClass('x-hover'); }); }; /** * 执行format操作 * @private */ var _format = function () { var source = _getJsonText(); if (!source) { return; } // JSONP形式下的callback name var funcName = null; // json对象 var jsonObj = null; var newSource = source; var fnTry = null; var fnCatch = null; // 下面校验给定字符串是否为一个合法的json try { // 再看看是不是jsonp的格式 var reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/gm; var reTry = /^(try\s*\{\s*)?/g; var reCatch = /(\}\s*catch\s*\(\s*\S+\s*\)\s*\{([\s\S])*\})?$/g; // 检测是否有try-catch包裹 var sourceReplaced = source.replace(reTry, function () { fnTry = fnTry ? fnTry : arguments[1]; return ''; }).replace(reCatch, function () { fnCatch = fnCatch ? fnCatch : arguments[1]; return ''; }).trim(); var matches = reg.exec(sourceReplaced); if (matches != null && (fnTry && fnCatch || !fnTry && !fnCatch)) { funcName = matches[1]; newSource = matches[2]; jsonObj = new Function("return " + newSource)(); } else { reg = /^([\{\[])/; if (!reg.test(source)) { return; } } // 强化验证 if (jsonObj == null || typeof jsonObj != 'object') { jsonObj = new Function("return " + source)(); // 还要防止下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}" if (typeof jsonObj == "string") { // 再来一次 jsonObj = new Function("return " + jsonObj)(); } } } catch (ex) { return; } // 是json格式,可以进行JSON自动格式化 if (jsonObj != null && typeof jsonObj == "object") { try { // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理 var jsonStr = JSON.stringify(jsonObj); // 如果newSource的长度比原source长度短很多的话,猜测应该是格式化错了,需要撤销操作 // 这里一定要unicode decode一下,要不然会出现误判 var len1 = jsonStr.replace(/'|"|\s/g, '').length; var len2 = (_uniDecode(newSource)).replace(/'|"|\s/g, '').length; // 误差不允许超过20% if (Math.abs(len1 - len2) / ((len1 + len2) / 2) > 0.2) { return; } newSource = jsonStr; } catch (ex) { // 通过JSON反解不出来的,一定有问题 return; } $('body').html(_htmlFragment); _loadCss(); _bindEvent(); JsonFormatEntrance.clear(); JsonFormatEntrance.format(newSource); // 如果是JSONP格式的,需要把方法名也显示出来 if (funcName != null) { if (fnTry && fnCatch) { $('#jfCallbackName_start').html('
' + fnTry + '
' + funcName + '('); $('#jfCallbackName_end').html(')
' + fnCatch + '
'); } else { $('#jfCallbackName_start').html(funcName + '('); $('#jfCallbackName_end').html(')'); } } } }; var _init = function () { $(function () { if (baidu.feOption.pageJsonMustFormat) { _format(); } else { chrome.extension.sendMessage({ type: MSG_TYPE.GET_OPTIONS, items: ['JSON_PAGE_FORMAT'] }, function (opts) { if (!opts || opts.JSON_PAGE_FORMAT != 'false') { _format(); } }); } }); }; return { init: _init }; })(); baidu.csJsonFormat.init();