contentscript-jsonformat.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /**
  2. * content_scripts中如果被检测到当前页面内容为json数据,则自动进行JSON格式化
  3. */
  4. baidu.csJsonFormat = (function () {
  5. "use strict";
  6. var _htmlFragment = [
  7. '<div class="mod-json mod-contentscript"><div class="rst-item">',
  8. '<div id="formattingMsg">',
  9. '<svg id="spinner" width="16" height="16" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg" version="1.1">',
  10. '<path d="M 150,0 a 150,150 0 0,1 106.066,256.066 l -35.355,-35.355 a -100,-100 0 0,0 -70.711,-170.711 z" fill="#3d7fe6"></path>',
  11. '</svg>加载中...',
  12. '</div>',
  13. '<div id="jfCallbackName_start" class="callback-name"></div>',
  14. '<div id="jfContent"></div>',
  15. '<pre id="jfContent_pre"></pre>',
  16. '<div id="jfCallbackName_end" class="callback-name"></div>',
  17. '</div></div>'
  18. ].join('');
  19. var _loadCss = function () {
  20. var fcpCss = chrome.extension.getURL('static/css/fe-jsonformat-content.css');
  21. jQuery('<link id="_fehelper_fcp_css_" href="' + fcpCss + '" rel="stylesheet" type="text/css" />').appendTo('head');
  22. };
  23. /**
  24. * 从页面提取JSON文本
  25. * @returns {string}
  26. * @private
  27. */
  28. var _getJsonText = function () {
  29. var arr = [];
  30. var sData = '';
  31. [].slice.call(document.getElementsByTagName('*')).filter(function (el) {
  32. return (el.offsetHeight + el.offsetWidth) > 0;
  33. }).forEach(function (elm) {
  34. var sT = '';
  35. [].slice.call(elm.childNodes).forEach(function (el) {
  36. if (el.nodeType === document.TEXT_NODE) {
  37. sT += el.data;
  38. } else {
  39. arr.push(sT);
  40. sT = '';
  41. }
  42. });
  43. sT && arr.push(sT);
  44. });
  45. arr = arr.filter(function (s) {
  46. return s.trim();
  47. });
  48. sData = arr[0] || '';
  49. arr.forEach(function (item) {
  50. try {
  51. var isJson = false;
  52. if (sData) {
  53. if (sData.match(/^\s*[\[\{]/)) {
  54. var oJson = JSON.parse(sData);
  55. if (typeof oJson === 'object') {
  56. isJson = true;
  57. }
  58. } else {
  59. var sTempData = sData.slice(sData.indexOf('(') + 1, sData.lastIndexOf(')'));
  60. if (typeof JSON.parse(sTempData) === 'object') {
  61. isJson = true;
  62. sData = sTempData;
  63. }
  64. }
  65. }
  66. } catch (err) {
  67. }
  68. if (item.length > sData.length) {
  69. sData = sData || item;
  70. }
  71. if (!isJson) {
  72. sData = '';
  73. }
  74. });
  75. return sData;
  76. };
  77. /**
  78. * 此方法用于将Unicode码解码为正常字符串
  79. * @param {Object} text
  80. */
  81. var _uniDecode = function (text) {
  82. text = text.replace(/\\/g, "%").replace('%U', '%u').replace('%u0025', '%25');
  83. text = unescape(text.toString().replace(/%2B/g, "+"));
  84. var matches = text.match(/(%u00([0-9A-F]{2}))/gi);
  85. if (matches) {
  86. for (var matchid = 0; matchid < matches.length; matchid++) {
  87. var code = matches[matchid].substring(1, 3);
  88. var x = Number("0x" + code);
  89. if (x >= 128) {
  90. text = text.replace(matches[matchid], code);
  91. }
  92. }
  93. }
  94. text = unescape(text.toString().replace(/%2B/g, "+"));
  95. return text;
  96. };
  97. /**
  98. * chrome 下复制到剪贴板
  99. * @param text
  100. */
  101. var _copyToClipboard = function (text) {
  102. const input = document.createElement('textarea');
  103. input.style.position = 'fixed';
  104. input.style.opacity = 0;
  105. input.value = text;
  106. document.body.appendChild(input);
  107. input.select();
  108. document.execCommand('Copy');
  109. document.body.removeChild(input);
  110. alert('Json片段复制成功,随处粘贴可用!')
  111. };
  112. /**
  113. * 给某个节点增加操作项
  114. * @param el
  115. * @private
  116. */
  117. var _addOptForItem = function (el) {
  118. // 复制json片段
  119. var fnCopy = function (ec) {
  120. var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
  121. if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
  122. txt = '{' + txt + '}';
  123. }
  124. try {
  125. txt = JSON.stringify(JSON.parse(txt), null, 4);
  126. } catch (err) {
  127. }
  128. _copyToClipboard(txt);
  129. };
  130. // 删除json片段
  131. var fnDel = function (ed) {
  132. if (el.parent().is('#formattedJson')) {
  133. alert('如果连最外层的Json也删掉的话,就没啥意义了哦!');
  134. return false;
  135. }
  136. el.remove();
  137. boxOpt.css('top', -1000);
  138. };
  139. var boxOpt = $('#boxOpt');
  140. if (!boxOpt.length) {
  141. boxOpt = $('<div id="boxOpt"><a class="opt-copy">复制</a>|<a class="opt-del">删除</a></div>').appendTo('body');
  142. }
  143. boxOpt.find('a.opt-copy').unbind('click').bind('click', fnCopy);
  144. boxOpt.find('a.opt-del').unbind('click').bind('click', fnDel);
  145. boxOpt.css({
  146. left: el.offset().left + el.width() - 50,
  147. top: el.offset().top
  148. });
  149. };
  150. /**
  151. * 事件绑定
  152. * @private
  153. */
  154. var _bindEvent = function () {
  155. // 点击区块高亮
  156. $('#jfContent').delegate('.kvov', 'click', function (e) {
  157. $('#jfContent .kvov').removeClass('x-outline');
  158. var el = $(this).removeClass('x-hover').addClass('x-outline');
  159. // 增加复制、删除功能
  160. _addOptForItem(el);
  161. if (!$(e.target).is('.kvov .e')) {
  162. e.stopPropagation();
  163. } else {
  164. $(e.target).parent().trigger('click');
  165. }
  166. }).delegate('.kvov', 'mouseover', function (e) {
  167. $(this).addClass('x-hover');
  168. return false;
  169. }).delegate('.kvov', 'mouseout', function (e) {
  170. $(this).removeClass('x-hover');
  171. });
  172. };
  173. /**
  174. * 执行format操作
  175. * @private
  176. */
  177. var _format = function () {
  178. var source = _getJsonText();
  179. if (!source) {
  180. return;
  181. }
  182. // JSONP形式下的callback name
  183. var funcName = null;
  184. // json对象
  185. var jsonObj = null;
  186. var newSource = '';
  187. // 下面校验给定字符串是否为一个合法的json
  188. try {
  189. // 再看看是不是jsonp的格式
  190. var reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/igm;
  191. var matches = reg.exec(source);
  192. if (matches != null) {
  193. funcName = matches[1];
  194. newSource = matches[2];
  195. jsonObj = new Function("return " + newSource)();
  196. } else {
  197. reg = /^([\{\[])/;
  198. if (!reg.test(source)) {
  199. return;
  200. }
  201. }
  202. } catch (ex) {
  203. return;
  204. }
  205. try {
  206. if (jsonObj == null || typeof jsonObj != 'object') {
  207. jsonObj = new Function("return " + source)();
  208. // 还要防止下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}"
  209. if (typeof jsonObj == "string") {
  210. // 再来一次
  211. jsonObj = new Function("return " + jsonObj)();
  212. }
  213. }
  214. } catch (e) {
  215. return;
  216. }
  217. // 是json格式,可以进行JSON自动格式化
  218. if (jsonObj != null && typeof jsonObj == "object") {
  219. try {
  220. // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理
  221. newSource = JSON.stringify(jsonObj);
  222. // 如果newSource的长度比原source长度短很多的话,猜测应该是格式化错了,需要撤销操作
  223. // 这里一定要unicode decode一下,要不然会出现误判
  224. if (newSource.length * 2 < (_uniDecode(source)).length) {
  225. return;
  226. }
  227. } catch (ex) {
  228. // 通过JSON反解不出来的,一定有问题
  229. return;
  230. }
  231. $('body').html(_htmlFragment);
  232. _loadCss();
  233. _bindEvent();
  234. JsonFormatEntrance.clear();
  235. JsonFormatEntrance.format(newSource);
  236. // 如果是JSONP格式的,需要把方法名也显示出来
  237. if (funcName != null) {
  238. $('#jfCallbackName_start').html(funcName + '(');
  239. $('#jfCallbackName_end').html(')');
  240. }
  241. }
  242. };
  243. var _init = function () {
  244. $(function () {
  245. if (!/^filesystem\:/.test(location.href)) {
  246. if (baidu.feOption.pageJsonMustFormat) {
  247. _format();
  248. } else {
  249. chrome.extension.sendMessage({
  250. type: MSG_TYPE.GET_OPTIONS,
  251. items: ['JSON_PAGE_FORMAT']
  252. }, function (opts) {
  253. if (opts.JSON_PAGE_FORMAT != 'false') {
  254. _format();
  255. }
  256. });
  257. }
  258. }
  259. });
  260. };
  261. return {
  262. init: _init
  263. };
  264. })();
  265. baidu.csJsonFormat.init();