|
@@ -1,18 +1,98 @@
|
|
|
/**
|
|
|
- * FeHelper Json Format Lib
|
|
|
+ * 日期格式化
|
|
|
+ * @param {Object} pattern
|
|
|
+ */
|
|
|
+Date.prototype.format = function (pattern) {
|
|
|
+ let pad = function (source, length) {
|
|
|
+ let pre = "",
|
|
|
+ negative = (source < 0),
|
|
|
+ string = String(Math.abs(source));
|
|
|
+
|
|
|
+ if (string.length < length) {
|
|
|
+ pre = (new Array(length - string.length + 1)).join('0');
|
|
|
+ }
|
|
|
+
|
|
|
+ return (negative ? "-" : "") + pre + string;
|
|
|
+ };
|
|
|
+
|
|
|
+ if ('string' !== typeof pattern) {
|
|
|
+ return this.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ let replacer = function (patternPart, result) {
|
|
|
+ pattern = pattern.replace(patternPart, result);
|
|
|
+ };
|
|
|
+
|
|
|
+ let year = this.getFullYear(),
|
|
|
+ month = this.getMonth() + 1,
|
|
|
+ date2 = this.getDate(),
|
|
|
+ hours = this.getHours(),
|
|
|
+ minutes = this.getMinutes(),
|
|
|
+ seconds = this.getSeconds(),
|
|
|
+ milliSec = this.getMilliseconds();
|
|
|
+
|
|
|
+ replacer(/yyyy/g, pad(year, 4));
|
|
|
+ replacer(/yy/g, pad(parseInt(year.toString().slice(2), 10), 2));
|
|
|
+ replacer(/MM/g, pad(month, 2));
|
|
|
+ replacer(/M/g, month);
|
|
|
+ replacer(/dd/g, pad(date2, 2));
|
|
|
+ replacer(/d/g, date2);
|
|
|
+
|
|
|
+ replacer(/HH/g, pad(hours, 2));
|
|
|
+ replacer(/H/g, hours);
|
|
|
+ replacer(/hh/g, pad(hours % 12, 2));
|
|
|
+ replacer(/h/g, hours % 12);
|
|
|
+ replacer(/mm/g, pad(minutes, 2));
|
|
|
+ replacer(/m/g, minutes);
|
|
|
+ replacer(/ss/g, pad(seconds, 2));
|
|
|
+ replacer(/s/g, seconds);
|
|
|
+ replacer(/SSS/g, pad(milliSec, 3));
|
|
|
+ replacer(/S/g, milliSec);
|
|
|
+
|
|
|
+ return pattern;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 自动消失的Alert弹窗
|
|
|
+ * @param content
|
|
|
*/
|
|
|
-let JsonFormatEntrance = (function () {
|
|
|
+window.toast = function (content) {
|
|
|
+ window.clearTimeout(window.feHelperAlertMsgTid);
|
|
|
+ let elAlertMsg = document.querySelector("#fehelper_alertmsg");
|
|
|
+ if (!elAlertMsg) {
|
|
|
+ let elWrapper = document.createElement('div');
|
|
|
+ elWrapper.innerHTML = '<div id="fehelper_alertmsg" style="position:fixed;bottom:25px;left:5px;z-index:1000000">' +
|
|
|
+ '<p style="background:#000;display:inline-block;color:#fff;text-align:center;' +
|
|
|
+ 'padding:10px 10px;margin:0 auto;font-size:14px;border-radius:4px;">' + content + '</p></div>';
|
|
|
+ elAlertMsg = elWrapper.childNodes[0];
|
|
|
+ document.body.appendChild(elAlertMsg);
|
|
|
+ } else {
|
|
|
+ elAlertMsg.querySelector('p').innerHTML = content;
|
|
|
+ elAlertMsg.style.display = 'block';
|
|
|
+ }
|
|
|
+
|
|
|
+ window.feHelperAlertMsgTid = window.setTimeout(function () {
|
|
|
+ elAlertMsg.style.display = 'none';
|
|
|
+ }, 1000);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * FeHelper Json Format Lib,入口文件
|
|
|
+ * @example
|
|
|
+ * Formatter.format(jsonString)
|
|
|
+ */
|
|
|
+window.Formatter = (function () {
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
let jfContent,
|
|
|
jfPre,
|
|
|
jfStyleEl,
|
|
|
- jfOptEl,
|
|
|
- jfPathEl,
|
|
|
+ jfStatusBar,
|
|
|
formattingMsg;
|
|
|
|
|
|
- let lastKvovIdGiven = 0;
|
|
|
+ let lastItemIdGiven = 0;
|
|
|
let cachedJsonString = '';
|
|
|
|
|
|
let _initElements = function () {
|
|
@@ -40,42 +120,12 @@ let JsonFormatEntrance = (function () {
|
|
|
try {
|
|
|
jfContent.html('').show();
|
|
|
jfPre.html('').hide();
|
|
|
- jfPathEl && jfPathEl.hide();
|
|
|
+ jfStatusBar && jfStatusBar.hide();
|
|
|
formattingMsg.hide();
|
|
|
} catch (e) {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // Add listener to receive response from BG when ready
|
|
|
- let postMessage = function (msg) {
|
|
|
-
|
|
|
- switch (msg[0]) {
|
|
|
- case 'NOT JSON' :
|
|
|
- jfPre.show();
|
|
|
- jfContent.html('<span class="x-json-tips">JSON不合法,请检查:</span>');
|
|
|
- break;
|
|
|
-
|
|
|
- case 'FORMATTING' :
|
|
|
- formattingMsg.show();
|
|
|
- break;
|
|
|
-
|
|
|
- case 'FORMATTED' :
|
|
|
- formattingMsg.hide();
|
|
|
- jfContent.html(msg[1]);
|
|
|
-
|
|
|
- _buildOptionBar();
|
|
|
- // 事件绑定
|
|
|
- _addEvents();
|
|
|
- // 支持文件下载
|
|
|
- _downloadSupport(cachedJsonString);
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- default :
|
|
|
- throw new Error('Message not understood: ' + msg[0]);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
/**
|
|
|
* HTML特殊字符格式化
|
|
|
* @param str
|
|
@@ -90,26 +140,6 @@ let JsonFormatEntrance = (function () {
|
|
|
return str;
|
|
|
};
|
|
|
|
|
|
-
|
|
|
- /**
|
|
|
- * 执行代码格式化
|
|
|
- * @param {[type]} jsonStr [description]
|
|
|
- * @return {[type]}
|
|
|
- */
|
|
|
- let format = function (jsonStr) {
|
|
|
- cachedJsonString = JSON.stringify(JSON.parse(jsonStr), null, 4);
|
|
|
-
|
|
|
- _initElements();
|
|
|
- jfPre.html(htmlspecialchars(cachedJsonString));
|
|
|
-
|
|
|
- JsonFormatDealer.postMessage({
|
|
|
- type: "SENDING TEXT",
|
|
|
- text: jsonStr,
|
|
|
- length: jsonStr.length
|
|
|
- });
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
/**
|
|
|
* 直接下载,能解决中文乱码
|
|
|
* @param content
|
|
@@ -181,7 +211,7 @@ let JsonFormatEntrance = (function () {
|
|
|
*/
|
|
|
let getJsonText = function (el) {
|
|
|
|
|
|
- let txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
|
|
|
+ let txt = el.text().replace(/复制\|下载\|删除/gm,'').replace(/":\s/gm, '":').replace(/,$/, '').trim();
|
|
|
if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
|
|
|
txt = '{' + txt + '}';
|
|
|
}
|
|
@@ -193,15 +223,43 @@ let JsonFormatEntrance = (function () {
|
|
|
return txt;
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 给某个节点增加操作项
|
|
|
- * @param el
|
|
|
- * @private
|
|
|
- */
|
|
|
- let _addOptForItem = function (el) {
|
|
|
+ // 添加json路径
|
|
|
+ let _showJsonPath = function (curEl) {
|
|
|
+ let keys = [];
|
|
|
+ do {
|
|
|
+ if (curEl.hasClass('item-block')) {
|
|
|
+ if (!curEl.hasClass('rootItem')) {
|
|
|
+ keys.unshift('[' + curEl.prevAll('.item').length + ']');
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ keys.unshift(curEl.find('>.key').text());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (curEl.parent().hasClass('rootItem') || curEl.parent().parent().hasClass('rootItem')) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ curEl = curEl.parent().parent();
|
|
|
+
|
|
|
+ } while (curEl.length && !curEl.hasClass('rootItem'));
|
|
|
+
|
|
|
+ let path = keys.join('#@#').replace(/#@#\[/g, '[').replace(/#@#/g, '.');
|
|
|
+
|
|
|
+ let jfPath = $('#jsonPath');
|
|
|
+ if (!jfPath.length) {
|
|
|
+ jfPath = $('<span id="jsonPath"/>').prependTo(jfStatusBar);
|
|
|
+ }
|
|
|
+ jfPath.html('当前节点:JSON.' + path);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 给某个节点增加操作项
|
|
|
+ let _addOptForItem = function (el, show) {
|
|
|
|
|
|
// 下载json片段
|
|
|
- let fnDownload = function (ec) {
|
|
|
+ let fnDownload = function (event) {
|
|
|
+ event.stopPropagation();
|
|
|
|
|
|
let txt = getJsonText(el);
|
|
|
// 下载片段
|
|
@@ -232,36 +290,56 @@ let JsonFormatEntrance = (function () {
|
|
|
};
|
|
|
|
|
|
// 复制json片段
|
|
|
- let fnCopy = function (ec) {
|
|
|
+ let fnCopy = function (event) {
|
|
|
+ event.stopPropagation();
|
|
|
_copyToClipboard(getJsonText(el));
|
|
|
};
|
|
|
|
|
|
// 删除json片段
|
|
|
- let fnDel = function (ed) {
|
|
|
+ let fnDel = function (event) {
|
|
|
+ event.stopPropagation();
|
|
|
if (el.parent().is('#formattedJson')) {
|
|
|
toast('如果连最外层的Json也删掉的话,就没啥意义了哦!');
|
|
|
return false;
|
|
|
}
|
|
|
toast('节点已删除成功!');
|
|
|
el.remove();
|
|
|
- jfOptEl.css('top', -1000).hide();
|
|
|
- jfPathEl && jfPathEl.hide();
|
|
|
+ jfStatusBar && jfStatusBar.hide();
|
|
|
};
|
|
|
|
|
|
+ $('.boxOpt').hide();
|
|
|
+ if (show) {
|
|
|
+ let jfOptEl = el.children('.boxOpt');
|
|
|
+ if (!jfOptEl.length) {
|
|
|
+ jfOptEl = $('<b class="boxOpt">' +
|
|
|
+ '<a class="opt-copy" title="复制当前选中节点的JSON数据">复制</a>|' +
|
|
|
+ '<a class="opt-download" target="_blank" title="下载当前选中节点的JSON数据">下载</a>|' +
|
|
|
+ '<a class="opt-del" title="删除当前选中节点的JSON数据">删除</a></b>').appendTo(el);
|
|
|
+ } else {
|
|
|
+ jfOptEl.show();
|
|
|
+ }
|
|
|
+
|
|
|
+ jfOptEl.find('a.opt-download').unbind('click').bind('click', fnDownload);
|
|
|
+ jfOptEl.find('a.opt-copy').unbind('click').bind('click', fnCopy);
|
|
|
+ jfOptEl.find('a.opt-del').unbind('click').bind('click', fnDel);
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
|
|
|
- jfOptEl = $('#boxOpt');
|
|
|
- if (!jfOptEl.length) {
|
|
|
- jfOptEl = $('<div id="boxOpt"><a class="opt-download" target="_blank">下载</a>|<a class="opt-copy">复制</a>|<a class="opt-del">删除</a></div>').appendTo(jfContent);
|
|
|
+ // 显示当前节点的Key
|
|
|
+ let _toogleStatusBar = function (curEl, show) {
|
|
|
+ if (!jfStatusBar) {
|
|
|
+ jfStatusBar = $('<div id="statusBar"/>').appendTo('body');
|
|
|
}
|
|
|
|
|
|
- jfOptEl.find('a.opt-download').unbind('click').bind('click', fnDownload);
|
|
|
- jfOptEl.find('a.opt-copy').unbind('click').bind('click', fnCopy);
|
|
|
- jfOptEl.find('a.opt-del').unbind('click').bind('click', fnDel);
|
|
|
+ if (!show) {
|
|
|
+ jfStatusBar.hide();
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ jfStatusBar.show();
|
|
|
+ }
|
|
|
|
|
|
- jfOptEl.css({
|
|
|
- left: el.offset().left + el.width() - 90,
|
|
|
- top: el.offset().top
|
|
|
- }).show();
|
|
|
+ _showJsonPath(curEl);
|
|
|
};
|
|
|
|
|
|
|
|
@@ -274,19 +352,19 @@ let JsonFormatEntrance = (function () {
|
|
|
|
|
|
$.each(elements, function (i) {
|
|
|
el = $(this);
|
|
|
- if (el.children('.blockInner').length) {
|
|
|
+ if (el.children('.kv-list').length) {
|
|
|
el.addClass('collapsed');
|
|
|
|
|
|
if (!el.attr('id')) {
|
|
|
- el.attr('id', 'kvov' + (++lastKvovIdGiven));
|
|
|
+ el.attr('id', 'item' + (++lastItemIdGiven));
|
|
|
|
|
|
- let count = el.children('.blockInner').eq(0).children().length;
|
|
|
+ let count = el.children('.kv-list').eq(0).children().length;
|
|
|
// Generate comment text eg "4 items"
|
|
|
let comment = count + (count === 1 ? ' item' : ' items');
|
|
|
// Add CSS that targets it
|
|
|
jfStyleEl[0].insertAdjacentHTML(
|
|
|
'beforeend',
|
|
|
- '\n#kvov' + lastKvovIdGiven + '.collapsed:after{color: #aaa; content:" // ' + comment + '"}'
|
|
|
+ '\n#item' + lastItemIdGiven + '.collapsed:after{color: #aaa; content:" // ' + comment + '"}'
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -303,7 +381,7 @@ let JsonFormatEntrance = (function () {
|
|
|
let optionBar = $('#optionBar');
|
|
|
if (optionBar.length) {
|
|
|
optionBar.html('');
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
optionBar = $('<span id="optionBar" />').appendTo(jfContent.parent());
|
|
|
}
|
|
|
|
|
@@ -325,8 +403,7 @@ let JsonFormatEntrance = (function () {
|
|
|
buttonFormatted.text('格式化');
|
|
|
}
|
|
|
|
|
|
- jfOptEl && jfOptEl.hide();
|
|
|
- jfPathEl && jfPathEl.hide();
|
|
|
+ jfStatusBar && jfStatusBar.hide();
|
|
|
});
|
|
|
|
|
|
buttonCollapseAll.bind('click', function (e) {
|
|
@@ -337,60 +414,23 @@ let JsonFormatEntrance = (function () {
|
|
|
|
|
|
if (buttonCollapseAll.text() === '折叠所有') {
|
|
|
buttonCollapseAll.text('展开所有');
|
|
|
- collapse($('.objProp,.arrElem'));
|
|
|
+ collapse($('.item-object,.item-block'));
|
|
|
} else {
|
|
|
buttonCollapseAll.text('折叠所有');
|
|
|
- $('.objProp,.arrElem').removeClass('collapsed');
|
|
|
+ $('.item-object,.item-block').removeClass('collapsed');
|
|
|
}
|
|
|
- jfOptEl && jfOptEl.hide();
|
|
|
- jfPathEl && jfPathEl.hide();
|
|
|
+ jfStatusBar && jfStatusBar.hide();
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
- // 显示当前节点的Key
|
|
|
- let _showJsonKey = function (curEl) {
|
|
|
- let keys = [];
|
|
|
- do {
|
|
|
- if (curEl.hasClass('arrElem')) {
|
|
|
- if (!curEl.hasClass('rootKvov')) {
|
|
|
- keys.unshift('[' + curEl.prevAll('.kvov').length + ']');
|
|
|
- }
|
|
|
- } else {
|
|
|
- keys.unshift(curEl.find('>.k').text());
|
|
|
- }
|
|
|
-
|
|
|
- if (curEl.parent().hasClass('rootKvov') || curEl.parent().parent().hasClass('rootKvov')) {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- curEl = curEl.parent().parent();
|
|
|
-
|
|
|
- } while (curEl.length && !curEl.hasClass('rootKvov'));
|
|
|
-
|
|
|
- let path = keys.join('#@#').replace(/#@#\[/g, '[').replace(/#@#/g, '.');
|
|
|
- if (!jfPathEl) {
|
|
|
- jfPathEl = $('<div/>').css({
|
|
|
- position: 'fixed',
|
|
|
- bottom: 0,
|
|
|
- left: 0,
|
|
|
- background: 'rgb(0, 0, 0,0.6)',
|
|
|
- color: '#ff0',
|
|
|
- fontSize: '12px',
|
|
|
- fontWeight: 'bold',
|
|
|
- padding: '2px 10px 2px 2px',
|
|
|
- zIndex: 10
|
|
|
- }).appendTo('body');
|
|
|
- }
|
|
|
- jfPathEl.html('当前路径:' + path).show();
|
|
|
- };
|
|
|
-
|
|
|
// 附加操作
|
|
|
let _addEvents = function () {
|
|
|
|
|
|
// 折叠、展开
|
|
|
- $('#jfContent span.e').bind('click', function (ev) {
|
|
|
+ $('#jfContent span.expand').bind('click', function (ev) {
|
|
|
ev.preventDefault();
|
|
|
+ ev.stopPropagation();
|
|
|
|
|
|
let parentEl = $(this).parent();
|
|
|
parentEl.toggleClass('collapsed');
|
|
@@ -401,25 +441,26 @@ let JsonFormatEntrance = (function () {
|
|
|
});
|
|
|
|
|
|
// 点击选中:高亮
|
|
|
- $('#jfContent .kvov').bind('click', function (e) {
|
|
|
+ $('#jfContent .item').bind('click', function (e) {
|
|
|
+
|
|
|
+ let el = $(this);
|
|
|
|
|
|
- if ($(this).hasClass('x-outline')) {
|
|
|
- jfOptEl && jfOptEl.hide();
|
|
|
- jfPathEl && jfPathEl.hide();
|
|
|
- $(this).removeClass('x-outline');
|
|
|
+ if (el.hasClass('x-selected')) {
|
|
|
+ _toogleStatusBar(el, false);
|
|
|
+ _addOptForItem(el, false);
|
|
|
+ el.removeClass('x-selected');
|
|
|
e.stopPropagation();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- $('.x-outline').removeClass('x-outline');
|
|
|
- let el = $(this).removeClass('x-hover').addClass('x-outline');
|
|
|
+ $('.x-selected').removeClass('x-selected');
|
|
|
+ el.addClass('x-selected');
|
|
|
|
|
|
- // 增加复制、删除功能
|
|
|
- _addOptForItem(el);
|
|
|
- // 显示key
|
|
|
- _showJsonKey(el);
|
|
|
+ // 显示底部状态栏
|
|
|
+ _toogleStatusBar(el, true);
|
|
|
+ _addOptForItem(el, true);
|
|
|
|
|
|
- if (!$(e.target).is('.kvov .e')) {
|
|
|
+ if (!$(e.target).is('.item .expand')) {
|
|
|
e.stopPropagation();
|
|
|
} else {
|
|
|
$(e.target).parent().trigger('click');
|
|
@@ -429,25 +470,96 @@ let JsonFormatEntrance = (function () {
|
|
|
if (typeof window._OnJsonItemClickByFH === 'function') {
|
|
|
window._OnJsonItemClickByFH(getJsonText(el));
|
|
|
}
|
|
|
- }).bind('mouseover', function (e) {
|
|
|
- $(this).addClass('x-hover');
|
|
|
- return false;
|
|
|
- }).bind('mouseout', function (e) {
|
|
|
- $(this).removeClass('x-hover');
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
+ /**
|
|
|
+ * 执行代码格式化
|
|
|
+ */
|
|
|
+ let format = function (jsonStr, skin) {
|
|
|
+ cachedJsonString = JSON.stringify(JSON.parse(jsonStr), null, 4);
|
|
|
+
|
|
|
+ _initElements();
|
|
|
+ jfPre.html(htmlspecialchars(cachedJsonString));
|
|
|
+
|
|
|
+ // 用webwork的方式来进行格式化,效率更高
|
|
|
+ let worker = new Worker(URL.createObjectURL(new Blob(["(" + JsonFormatWebWorker.toString() + ")()"], {type: 'text/javascript'})));
|
|
|
+ worker.onmessage = function (evt) {
|
|
|
+ let msg = evt.data;
|
|
|
+ switch (msg[0]) {
|
|
|
+ case 'FORMATTING' :
|
|
|
+ formattingMsg.show();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'FORMATTED' :
|
|
|
+ formattingMsg.hide();
|
|
|
+ jfContent.html(msg[1]);
|
|
|
+
|
|
|
+ _buildOptionBar();
|
|
|
+ // 事件绑定
|
|
|
+ _addEvents();
|
|
|
+ // 支持文件下载
|
|
|
+ _downloadSupport(cachedJsonString);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ worker.postMessage({
|
|
|
+ jsonString: jsonStr,
|
|
|
+ skin: skin
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 同步的方式格式化
|
|
|
+ let formatSync = function (jsonStr, skin) {
|
|
|
+ cachedJsonString = JSON.stringify(JSON.parse(jsonStr), null, 4);
|
|
|
+
|
|
|
+ _initElements();
|
|
|
+ jfPre.html(htmlspecialchars(cachedJsonString));
|
|
|
+
|
|
|
+ let worker = new JsonFormatWebWorker();
|
|
|
+ worker.getFormattedHtml({
|
|
|
+ data: {
|
|
|
+ jsonString: jsonStr,
|
|
|
+ skin: skin
|
|
|
+ },
|
|
|
+ onFormatting: function (msg) {
|
|
|
+ formattingMsg.show();
|
|
|
+ },
|
|
|
+ onFormatted: function (msg) {
|
|
|
+ formattingMsg.hide();
|
|
|
+ jfContent.html(msg[1]);
|
|
|
+
|
|
|
+ _buildOptionBar();
|
|
|
+ // 事件绑定
|
|
|
+ _addEvents();
|
|
|
+ // 支持文件下载
|
|
|
+ _downloadSupport(cachedJsonString);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
return {
|
|
|
format: format,
|
|
|
- postMessage: postMessage
|
|
|
+ formatSync: formatSync
|
|
|
}
|
|
|
})();
|
|
|
|
|
|
|
|
|
-let JsonFormatDealer = (function () {
|
|
|
+/*============================================== web worker =========================================================*/
|
|
|
|
|
|
- "use strict";
|
|
|
+/**
|
|
|
+ * 用webworker的形式来进行json格式化,在应对大json的时候,效果会非常明显
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+var JsonFormatWebWorker = function () {
|
|
|
+
|
|
|
+ // 引入big-json.js解决大数字的问题
|
|
|
+ let __importScript = (filename) => {
|
|
|
+ this.compress && fetch(filename).then(resp => resp.text()).then(jsText => eval(jsText));
|
|
|
+ };
|
|
|
+ __importScript('json-bigint.js');
|
|
|
|
|
|
// Constants
|
|
|
let
|
|
@@ -456,148 +568,145 @@ let JsonFormatDealer = (function () {
|
|
|
TYPE_OBJECT = 3,
|
|
|
TYPE_ARRAY = 4,
|
|
|
TYPE_BOOL = 5,
|
|
|
- TYPE_NULL = 6
|
|
|
- ;
|
|
|
-
|
|
|
- // Utility functions
|
|
|
- function removeComments(str) {
|
|
|
- str = ('__' + str + '__').split('');
|
|
|
- let mode = {
|
|
|
- singleQuote: false,
|
|
|
- doubleQuote: false,
|
|
|
- regex: false,
|
|
|
- blockComment: false,
|
|
|
- lineComment: false,
|
|
|
- condComp: false
|
|
|
+ TYPE_NULL = 6;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * HTML特殊字符格式化
|
|
|
+ * @param str
|
|
|
+ * @returns {*}
|
|
|
+ */
|
|
|
+ let htmlspecialchars = function (str) {
|
|
|
+ str = str.replace(/&/g, '&');
|
|
|
+ str = str.replace(/</g, '<');
|
|
|
+ str = str.replace(/>/g, '>');
|
|
|
+ str = str.replace(/"/g, '"');
|
|
|
+ str = str.replace(/'/g, ''');
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * FH 虚拟DOM
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+ let FhVDom = function () {
|
|
|
+
|
|
|
+ this._id = 'fhvd_' + (new Date * 1);
|
|
|
+ this.tag = '';
|
|
|
+ this.innerText = '';
|
|
|
+ this.textContent = '';
|
|
|
+ this.childNodes = [];
|
|
|
+ this.className = '';
|
|
|
+ this.attributes = [];
|
|
|
+ this.classList = [];
|
|
|
+ this.classList.__proto__.add = this.classList.__proto__.push;
|
|
|
+
|
|
|
+ this.createElement = tag => {
|
|
|
+ this.tag = tag;
|
|
|
+ return this;
|
|
|
};
|
|
|
- for (let i = 0, l = str.length; i < l; i++) {
|
|
|
- if (mode.regex) {
|
|
|
- if (str[i] === '/' && str[i - 1] !== '\\') {
|
|
|
- mode.regex = false;
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (mode.singleQuote) {
|
|
|
- if (str[i] === "'" && str[i - 1] !== '\\') {
|
|
|
- mode.singleQuote = false;
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (mode.doubleQuote) {
|
|
|
- if (str[i] === '"' && str[i - 1] !== '\\') {
|
|
|
- mode.doubleQuote = false;
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (mode.blockComment) {
|
|
|
- if (str[i] === '*' && str[i + 1] === '/') {
|
|
|
- str[i + 1] = '';
|
|
|
- mode.blockComment = false;
|
|
|
- }
|
|
|
- str[i] = '';
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (mode.lineComment) {
|
|
|
- if (str[i + 1] === '\n' || str[i + 1] === '\r') {
|
|
|
- mode.lineComment = false;
|
|
|
- }
|
|
|
- str[i] = '';
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (mode.condComp) {
|
|
|
- if (str[i - 2] === '@' && str[i - 1] === '*' && str[i] === '/') {
|
|
|
- mode.condComp = false;
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
- mode.doubleQuote = str[i] === '"';
|
|
|
- mode.singleQuote = str[i] === "'";
|
|
|
- if (str[i] === '/') {
|
|
|
- if (str[i + 1] === '*' && str[i + 2] === '@') {
|
|
|
- mode.condComp = true;
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (str[i + 1] === '*') {
|
|
|
- str[i] = '';
|
|
|
- mode.blockComment = true;
|
|
|
- continue;
|
|
|
+
|
|
|
+ this.setAttribute = (attr, value) => {
|
|
|
+ this.attributes.push([attr, value]);
|
|
|
+ };
|
|
|
+
|
|
|
+ this.appendChild = child => {
|
|
|
+ this.childNodes.push(child);
|
|
|
+ return this;
|
|
|
+ };
|
|
|
+
|
|
|
+ this.getOuterHTML = () => {
|
|
|
+ let outerHtml = [];
|
|
|
+ if (this.tag) {
|
|
|
+ outerHtml.push(`<${this.tag}`);
|
|
|
+ let clsName = (this.className || '') + ' ' + this.classList.join(' ');
|
|
|
+ clsName.replace(/\s/g, '').length && outerHtml.push(` class="${clsName}"`);
|
|
|
+ this.attributes.length && outerHtml.push(this.attributes.map(attr => ` ${attr[0]}="${attr[1]}"`).join(''));
|
|
|
+ outerHtml.push(`>`);
|
|
|
+ if (('' + this.innerText).length) {
|
|
|
+ outerHtml.push(this.innerText);
|
|
|
+ } else if (('' + this.textContent).length) {
|
|
|
+ outerHtml.push(this.textContent);
|
|
|
+ } else {
|
|
|
+ outerHtml.push(this.childNodes.map(node => node.getOuterHTML()).join(''))
|
|
|
}
|
|
|
- if (str[i + 1] === '/') {
|
|
|
- str[i] = '';
|
|
|
- mode.lineComment = true;
|
|
|
- continue;
|
|
|
+ outerHtml.push(`</${this.tag}>`);
|
|
|
+ } else {
|
|
|
+ if (('' + this.innerText).length) {
|
|
|
+ outerHtml.push(this.innerText);
|
|
|
+ } else if (('' + this.textContent).length) {
|
|
|
+ outerHtml.push(this.textContent);
|
|
|
}
|
|
|
- mode.regex = true;
|
|
|
}
|
|
|
- }
|
|
|
- return str.join('').slice(2, -2);
|
|
|
- }
|
|
|
-
|
|
|
- // Template elements
|
|
|
- let templates,
|
|
|
- baseDiv = document.createElement('div'),
|
|
|
- baseSpan = document.createElement('span');
|
|
|
+ return outerHtml.join('');
|
|
|
+ };
|
|
|
|
|
|
- function getSpanBoth(innerText, className) {
|
|
|
- let span = baseSpan.cloneNode(false);
|
|
|
- span.className = className;
|
|
|
- span.innerText = innerText;
|
|
|
- return span;
|
|
|
- }
|
|
|
+ this.cloneNode = (deep) => {
|
|
|
+ let newDom = FhVDom.getInstance();
|
|
|
+ newDom.tag = this.tag;
|
|
|
+ if (deep || !this.tag) {
|
|
|
+ newDom.innerText = this.innerText;
|
|
|
+ newDom.textContent = this.textContent;
|
|
|
+ } else {
|
|
|
+ newDom.innerText = '';
|
|
|
+ newDom.textContent = '';
|
|
|
+ }
|
|
|
+ newDom.className = this.className;
|
|
|
+ newDom.classList = Array.from(this.classList);
|
|
|
+ newDom.attributes = Array.from(this.attributes);
|
|
|
+ return newDom;
|
|
|
+ };
|
|
|
+ };
|
|
|
|
|
|
- function getSpanText(innerText) {
|
|
|
- let span = baseSpan.cloneNode(false);
|
|
|
- span.innerText = innerText;
|
|
|
- return span;
|
|
|
- }
|
|
|
+ // 构造器
|
|
|
+ FhVDom.getInstance = () => new FhVDom();
|
|
|
|
|
|
- function getSpanClass(className) {
|
|
|
- let span = baseSpan.cloneNode(false);
|
|
|
- span.className = className;
|
|
|
+ function createSpanNode(innerText, className) {
|
|
|
+ let span = FhVDom.getInstance().createElement('span');
|
|
|
+ span.className = className || '';
|
|
|
+ span.innerText = innerText || '';
|
|
|
return span;
|
|
|
}
|
|
|
|
|
|
- function getDivClass(className) {
|
|
|
- let span = baseDiv.cloneNode(false);
|
|
|
- span.className = className;
|
|
|
- return span;
|
|
|
+ function createDivNode(className) {
|
|
|
+ let div = FhVDom.getInstance().createElement('div');
|
|
|
+ div.className = className || '';
|
|
|
+ return div;
|
|
|
}
|
|
|
|
|
|
// Create template nodes
|
|
|
let templatesObj = {
|
|
|
- t_kvov: getDivClass('kvov'),
|
|
|
- t_key: getSpanClass('k'),
|
|
|
- t_string: getSpanClass('s'),
|
|
|
- t_number: getSpanClass('n'),
|
|
|
- t_exp: getSpanClass('e'),
|
|
|
-
|
|
|
- t_null: getSpanBoth('null', 'nl'),
|
|
|
- t_true: getSpanBoth('true', 'bl'),
|
|
|
- t_false: getSpanBoth('false', 'bl'),
|
|
|
-
|
|
|
- t_oBrace: getSpanBoth('{', 'b'),
|
|
|
- t_cBrace: getSpanBoth('}', 'b'),
|
|
|
- t_oBracket: getSpanBoth('[', 'b'),
|
|
|
- t_cBracket: getSpanBoth(']', 'b'),
|
|
|
-
|
|
|
- t_ellipsis: getSpanClass('ell'),
|
|
|
- t_blockInner: getSpanClass('blockInner'),
|
|
|
-
|
|
|
- t_colonAndSpace: document.createTextNode(':\u00A0'),
|
|
|
- t_commaText: document.createTextNode(','),
|
|
|
- t_dblqText: document.createTextNode('"')
|
|
|
+ t_item: createDivNode('item'),
|
|
|
+ t_key: createSpanNode('', 'key'),
|
|
|
+ t_string: createSpanNode('', 'string'),
|
|
|
+ t_number: createSpanNode('', 'number'),
|
|
|
+ t_exp: createSpanNode('', 'expand'),
|
|
|
+
|
|
|
+ t_null: createSpanNode('null', 'null'),
|
|
|
+ t_true: createSpanNode('true', 'bool'),
|
|
|
+ t_false: createSpanNode('false', 'bool'),
|
|
|
+
|
|
|
+ t_oBrace: createSpanNode('{', 'brace'),
|
|
|
+ t_cBrace: createSpanNode('}', 'brace'),
|
|
|
+ t_oBracket: createSpanNode('[', 'brace'),
|
|
|
+ t_cBracket: createSpanNode(']', 'brace'),
|
|
|
+
|
|
|
+ t_ellipsis: createSpanNode('', 'ellipsis'),
|
|
|
+ t_kvList: createDivNode('kv-list'),
|
|
|
+
|
|
|
+ t_colonAndSpace: createSpanNode(':\u00A0', 'colon'),
|
|
|
+ t_commaText: createSpanNode(',', 'comma'),
|
|
|
+ t_dblqText: createSpanNode('"', 'quote')
|
|
|
};
|
|
|
|
|
|
// Core recursive DOM-building function
|
|
|
- function getKvovDOM(value, keyName) {
|
|
|
+ function getItemDOM(value, keyName) {
|
|
|
let type,
|
|
|
- kvov,
|
|
|
+ item,
|
|
|
nonZeroSize,
|
|
|
- templates = templatesObj, // bring into scope for tiny speed boost
|
|
|
+ templates = templatesObj,
|
|
|
objKey,
|
|
|
keySpan,
|
|
|
- valueElement
|
|
|
- ;
|
|
|
+ valueElement;
|
|
|
|
|
|
// Establish value type
|
|
|
if (typeof value === 'string')
|
|
@@ -613,8 +722,7 @@ let JsonFormatDealer = (function () {
|
|
|
else
|
|
|
type = TYPE_OBJECT;
|
|
|
|
|
|
- // Root node for this kvov
|
|
|
- kvov = templates.t_kvov.cloneNode(false);
|
|
|
+ item = templates.t_item.cloneNode(false);
|
|
|
|
|
|
// Add an 'expander' first (if this is object/array with non-zero size)
|
|
|
if (type === TYPE_OBJECT || type === TYPE_ARRAY) {
|
|
@@ -631,338 +739,140 @@ let JsonFormatDealer = (function () {
|
|
|
}
|
|
|
}
|
|
|
if (nonZeroSize)
|
|
|
- kvov.appendChild(templates.t_exp.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_exp.cloneNode(true));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// If there's a key, add that before the value
|
|
|
if (keyName !== false) { // NB: "" is a legal keyname in JSON
|
|
|
- // This kvov must be an object property
|
|
|
- kvov.classList.add('objProp');
|
|
|
- // Create a span for the key name
|
|
|
+ item.classList.add(type === TYPE_OBJECT ? 'item-object' : type === TYPE_ARRAY ? 'item-array' : 'item-line');
|
|
|
keySpan = templates.t_key.cloneNode(false);
|
|
|
keySpan.textContent = JSON.stringify(keyName).slice(1, -1); // remove quotes
|
|
|
- // Add it to kvov, with quote marks
|
|
|
- kvov.appendChild(templates.t_dblqText.cloneNode(false));
|
|
|
- kvov.appendChild(keySpan);
|
|
|
- kvov.appendChild(templates.t_dblqText.cloneNode(false));
|
|
|
- // Also add ": " (colon and non-breaking space)
|
|
|
- kvov.appendChild(templates.t_colonAndSpace.cloneNode(false));
|
|
|
+ item.appendChild(templates.t_dblqText.cloneNode(true));
|
|
|
+ item.appendChild(keySpan);
|
|
|
+ item.appendChild(templates.t_dblqText.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_colonAndSpace.cloneNode(true));
|
|
|
}
|
|
|
else {
|
|
|
- // This is an array element instead
|
|
|
- kvov.classList.add('arrElem');
|
|
|
+ item.classList.add('item-block');
|
|
|
}
|
|
|
|
|
|
- // Generate DOM for this value
|
|
|
- let blockInner, childKvov;
|
|
|
+ let kvList, childItem;
|
|
|
switch (type) {
|
|
|
case TYPE_STRING:
|
|
|
- // If string is a URL, get a link, otherwise get a span
|
|
|
- let innerStringEl = baseSpan.cloneNode(false),
|
|
|
+ let innerStringEl = FhVDom.getInstance().createElement('span'),
|
|
|
escapedString = JSON.stringify(value);
|
|
|
escapedString = escapedString.substring(1, escapedString.length - 1); // remove quotes
|
|
|
- if (value[0] === 'h' && value.substring(0, 4) === 'http') { // crude but fast - some false positives, but rare, and UX doesn't suffer terribly from them.
|
|
|
- let innerStringA = document.createElement('A');
|
|
|
- innerStringA.href = value;
|
|
|
- innerStringA.innerText = escapedString;
|
|
|
- innerStringEl.appendChild(innerStringA);
|
|
|
+ let isLink = false;
|
|
|
+ if (/^[\w]+:\/\//.test(value)) {
|
|
|
+ try {
|
|
|
+ let url = new URL(value);
|
|
|
+ let innerStringA = FhVDom.getInstance().createElement('A');
|
|
|
+ innerStringA.setAttribute('href', url.href);
|
|
|
+ innerStringA.setAttribute('target', '_blank');
|
|
|
+ innerStringA.innerText = htmlspecialchars(escapedString);
|
|
|
+ innerStringEl.appendChild(innerStringA);
|
|
|
+ isLink = true;
|
|
|
+ } catch (e) {
|
|
|
+ }
|
|
|
}
|
|
|
- else {
|
|
|
- innerStringEl.innerText = escapedString;
|
|
|
+
|
|
|
+ if (!isLink) {
|
|
|
+ innerStringEl.innerText = htmlspecialchars(escapedString);
|
|
|
}
|
|
|
valueElement = templates.t_string.cloneNode(false);
|
|
|
- valueElement.appendChild(templates.t_dblqText.cloneNode(false));
|
|
|
+ valueElement.appendChild(templates.t_dblqText.cloneNode(true));
|
|
|
valueElement.appendChild(innerStringEl);
|
|
|
- valueElement.appendChild(templates.t_dblqText.cloneNode(false));
|
|
|
- kvov.appendChild(valueElement);
|
|
|
+ valueElement.appendChild(templates.t_dblqText.cloneNode(true));
|
|
|
+ item.appendChild(valueElement);
|
|
|
break;
|
|
|
|
|
|
case TYPE_NUMBER:
|
|
|
- // Simply add a number element (span.n)
|
|
|
valueElement = templates.t_number.cloneNode(false);
|
|
|
valueElement.innerText = value;
|
|
|
- kvov.appendChild(valueElement);
|
|
|
+ item.appendChild(valueElement);
|
|
|
break;
|
|
|
|
|
|
case TYPE_OBJECT:
|
|
|
// Add opening brace
|
|
|
- kvov.appendChild(templates.t_oBrace.cloneNode(true));
|
|
|
- // If any properties, add a blockInner containing k/v pair(s)
|
|
|
+ item.appendChild(templates.t_oBrace.cloneNode(true));
|
|
|
if (nonZeroSize) {
|
|
|
- // Add ellipsis (empty, but will be made to do something when kvov is collapsed)
|
|
|
- kvov.appendChild(templates.t_ellipsis.cloneNode(false));
|
|
|
- // Create blockInner, which indents (don't attach yet)
|
|
|
- blockInner = templates.t_blockInner.cloneNode(false);
|
|
|
- // For each key/value pair, add as a kvov to blockInner
|
|
|
- let count = 0, k, comma;
|
|
|
- for (k in value) {
|
|
|
- if (value.hasOwnProperty(k)) {
|
|
|
- count++;
|
|
|
- childKvov = getKvovDOM(value[k], k);
|
|
|
- // Add comma
|
|
|
- comma = templates.t_commaText.cloneNode();
|
|
|
- childKvov.appendChild(comma);
|
|
|
- blockInner.appendChild(childKvov);
|
|
|
+ item.appendChild(templates.t_ellipsis.cloneNode(false));
|
|
|
+ kvList = templates.t_kvList.cloneNode(false);
|
|
|
+ let keys = Object.keys(value).filter(k => value.hasOwnProperty(k));
|
|
|
+ keys.forEach((k, index) => {
|
|
|
+ childItem = getItemDOM(value[k], k);
|
|
|
+ if (index < keys.length - 1) {
|
|
|
+ childItem.appendChild(templates.t_commaText.cloneNode(true));
|
|
|
}
|
|
|
- }
|
|
|
- // Now remove the last comma
|
|
|
- childKvov.removeChild(comma);
|
|
|
- // Add blockInner
|
|
|
- kvov.appendChild(blockInner);
|
|
|
+ kvList.appendChild(childItem);
|
|
|
+ });
|
|
|
+ item.appendChild(kvList);
|
|
|
}
|
|
|
|
|
|
// Add closing brace
|
|
|
- kvov.appendChild(templates.t_cBrace.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_cBrace.cloneNode(true));
|
|
|
break;
|
|
|
|
|
|
case TYPE_ARRAY:
|
|
|
- // Add opening bracket
|
|
|
- kvov.appendChild(templates.t_oBracket.cloneNode(true));
|
|
|
- // If non-zero length array, add blockInner containing inner vals
|
|
|
+ item.appendChild(templates.t_oBracket.cloneNode(true));
|
|
|
if (nonZeroSize) {
|
|
|
- // Add ellipsis
|
|
|
- kvov.appendChild(templates.t_ellipsis.cloneNode(false));
|
|
|
- // Create blockInner (which indents) (don't attach yet)
|
|
|
- blockInner = templates.t_blockInner.cloneNode(false);
|
|
|
- // For each key/value pair, add the markup
|
|
|
+ item.appendChild(templates.t_ellipsis.cloneNode(false));
|
|
|
+ kvList = templates.t_kvList.cloneNode(false);
|
|
|
for (let i = 0, length = value.length, lastIndex = length - 1; i < length; i++) {
|
|
|
- // Make a new kvov, with no key
|
|
|
- childKvov = getKvovDOM(value[i], false);
|
|
|
- // Add comma if not last one
|
|
|
+ childItem = getItemDOM(value[i], false);
|
|
|
if (i < lastIndex)
|
|
|
- childKvov.appendChild(templates.t_commaText.cloneNode());
|
|
|
- // Append the child kvov
|
|
|
- blockInner.appendChild(childKvov);
|
|
|
+ childItem.appendChild(templates.t_commaText.cloneNode(true));
|
|
|
+ kvList.appendChild(childItem);
|
|
|
}
|
|
|
- // Add blockInner
|
|
|
- kvov.appendChild(blockInner);
|
|
|
+ item.appendChild(kvList);
|
|
|
}
|
|
|
// Add closing bracket
|
|
|
- kvov.appendChild(templates.t_cBracket.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_cBracket.cloneNode(true));
|
|
|
break;
|
|
|
|
|
|
case TYPE_BOOL:
|
|
|
if (value)
|
|
|
- kvov.appendChild(templates.t_true.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_true.cloneNode(true));
|
|
|
else
|
|
|
- kvov.appendChild(templates.t_false.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_false.cloneNode(true));
|
|
|
break;
|
|
|
|
|
|
case TYPE_NULL:
|
|
|
- kvov.appendChild(templates.t_null.cloneNode(true));
|
|
|
+ item.appendChild(templates.t_null.cloneNode(true));
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- return kvov;
|
|
|
- }
|
|
|
-
|
|
|
- // Function to convert object to an HTML string
|
|
|
- function jsonObjToHTML(obj, jsonpFunctionName) {
|
|
|
-
|
|
|
- // Format object (using recursive kvov builder)
|
|
|
- let rootKvov = getKvovDOM(obj, false);
|
|
|
-
|
|
|
- // The whole DOM is now built.
|
|
|
-
|
|
|
- // Set class on root node to identify it
|
|
|
- rootKvov.classList.add('rootKvov');
|
|
|
-
|
|
|
- // Make div#formattedJson and append the root kvov
|
|
|
- let divFormattedJson = document.createElement('DIV');
|
|
|
- divFormattedJson.id = 'formattedJson';
|
|
|
- divFormattedJson.appendChild(rootKvov);
|
|
|
-
|
|
|
- // Convert it to an HTML string (shame about this step, but necessary for passing it through to the content page)
|
|
|
- let returnHTML = divFormattedJson.outerHTML;
|
|
|
-
|
|
|
- // Top and tail with JSONP padding if necessary
|
|
|
- if (jsonpFunctionName !== null) {
|
|
|
- returnHTML =
|
|
|
- '<div id="jsonpOpener">' + jsonpFunctionName + ' ( </div>' +
|
|
|
- returnHTML +
|
|
|
- '<div id="jsonpCloser">)</div>';
|
|
|
- }
|
|
|
-
|
|
|
- // Return the HTML
|
|
|
- return returnHTML;
|
|
|
+ return item;
|
|
|
}
|
|
|
|
|
|
// Listen for requests from content pages wanting to set up a port
|
|
|
- let postMessage = function (msg) {
|
|
|
- let jsonpFunctionName = null;
|
|
|
-
|
|
|
- if (msg.type === 'SENDING TEXT') {
|
|
|
- // Try to parse as JSON
|
|
|
- let obj,
|
|
|
- text = msg.text;
|
|
|
- try {
|
|
|
- obj = JSON.parse(text);
|
|
|
- }
|
|
|
- catch (e) {
|
|
|
- // Not JSON; could be JSONP though.
|
|
|
-
|
|
|
- // Try stripping 'padding' (if any), and try parsing it again
|
|
|
- text = text.trim();
|
|
|
- // Find where the first paren is (and exit if none)
|
|
|
- let indexOfParen;
|
|
|
- if (!(indexOfParen = text.indexOf('('))) {
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'no opening parenthesis']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Get the substring up to the first "(", with any comments/whitespace stripped out
|
|
|
- let firstBit = removeComments(text.substring(0, indexOfParen)).trim();
|
|
|
- if (!firstBit.match(/^[a-zA-Z_$][\.\[\]'"0-9a-zA-Z_$]*$/)) {
|
|
|
- // The 'firstBit' is NOT a valid function identifier.
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'first bit not a valid function name']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Find last parenthesis (exit if none)
|
|
|
- let indexOfLastParen;
|
|
|
- if (!(indexOfLastParen = text.lastIndexOf(')'))) {
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'no closing paren']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Check that what's after the last parenthesis is just whitespace, comments, and possibly a semicolon (exit if anything else)
|
|
|
- let lastBit = removeComments(text.substring(indexOfLastParen + 1)).trim();
|
|
|
- if (lastBit !== "" && lastBit !== ';') {
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'last closing paren followed by invalid characters']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // So, it looks like a valid JS function call, but we don't know whether it's JSON inside the parentheses...
|
|
|
- // Check if the 'argument' is actually JSON (and record the parsed result)
|
|
|
- text = text.substring(indexOfParen + 1, indexOfLastParen);
|
|
|
- try {
|
|
|
- obj = JSON.parse(text);
|
|
|
- }
|
|
|
- catch (e2) {
|
|
|
- // Just some other text that happens to be in a function call.
|
|
|
- // Respond as not JSON, and exit
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'looks like a function call, but the parameter is not valid JSON']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- jsonpFunctionName = firstBit;
|
|
|
- }
|
|
|
-
|
|
|
- // If still running, we now have obj, which is valid JSON.
|
|
|
-
|
|
|
- // Ensure it's not a number or string (technically valid JSON, but no point prettifying it)
|
|
|
- if (typeof obj !== 'object' && typeof obj !== 'array') {
|
|
|
- JsonFormatEntrance.postMessage(['NOT JSON', 'technically JSON but not an object or array']);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- JsonFormatEntrance.postMessage(['FORMATTING']);
|
|
|
-
|
|
|
- try {
|
|
|
- // 有的页面设置了 安全策略,连localStorage都不能用,setTimeout开启多线程就更别说了
|
|
|
- localStorage.getItem('just test : Blocked script execution in xxx?');
|
|
|
-
|
|
|
- // 在非UI线程中操作:异步。。。
|
|
|
- setTimeout(function () {
|
|
|
- // Do formatting
|
|
|
- let html = jsonObjToHTML(obj, jsonpFunctionName);
|
|
|
-
|
|
|
- // Post the HTML string to the content script
|
|
|
- JsonFormatEntrance.postMessage(['FORMATTED', html]);
|
|
|
- }, 0);
|
|
|
- } catch (ex) {
|
|
|
- // 错误信息类似:Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag.
|
|
|
- let html = jsonObjToHTML(obj, jsonpFunctionName);
|
|
|
- JsonFormatEntrance.postMessage(['FORMATTED', html]);
|
|
|
- }
|
|
|
-
|
|
|
+ self.onmessage = function (event) {
|
|
|
+ self.postMessage(['FORMATTING']);
|
|
|
+ let rootItem;
|
|
|
+ if (event.data.skin && event.data.skin === 'theme-simple') {
|
|
|
+ rootItem = createDivNode('rootItem');
|
|
|
+ rootItem.textContent = JSON.stringify(JSON.parse(event.data.jsonString), null, 4);
|
|
|
+ } else {
|
|
|
+ rootItem = getItemDOM(JSON.parse(event.data.jsonString), false);
|
|
|
+ rootItem.classList.add('rootItem');
|
|
|
}
|
|
|
+ let formattedHtml = `<div id="formattedJson">${rootItem.getOuterHTML()}</div>`;
|
|
|
+ self.postMessage(['FORMATTED', formattedHtml]);
|
|
|
};
|
|
|
|
|
|
- return {
|
|
|
- postMessage: postMessage
|
|
|
- };
|
|
|
-})();
|
|
|
-
|
|
|
-window.Formatter = {
|
|
|
- format: JsonFormatEntrance.format
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * 日期格式化
|
|
|
- * @param {Object} pattern
|
|
|
- */
|
|
|
-Date.prototype.format = function (pattern) {
|
|
|
- let pad = function (source, length) {
|
|
|
- let pre = "",
|
|
|
- negative = (source < 0),
|
|
|
- string = String(Math.abs(source));
|
|
|
-
|
|
|
- if (string.length < length) {
|
|
|
- pre = (new Array(length - string.length + 1)).join('0');
|
|
|
+ // 针对不支持webworker的情况,允许直接调用
|
|
|
+ this.getFormattedHtml = function (options) {
|
|
|
+ options.onFormatting && options.onFormatting(['FORMATTING']);
|
|
|
+ let rootItem;
|
|
|
+ if (options.data.skin && options.data.skin === 'theme-simple') {
|
|
|
+ rootItem = createDivNode('rootItem');
|
|
|
+ rootItem.textContent = JSON.stringify(JSON.parse(options.data.jsonString), null, 4);
|
|
|
+ } else {
|
|
|
+ rootItem = getItemDOM(JSON.parse(options.data.jsonString), false);
|
|
|
+ rootItem.classList.add('rootItem');
|
|
|
}
|
|
|
-
|
|
|
- return (negative ? "-" : "") + pre + string;
|
|
|
- };
|
|
|
-
|
|
|
- if ('string' !== typeof pattern) {
|
|
|
- return this.toString();
|
|
|
- }
|
|
|
-
|
|
|
- let replacer = function (patternPart, result) {
|
|
|
- pattern = pattern.replace(patternPart, result);
|
|
|
+ let formattedHtml = `<div id="formattedJson">${rootItem.getOuterHTML()}</div>`;
|
|
|
+ options.onFormatted && options.onFormatted(['FORMATTED', formattedHtml]);
|
|
|
};
|
|
|
-
|
|
|
- let year = this.getFullYear(),
|
|
|
- month = this.getMonth() + 1,
|
|
|
- date2 = this.getDate(),
|
|
|
- hours = this.getHours(),
|
|
|
- minutes = this.getMinutes(),
|
|
|
- seconds = this.getSeconds(),
|
|
|
- milliSec = this.getMilliseconds();
|
|
|
-
|
|
|
- replacer(/yyyy/g, pad(year, 4));
|
|
|
- replacer(/yy/g, pad(parseInt(year.toString().slice(2), 10), 2));
|
|
|
- replacer(/MM/g, pad(month, 2));
|
|
|
- replacer(/M/g, month);
|
|
|
- replacer(/dd/g, pad(date2, 2));
|
|
|
- replacer(/d/g, date2);
|
|
|
-
|
|
|
- replacer(/HH/g, pad(hours, 2));
|
|
|
- replacer(/H/g, hours);
|
|
|
- replacer(/hh/g, pad(hours % 12, 2));
|
|
|
- replacer(/h/g, hours % 12);
|
|
|
- replacer(/mm/g, pad(minutes, 2));
|
|
|
- replacer(/m/g, minutes);
|
|
|
- replacer(/ss/g, pad(seconds, 2));
|
|
|
- replacer(/s/g, seconds);
|
|
|
- replacer(/SSS/g, pad(milliSec, 3));
|
|
|
- replacer(/S/g, milliSec);
|
|
|
-
|
|
|
- return pattern;
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * 自动消失的Alert弹窗
|
|
|
- * @param content
|
|
|
- */
|
|
|
-window.toast = function (content) {
|
|
|
- window.clearTimeout(window.feHelperAlertMsgTid);
|
|
|
- let elAlertMsg = document.querySelector("#fehelper_alertmsg");
|
|
|
- if (!elAlertMsg) {
|
|
|
- let elWrapper = document.createElement('div');
|
|
|
- elWrapper.innerHTML = '<div id="fehelper_alertmsg" style="position:fixed;top:5px;right:5px;z-index:1000000">' +
|
|
|
- '<p style="background:#000;display:inline-block;color:#fff;text-align:center;' +
|
|
|
- 'padding:10px 10px;margin:0 auto;font-size:14px;border-radius:4px;">' + content + '</p></div>';
|
|
|
- elAlertMsg = elWrapper.childNodes[0];
|
|
|
- document.body.appendChild(elAlertMsg);
|
|
|
- } else {
|
|
|
- elAlertMsg.querySelector('p').innerHTML = content;
|
|
|
- elAlertMsg.style.display = 'block';
|
|
|
- }
|
|
|
-
|
|
|
- window.feHelperAlertMsgTid = window.setTimeout(function () {
|
|
|
- elAlertMsg.style.display = 'none';
|
|
|
- }, 3000);
|
|
|
};
|