automatic.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /**
  2. * Json Page Automatic Format Via FeHelper
  3. * @author zhaoxianlie
  4. */
  5. // json with bigint supported
  6. Tarp.require('../static/vendor/json-bigint/index');
  7. module.exports = (() => {
  8. "use strict";
  9. let _htmlFragment = [
  10. '<div class="mod-json mod-contentscript"><div class="rst-item">',
  11. '<div id="formattingMsg">',
  12. '<svg id="spinner" width="16" height="16" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg" version="1.1">',
  13. '<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>',
  14. '</svg>加载中...',
  15. '</div>',
  16. '<div id="jfCallbackName_start" class="callback-name"></div>',
  17. '<div id="jfContent"></div>',
  18. '<pre id="jfContent_pre"></pre>',
  19. '<div id="jfCallbackName_end" class="callback-name"></div>',
  20. '</div></div>'
  21. ].join('');
  22. let _loadCss = function () {
  23. let cssUrl = chrome.extension.getURL('json-format/without-ui.css');
  24. jQuery('<link id="_fehelper_fcp_css_" href="' + cssUrl + '" rel="stylesheet" type="text/css" />').appendTo('head');
  25. };
  26. /**
  27. * 从页面提取JSON文本
  28. * @returns {string}
  29. * @private
  30. */
  31. let _getJsonText = function () {
  32. let pre = $('body>pre:eq(0)')[0] || {textContent: ""};
  33. let source = $.trim(pre.textContent);
  34. if (!source) {
  35. source = $.trim(document.body.textContent || '')
  36. }
  37. if (!source) {
  38. return false;
  39. }
  40. // 如果body的内容还包含HTML标签,肯定不是合法的json了
  41. // 如果是合法的json,也只可能有一个text节点
  42. let nodes = document.body.childNodes;
  43. let newSource = '';
  44. for (let i = 0, len = nodes.length; i < len; i++) {
  45. if (nodes[i].nodeType === Node.TEXT_NODE) {
  46. newSource += nodes[i].textContent;
  47. } else if (nodes[i].nodeType === Node.ELEMENT_NODE) {
  48. let tagName = nodes[i].tagName.toLowerCase();
  49. let html = $.trim(nodes[i].textContent);
  50. // 如果是pre标签,则看内容是不是和source一样,一样则continue
  51. if (tagName === 'pre' && html === source) {
  52. } else if ((nodes[i].offsetWidth === 0 || nodes[i].offsetHeight === 0 || !html) && ['script', 'link'].indexOf(tagName) === -1) {
  53. // 如果用户安装迅雷或者其他的插件,也回破坏页面结构,需要兼容一下
  54. } else {
  55. return false;
  56. }
  57. } else {
  58. return false;
  59. }
  60. }
  61. return $.trim(newSource || '') || source;
  62. };
  63. /**
  64. * 此方法用于将Unicode码解码为正常字符串
  65. * @param {Object} text
  66. */
  67. let _uniDecode = function (text) {
  68. text = text.replace(/\\/g, "%").replace('%U', '%u').replace('%u0025', '%25');
  69. text = unescape(text.toString().replace(/%2B/g, "+"));
  70. let matches = text.match(/(%u00([0-9A-F]{2}))/gi);
  71. if (matches) {
  72. for (let matchid = 0; matchid < matches.length; matchid++) {
  73. let code = matches[matchid].substring(1, 3);
  74. let x = Number("0x" + code);
  75. if (x >= 128) {
  76. text = text.replace(matches[matchid], code);
  77. }
  78. }
  79. }
  80. text = unescape(text.toString().replace(/%2B/g, "+"));
  81. return text;
  82. };
  83. /**
  84. * 执行format操作
  85. * @private
  86. */
  87. let _format = function () {
  88. let source = _getJsonText();
  89. if (!source) {
  90. return;
  91. }
  92. // JSONP形式下的callback name
  93. let funcName = null;
  94. let jsonObj = null;
  95. let fnTry = null;
  96. let fnCatch = null;
  97. // 下面校验给定字符串是否为一个合法的json
  98. try {
  99. // 再看看是不是jsonp的格式
  100. let reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/gm;
  101. let reTry = /^(try\s*\{\s*)?/g;
  102. let reCatch = /(\}\s*catch\s*\(\s*\S+\s*\)\s*\{([\s\S])*\})?$/g;
  103. // 检测是否有try-catch包裹
  104. let sourceReplaced = source.replace(reTry, function () {
  105. fnTry = fnTry ? fnTry : arguments[1];
  106. return '';
  107. }).replace(reCatch, function () {
  108. fnCatch = fnCatch ? fnCatch : arguments[1];
  109. return '';
  110. }).trim();
  111. let matches = reg.exec(sourceReplaced);
  112. if (matches != null && (fnTry && fnCatch || !fnTry && !fnCatch)) {
  113. funcName = matches[1];
  114. source = matches[2];
  115. } else {
  116. reg = /^([\{\[])/;
  117. if (!reg.test(source)) {
  118. return;
  119. }
  120. }
  121. // 这里可能会throw exception
  122. jsonObj = JSON.parse(source);
  123. } catch (ex) {
  124. // new Function的方式,能自动给key补全双引号,但是不支持bigint,所以是下下策,放在try-catch里搞
  125. try {
  126. jsonObj = new Function("return " + source)();
  127. } catch (exx) {
  128. try{
  129. // 再给你一次机会,是不是下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}"
  130. jsonObj = new Function("return '" + source + "'")();
  131. if(typeof jsonObj === 'string') {
  132. // 最后给你一次机会,是个字符串,老夫给你再转一次
  133. jsonObj = new Function("return " + jsonObj)();
  134. }
  135. }catch(exxx){
  136. return ;
  137. }
  138. }
  139. }
  140. // 是json格式,可以进行JSON自动格式化
  141. if (jsonObj != null && typeof jsonObj === "object") {
  142. try {
  143. // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理
  144. let jsonStr = JSON.stringify(jsonObj);
  145. // 如果newSource的长度比原source长度短很多的话,猜测应该是格式化错了,需要撤销操作
  146. // 这里一定要unicode decode一下,要不然会出现误判
  147. let len1 = jsonStr.replace(/'|"|\s/g, '').length;
  148. let len2 = (_uniDecode(source)).replace(/'|"|\s/g, '').length;
  149. // 误差不允许超过20%
  150. if (Math.abs(len1 - len2) / ((len1 + len2) / 2) > 0.2) {
  151. return;
  152. }
  153. source = jsonStr;
  154. } catch (ex) {
  155. // 通过JSON反解不出来的,一定有问题
  156. return;
  157. }
  158. $('body').html(_htmlFragment);
  159. _loadCss();
  160. // 格式化
  161. Tarp.require('../json-format/format-lib').format(source);
  162. // 如果是JSONP格式的,需要把方法名也显示出来
  163. if (funcName != null) {
  164. if (fnTry && fnCatch) {
  165. $('#jfCallbackName_start').html('<pre style="padding:0">' + fnTry + '</pre>' + funcName + '(');
  166. $('#jfCallbackName_end').html(')<br><pre style="padding:0">' + fnCatch + '</pre>');
  167. } else {
  168. $('#jfCallbackName_start').html(funcName + '(');
  169. $('#jfCallbackName_end').html(')');
  170. }
  171. }
  172. }
  173. };
  174. return {
  175. format: _format
  176. };
  177. })();