/**
* 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();