contentscript-jsonformat.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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 pre = $('body>pre:eq(0)')[0] || {textContent: ""};
  30. var source = $.trim(pre.textContent);
  31. if (!source) {
  32. source = $.trim(document.body.textContent || '')
  33. }
  34. if (!source) {
  35. return false;
  36. }
  37. // 如果body的内容还包含HTML标签,肯定不是合法的json了
  38. // 如果是合法的json,也只可能有一个text节点
  39. var nodes = document.body.childNodes;
  40. var newSource = '';
  41. for (var i = 0, len = nodes.length; i < len; i++) {
  42. if (nodes[i].nodeType == Node.TEXT_NODE) {
  43. newSource += nodes[i].textContent;
  44. } else if (nodes[i].nodeType == Node.ELEMENT_NODE) {
  45. var tagName = nodes[i].tagName.toLowerCase();
  46. var html = $.trim(nodes[i].textContent);
  47. // 如果是pre标签,则看内容是不是和source一样,一样则continue
  48. if (tagName === 'pre' && html === source) {
  49. continue;
  50. } else if ((nodes[i].offsetWidth === 0 || nodes[i].offsetHeight === 0 || !html) && ['script','link'].indexOf(tagName) == -1) {
  51. // 如果用户安装迅雷或者其他的插件,也回破坏页面结构,需要兼容一下
  52. continue;
  53. } else {
  54. return false;
  55. }
  56. } else {
  57. return false;
  58. }
  59. }
  60. return $.trim(newSource || '') || source;
  61. };
  62. /**
  63. * 此方法用于将Unicode码解码为正常字符串
  64. * @param {Object} text
  65. */
  66. var _uniDecode = function (text) {
  67. text = text.replace(/\\/g, "%").replace('%U', '%u').replace('%u0025', '%25');
  68. text = unescape(text.toString().replace(/%2B/g, "+"));
  69. var matches = text.match(/(%u00([0-9A-F]{2}))/gi);
  70. if (matches) {
  71. for (var matchid = 0; matchid < matches.length; matchid++) {
  72. var code = matches[matchid].substring(1, 3);
  73. var x = Number("0x" + code);
  74. if (x >= 128) {
  75. text = text.replace(matches[matchid], code);
  76. }
  77. }
  78. }
  79. text = unescape(text.toString().replace(/%2B/g, "+"));
  80. return text;
  81. };
  82. /**
  83. * chrome 下复制到剪贴板
  84. * @param text
  85. */
  86. var _copyToClipboard = function (text) {
  87. const input = document.createElement('textarea');
  88. input.style.position = 'fixed';
  89. input.style.opacity = 0;
  90. input.value = text;
  91. document.body.appendChild(input);
  92. input.select();
  93. document.execCommand('Copy');
  94. document.body.removeChild(input);
  95. alert('Json片段复制成功,随处粘贴可用!')
  96. };
  97. /**
  98. * 给某个节点增加操作项
  99. * @param el
  100. * @private
  101. */
  102. var _addOptForItem = function (el) {
  103. // 复制json片段
  104. var fnCopy = function (ec) {
  105. var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
  106. if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
  107. txt = '{' + txt + '}';
  108. }
  109. try {
  110. txt = JSON.stringify(JSON.parse(txt), null, 4);
  111. } catch (err) {
  112. }
  113. _copyToClipboard(txt);
  114. };
  115. // 删除json片段
  116. var fnDel = function (ed) {
  117. if (el.parent().is('#formattedJson')) {
  118. alert('如果连最外层的Json也删掉的话,就没啥意义了哦!');
  119. return false;
  120. }
  121. el.remove();
  122. boxOpt.css('top', -1000);
  123. };
  124. var boxOpt = $('#boxOpt');
  125. if (!boxOpt.length) {
  126. boxOpt = $('<div id="boxOpt"><a class="opt-copy">复制</a>|<a class="opt-del">删除</a></div>').appendTo('body');
  127. }
  128. boxOpt.find('a.opt-copy').unbind('click').bind('click', fnCopy);
  129. boxOpt.find('a.opt-del').unbind('click').bind('click', fnDel);
  130. boxOpt.css({
  131. left: el.offset().left + el.width() - 50,
  132. top: el.offset().top
  133. });
  134. };
  135. /**
  136. * 事件绑定
  137. * @private
  138. */
  139. var _bindEvent = function () {
  140. // 点击区块高亮
  141. $('#jfContent').delegate('.kvov', 'click', function (e) {
  142. $('#jfContent .kvov').removeClass('x-outline');
  143. var el = $(this).removeClass('x-hover').addClass('x-outline');
  144. // 增加复制、删除功能
  145. _addOptForItem(el);
  146. if (!$(e.target).is('.kvov .e')) {
  147. e.stopPropagation();
  148. } else {
  149. $(e.target).parent().trigger('click');
  150. }
  151. }).delegate('.kvov', 'mouseover', function (e) {
  152. $(this).addClass('x-hover');
  153. return false;
  154. }).delegate('.kvov', 'mouseout', function (e) {
  155. $(this).removeClass('x-hover');
  156. });
  157. };
  158. /**
  159. * 执行format操作
  160. * @private
  161. */
  162. var _format = function () {
  163. var source = _getJsonText();
  164. if (!source) {
  165. return;
  166. }
  167. // JSONP形式下的callback name
  168. var funcName = null;
  169. // json对象
  170. var jsonObj = null;
  171. var newSource = '';
  172. // 下面校验给定字符串是否为一个合法的json
  173. try {
  174. // 再看看是不是jsonp的格式
  175. var reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/igm;
  176. var matches = reg.exec(source);
  177. if (matches != null) {
  178. funcName = matches[1];
  179. newSource = matches[2];
  180. jsonObj = new Function("return " + newSource)();
  181. } else {
  182. reg = /^([\{\[])/;
  183. if (!reg.test(source)) {
  184. return;
  185. }
  186. }
  187. } catch (ex) {
  188. return;
  189. }
  190. try {
  191. if (jsonObj == null || typeof jsonObj != 'object') {
  192. jsonObj = new Function("return " + source)();
  193. // 还要防止下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}"
  194. if (typeof jsonObj == "string") {
  195. // 再来一次
  196. jsonObj = new Function("return " + jsonObj)();
  197. }
  198. }
  199. } catch (e) {
  200. return;
  201. }
  202. // 是json格式,可以进行JSON自动格式化
  203. if (jsonObj != null && typeof jsonObj == "object") {
  204. try {
  205. // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理
  206. newSource = JSON.stringify(jsonObj);
  207. // 如果newSource的长度比原source长度短很多的话,猜测应该是格式化错了,需要撤销操作
  208. // 这里一定要unicode decode一下,要不然会出现误判
  209. if (newSource.length * 2 < (_uniDecode(source)).length) {
  210. return;
  211. }
  212. } catch (ex) {
  213. // 通过JSON反解不出来的,一定有问题
  214. return;
  215. }
  216. $('body').html(_htmlFragment);
  217. _loadCss();
  218. _bindEvent();
  219. JsonFormatEntrance.clear();
  220. JsonFormatEntrance.format(newSource);
  221. // 如果是JSONP格式的,需要把方法名也显示出来
  222. if (funcName != null) {
  223. $('#jfCallbackName_start').html(funcName + '(');
  224. $('#jfCallbackName_end').html(')');
  225. }
  226. }
  227. };
  228. var _init = function () {
  229. $(function () {
  230. if (!/^filesystem\:/.test(location.href)) {
  231. if (baidu.feOption.pageJsonMustFormat) {
  232. _format();
  233. } else {
  234. chrome.extension.sendMessage({
  235. type: MSG_TYPE.GET_OPTIONS,
  236. items: ['JSON_PAGE_FORMAT']
  237. }, function (opts) {
  238. if (opts.JSON_PAGE_FORMAT != 'false') {
  239. _format();
  240. }
  241. });
  242. }
  243. }
  244. });
  245. };
  246. return {
  247. init: _init
  248. };
  249. })();
  250. baidu.csJsonFormat.init();