format-lib.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. /**
  2. * FeHelper Json Format Lib
  3. */
  4. let JsonFormatEntrance = (function () {
  5. "use strict";
  6. let jfContent,
  7. jfPre,
  8. jfStyleEl,
  9. jfOptEl,
  10. formattingMsg;
  11. let lastKvovIdGiven = 0;
  12. let cachedJsonString = '';
  13. let _initElements = function () {
  14. jfContent = $('#jfContent');
  15. if (!jfContent[0]) {
  16. jfContent = $('<div id="jfContent" />').appendTo('body');
  17. }
  18. jfPre = $('#jfContent_pre');
  19. if (!jfPre[0]) {
  20. jfPre = $('<pre id="jfContent_pre" />').appendTo('body');
  21. }
  22. jfStyleEl = $('#jfStyleEl');
  23. if (!jfStyleEl[0]) {
  24. jfStyleEl = $('<style id="jfStyleEl" />').appendTo('head');
  25. }
  26. formattingMsg = $('#formattingMsg');
  27. if (!formattingMsg[0]) {
  28. formattingMsg = $('<div id="formattingMsg"><span class="x-loading"></span>格式化中...</div>').appendTo('body');
  29. }
  30. jfOptEl = $('#boxOpt');
  31. if (!jfOptEl.length) {
  32. jfOptEl = $('<div id="boxOpt"><a class="opt-download" target="_blank">下载</a>|<a class="opt-copy">复制</a>|<a class="opt-del">删除</a></div>').appendTo('body');
  33. }
  34. try {
  35. jfContent.html('').show();
  36. jfPre.html('').hide();
  37. jfOptEl.hide();
  38. formattingMsg.hide();
  39. } catch (e) {
  40. }
  41. };
  42. // Add listener to receive response from BG when ready
  43. let postMessage = function (msg) {
  44. switch (msg[0]) {
  45. case 'NOT JSON' :
  46. jfPre.show();
  47. jfContent.html('<span class="x-json-tips">JSON不合法,请检查:</span>');
  48. break;
  49. case 'FORMATTING' :
  50. formattingMsg.show();
  51. break;
  52. case 'FORMATTED' :
  53. formattingMsg.hide();
  54. jfContent.html(msg[1]);
  55. _loadJs();
  56. _buildOptionBar();
  57. // 事件绑定
  58. _addEvents();
  59. // 支持文件下载
  60. _downloadSupport(cachedJsonString);
  61. break;
  62. default :
  63. throw new Error('Message not understood: ' + msg[0]);
  64. }
  65. };
  66. /**
  67. * 执行代码格式化
  68. * @param {[type]} jsonStr [description]
  69. * @return {[type]}
  70. */
  71. let format = function (jsonStr) {
  72. cachedJsonString = JSON.stringify(JSON.parse(jsonStr), null, 4);
  73. _initElements();
  74. jfPre.html(cachedJsonString);
  75. JsonFormatDealer.postMessage({
  76. type: "SENDING TEXT",
  77. text: jsonStr,
  78. length: jsonStr.length
  79. });
  80. };
  81. let _loadJs = function () {
  82. if (typeof Tarp === 'object') {
  83. Tarp.require('../static/js/utils.js');
  84. } else {
  85. alert('无法加载Tarp.require.js');
  86. }
  87. };
  88. /**
  89. * 直接下载,能解决中文乱码
  90. * @param content
  91. * @private
  92. */
  93. let _downloadSupport = function (content) {
  94. // 下载链接
  95. let localUrl = location.href;
  96. let dt = (new Date()).format('yyyyMMddHHmmss');
  97. content = ['/* ', localUrl, ' */', '\n', content].join('');
  98. let blob = new Blob([content], {type: 'application/octet-stream'});
  99. let button = $('<button id="btnDownload">下载JSON</button>').appendTo('#optionBar');
  100. if (typeof chrome === 'undefined' || !chrome.permissions) {
  101. button.click(function (e) {
  102. let aLink = $('<a id="btnDownload" target="_blank" title="保存到本地">下载JSON数据</a>');
  103. aLink.attr('download', 'FeHelper-' + dt + '.json');
  104. aLink.attr('href', URL.createObjectURL(blob));
  105. aLink[0].click();
  106. });
  107. } else {
  108. button.click(function (e) {
  109. // 请求权限
  110. chrome.permissions.request({
  111. permissions: ['downloads']
  112. }, (granted) => {
  113. if (granted) {
  114. chrome.downloads.download({
  115. url: URL.createObjectURL(blob),
  116. saveAs: true,
  117. conflictAction: 'overwrite',
  118. filename: 'FeHelper-' + dt + '.json'
  119. });
  120. } else {
  121. alert('必须接受授权,才能正常下载!');
  122. }
  123. });
  124. });
  125. }
  126. };
  127. /**
  128. * chrome 下复制到剪贴板
  129. * @param text
  130. */
  131. let _copyToClipboard = function (text) {
  132. let input = document.createElement('textarea');
  133. input.style.position = 'fixed';
  134. input.style.opacity = 0;
  135. input.value = text;
  136. document.body.appendChild(input);
  137. input.select();
  138. document.execCommand('Copy');
  139. document.body.removeChild(input);
  140. alert('Json片段复制成功,随处粘贴可用!')
  141. };
  142. /**
  143. * 从el中获取json文本
  144. * @param el
  145. * @returns {string}
  146. */
  147. let getJsonText = function (el) {
  148. let txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
  149. if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
  150. txt = '{' + txt + '}';
  151. }
  152. try {
  153. txt = JSON.stringify(JSON.parse(txt), null, 4);
  154. } catch (err) {
  155. }
  156. return txt;
  157. };
  158. /**
  159. * 给某个节点增加操作项
  160. * @param el
  161. * @private
  162. */
  163. let _addOptForItem = function (el) {
  164. // 下载json片段
  165. let fnDownload = function (ec) {
  166. let txt = getJsonText(el);
  167. // 下载片段
  168. let dt = (new Date()).format('yyyyMMddHHmmss');
  169. let blob = new Blob([txt], {type: 'application/octet-stream'});
  170. if (typeof chrome === 'undefined' || !chrome.permissions) {
  171. // 下载JSON的简单形式
  172. $(this).attr('download', 'FeHelper-' + dt + '.json').attr('href', URL.createObjectURL(blob));
  173. } else {
  174. // 请求权限
  175. chrome.permissions.request({
  176. permissions: ['downloads']
  177. }, (granted) => {
  178. if (granted) {
  179. chrome.downloads.download({
  180. url: URL.createObjectURL(blob),
  181. saveAs: true,
  182. conflictAction: 'overwrite',
  183. filename: 'FeHelper-' + dt + '.json'
  184. });
  185. } else {
  186. alert('必须接受授权,才能正常下载!');
  187. }
  188. });
  189. }
  190. };
  191. // 复制json片段
  192. let fnCopy = function (ec) {
  193. _copyToClipboard(getJsonText(el));
  194. };
  195. // 删除json片段
  196. let fnDel = function (ed) {
  197. if (el.parent().is('#formattedJson')) {
  198. alert('如果连最外层的Json也删掉的话,就没啥意义了哦!');
  199. return false;
  200. }
  201. alert('节点已删除成功!');
  202. el.remove();
  203. jfOptEl.css('top', -1000).hide();
  204. };
  205. jfOptEl.find('a.opt-download').unbind('click').bind('click', fnDownload);
  206. jfOptEl.find('a.opt-copy').unbind('click').bind('click', fnCopy);
  207. jfOptEl.find('a.opt-del').unbind('click').bind('click', fnDel);
  208. jfOptEl.css({
  209. left: el.offset().left + el.width() - 90,
  210. top: el.offset().top
  211. }).show();
  212. };
  213. /**
  214. * 折叠所有
  215. * @param elements
  216. */
  217. function collapse(elements) {
  218. let el;
  219. $.each(elements, function (i) {
  220. el = $(this);
  221. if (el.children('.blockInner').length) {
  222. el.addClass('collapsed');
  223. if (!el.attr('id')) {
  224. el.attr('id', 'kvov' + (++lastKvovIdGiven));
  225. let count = el.children('.blockInner').eq(0).children().length;
  226. // Generate comment text eg "4 items"
  227. let comment = count + (count === 1 ? ' item' : ' items');
  228. // Add CSS that targets it
  229. jfStyleEl[0].insertAdjacentHTML(
  230. 'beforeend',
  231. '\n#kvov' + lastKvovIdGiven + '.collapsed:after{color: #aaa; content:" // ' + comment + '"}'
  232. );
  233. }
  234. }
  235. });
  236. }
  237. /**
  238. * 创建几个全局操作的按钮,置于页面右上角即可
  239. * @private
  240. */
  241. let _buildOptionBar = function () {
  242. let optionBar = $('#optionBar');
  243. if (optionBar) {
  244. optionBar.remove();
  245. }
  246. optionBar = $('<div id="optionBar" />').appendTo(jfContent.parent());
  247. let buttonFormatted = $('<button id="buttonFormatted" class="selected">元数据</button>').appendTo(optionBar);
  248. let buttonCollapseAll = $('<button id="buttonCollapseAll">折叠所有</button>').appendTo(optionBar);
  249. let plainOn = false;
  250. buttonFormatted.bind('click', function (e) {
  251. if (plainOn) {
  252. plainOn = false;
  253. jfPre.hide();
  254. jfContent.show();
  255. buttonFormatted.text('元数据');
  256. } else {
  257. plainOn = true;
  258. jfPre.show();
  259. jfContent.hide();
  260. buttonFormatted.text('格式化');
  261. }
  262. optionBar.find('button').removeClass('selected');
  263. buttonFormatted.addClass('selected');
  264. jfOptEl.hide();
  265. });
  266. buttonCollapseAll.bind('click', function (e) {
  267. // 如果内容还没有格式化过,需要再格式化一下
  268. if (plainOn) {
  269. buttonFormatted.trigger('click');
  270. }
  271. if (!plainOn) {
  272. if (buttonCollapseAll.text() === '折叠所有') {
  273. buttonCollapseAll.text('展开所有');
  274. collapse($('.objProp'));
  275. } else {
  276. buttonCollapseAll.text('折叠所有');
  277. $('.objProp').removeClass('collapsed');
  278. }
  279. optionBar.find('button').removeClass('selected');
  280. buttonCollapseAll.addClass('selected');
  281. }
  282. jfOptEl.hide();
  283. });
  284. };
  285. // 附加操作
  286. let _addEvents = function () {
  287. // 折叠、展开
  288. $('#jfContent span.e').bind('click', function (ev) {
  289. ev.preventDefault();
  290. let parentEl = $(this).parent();
  291. parentEl.toggleClass('collapsed');
  292. if (parentEl.hasClass('collapsed')) {
  293. collapse(parentEl);
  294. }
  295. });
  296. // 点击选中:高亮
  297. $('#jfContent .kvov').bind('click', function (e) {
  298. if ($(this).hasClass('x-outline')) {
  299. jfOptEl.hide();
  300. $(this).removeClass('x-outline');
  301. e.stopPropagation();
  302. return true;
  303. }
  304. $('.x-outline').removeClass('x-outline');
  305. let el = $(this).removeClass('x-hover').addClass('x-outline');
  306. // 增加复制、删除功能
  307. _addOptForItem(el);
  308. if (!$(e.target).is('.kvov .e')) {
  309. e.stopPropagation();
  310. } else {
  311. $(e.target).parent().trigger('click');
  312. }
  313. // 触发钩子
  314. if (typeof window._OnJsonItemClickByFH === 'function') {
  315. window._OnJsonItemClickByFH(getJsonText(el));
  316. }
  317. }).bind('mouseover', function (e) {
  318. $(this).addClass('x-hover');
  319. return false;
  320. }).bind('mouseout', function (e) {
  321. $(this).removeClass('x-hover');
  322. });
  323. };
  324. return {
  325. format: format,
  326. postMessage: postMessage
  327. }
  328. })();
  329. let JsonFormatDealer = (function () {
  330. "use strict";
  331. // Constants
  332. let
  333. TYPE_STRING = 1,
  334. TYPE_NUMBER = 2,
  335. TYPE_OBJECT = 3,
  336. TYPE_ARRAY = 4,
  337. TYPE_BOOL = 5,
  338. TYPE_NULL = 6
  339. ;
  340. // Utility functions
  341. function removeComments(str) {
  342. str = ('__' + str + '__').split('');
  343. let mode = {
  344. singleQuote: false,
  345. doubleQuote: false,
  346. regex: false,
  347. blockComment: false,
  348. lineComment: false,
  349. condComp: false
  350. };
  351. for (let i = 0, l = str.length; i < l; i++) {
  352. if (mode.regex) {
  353. if (str[i] === '/' && str[i - 1] !== '\\') {
  354. mode.regex = false;
  355. }
  356. continue;
  357. }
  358. if (mode.singleQuote) {
  359. if (str[i] === "'" && str[i - 1] !== '\\') {
  360. mode.singleQuote = false;
  361. }
  362. continue;
  363. }
  364. if (mode.doubleQuote) {
  365. if (str[i] === '"' && str[i - 1] !== '\\') {
  366. mode.doubleQuote = false;
  367. }
  368. continue;
  369. }
  370. if (mode.blockComment) {
  371. if (str[i] === '*' && str[i + 1] === '/') {
  372. str[i + 1] = '';
  373. mode.blockComment = false;
  374. }
  375. str[i] = '';
  376. continue;
  377. }
  378. if (mode.lineComment) {
  379. if (str[i + 1] === '\n' || str[i + 1] === '\r') {
  380. mode.lineComment = false;
  381. }
  382. str[i] = '';
  383. continue;
  384. }
  385. if (mode.condComp) {
  386. if (str[i - 2] === '@' && str[i - 1] === '*' && str[i] === '/') {
  387. mode.condComp = false;
  388. }
  389. continue;
  390. }
  391. mode.doubleQuote = str[i] === '"';
  392. mode.singleQuote = str[i] === "'";
  393. if (str[i] === '/') {
  394. if (str[i + 1] === '*' && str[i + 2] === '@') {
  395. mode.condComp = true;
  396. continue;
  397. }
  398. if (str[i + 1] === '*') {
  399. str[i] = '';
  400. mode.blockComment = true;
  401. continue;
  402. }
  403. if (str[i + 1] === '/') {
  404. str[i] = '';
  405. mode.lineComment = true;
  406. continue;
  407. }
  408. mode.regex = true;
  409. }
  410. }
  411. return str.join('').slice(2, -2);
  412. }
  413. // Template elements
  414. let templates,
  415. baseDiv = document.createElement('div'),
  416. baseSpan = document.createElement('span');
  417. function getSpanBoth(innerText, className) {
  418. let span = baseSpan.cloneNode(false);
  419. span.className = className;
  420. span.innerText = innerText;
  421. return span;
  422. }
  423. function getSpanText(innerText) {
  424. let span = baseSpan.cloneNode(false);
  425. span.innerText = innerText;
  426. return span;
  427. }
  428. function getSpanClass(className) {
  429. let span = baseSpan.cloneNode(false);
  430. span.className = className;
  431. return span;
  432. }
  433. function getDivClass(className) {
  434. let span = baseDiv.cloneNode(false);
  435. span.className = className;
  436. return span;
  437. }
  438. // Create template nodes
  439. let templatesObj = {
  440. t_kvov: getDivClass('kvov'),
  441. t_key: getSpanClass('k'),
  442. t_string: getSpanClass('s'),
  443. t_number: getSpanClass('n'),
  444. t_exp: getSpanClass('e'),
  445. t_null: getSpanBoth('null', 'nl'),
  446. t_true: getSpanBoth('true', 'bl'),
  447. t_false: getSpanBoth('false', 'bl'),
  448. t_oBrace: getSpanBoth('{', 'b'),
  449. t_cBrace: getSpanBoth('}', 'b'),
  450. t_oBracket: getSpanBoth('[', 'b'),
  451. t_cBracket: getSpanBoth(']', 'b'),
  452. t_ellipsis: getSpanClass('ell'),
  453. t_blockInner: getSpanClass('blockInner'),
  454. t_colonAndSpace: document.createTextNode(':\u00A0'),
  455. t_commaText: document.createTextNode(','),
  456. t_dblqText: document.createTextNode('"')
  457. };
  458. // Core recursive DOM-building function
  459. function getKvovDOM(value, keyName) {
  460. let type,
  461. kvov,
  462. nonZeroSize,
  463. templates = templatesObj, // bring into scope for tiny speed boost
  464. objKey,
  465. keySpan,
  466. valueElement
  467. ;
  468. // Establish value type
  469. if (typeof value === 'string')
  470. type = TYPE_STRING;
  471. else if (typeof value === 'number')
  472. type = TYPE_NUMBER;
  473. else if (value === false || value === true)
  474. type = TYPE_BOOL;
  475. else if (value === null)
  476. type = TYPE_NULL;
  477. else if (value instanceof Array)
  478. type = TYPE_ARRAY;
  479. else
  480. type = TYPE_OBJECT;
  481. // Root node for this kvov
  482. kvov = templates.t_kvov.cloneNode(false);
  483. // Add an 'expander' first (if this is object/array with non-zero size)
  484. if (type === TYPE_OBJECT || type === TYPE_ARRAY) {
  485. if (typeof JSON.BigNumber === 'function' && value instanceof JSON.BigNumber) {
  486. value = JSON.stringify(value);
  487. type = TYPE_NUMBER;
  488. } else {
  489. nonZeroSize = false;
  490. for (objKey in value) {
  491. if (value.hasOwnProperty(objKey)) {
  492. nonZeroSize = true;
  493. break; // no need to keep counting; only need one
  494. }
  495. }
  496. if (nonZeroSize)
  497. kvov.appendChild(templates.t_exp.cloneNode(true));
  498. }
  499. }
  500. // If there's a key, add that before the value
  501. if (keyName !== false) { // NB: "" is a legal keyname in JSON
  502. // This kvov must be an object property
  503. kvov.classList.add('objProp');
  504. // Create a span for the key name
  505. keySpan = templates.t_key.cloneNode(false);
  506. keySpan.textContent = JSON.stringify(keyName).slice(1, -1); // remove quotes
  507. // Add it to kvov, with quote marks
  508. kvov.appendChild(templates.t_dblqText.cloneNode(false));
  509. kvov.appendChild(keySpan);
  510. kvov.appendChild(templates.t_dblqText.cloneNode(false));
  511. // Also add ":&nbsp;" (colon and non-breaking space)
  512. kvov.appendChild(templates.t_colonAndSpace.cloneNode(false));
  513. }
  514. else {
  515. // This is an array element instead
  516. kvov.classList.add('arrElem');
  517. }
  518. // Generate DOM for this value
  519. let blockInner, childKvov;
  520. switch (type) {
  521. case TYPE_STRING:
  522. // If string is a URL, get a link, otherwise get a span
  523. let innerStringEl = baseSpan.cloneNode(false),
  524. escapedString = JSON.stringify(value);
  525. escapedString = escapedString.substring(1, escapedString.length - 1); // remove quotes
  526. 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.
  527. let innerStringA = document.createElement('A');
  528. innerStringA.href = value;
  529. innerStringA.innerText = escapedString;
  530. innerStringEl.appendChild(innerStringA);
  531. }
  532. else {
  533. innerStringEl.innerText = escapedString;
  534. }
  535. valueElement = templates.t_string.cloneNode(false);
  536. valueElement.appendChild(templates.t_dblqText.cloneNode(false));
  537. valueElement.appendChild(innerStringEl);
  538. valueElement.appendChild(templates.t_dblqText.cloneNode(false));
  539. kvov.appendChild(valueElement);
  540. break;
  541. case TYPE_NUMBER:
  542. // Simply add a number element (span.n)
  543. valueElement = templates.t_number.cloneNode(false);
  544. valueElement.innerText = value;
  545. kvov.appendChild(valueElement);
  546. break;
  547. case TYPE_OBJECT:
  548. // Add opening brace
  549. kvov.appendChild(templates.t_oBrace.cloneNode(true));
  550. // If any properties, add a blockInner containing k/v pair(s)
  551. if (nonZeroSize) {
  552. // Add ellipsis (empty, but will be made to do something when kvov is collapsed)
  553. kvov.appendChild(templates.t_ellipsis.cloneNode(false));
  554. // Create blockInner, which indents (don't attach yet)
  555. blockInner = templates.t_blockInner.cloneNode(false);
  556. // For each key/value pair, add as a kvov to blockInner
  557. let count = 0, k, comma;
  558. for (k in value) {
  559. if (value.hasOwnProperty(k)) {
  560. count++;
  561. childKvov = getKvovDOM(value[k], k);
  562. // Add comma
  563. comma = templates.t_commaText.cloneNode();
  564. childKvov.appendChild(comma);
  565. blockInner.appendChild(childKvov);
  566. }
  567. }
  568. // Now remove the last comma
  569. childKvov.removeChild(comma);
  570. // Add blockInner
  571. kvov.appendChild(blockInner);
  572. }
  573. // Add closing brace
  574. kvov.appendChild(templates.t_cBrace.cloneNode(true));
  575. break;
  576. case TYPE_ARRAY:
  577. // Add opening bracket
  578. kvov.appendChild(templates.t_oBracket.cloneNode(true));
  579. // If non-zero length array, add blockInner containing inner vals
  580. if (nonZeroSize) {
  581. // Add ellipsis
  582. kvov.appendChild(templates.t_ellipsis.cloneNode(false));
  583. // Create blockInner (which indents) (don't attach yet)
  584. blockInner = templates.t_blockInner.cloneNode(false);
  585. // For each key/value pair, add the markup
  586. for (let i = 0, length = value.length, lastIndex = length - 1; i < length; i++) {
  587. // Make a new kvov, with no key
  588. childKvov = getKvovDOM(value[i], false);
  589. // Add comma if not last one
  590. if (i < lastIndex)
  591. childKvov.appendChild(templates.t_commaText.cloneNode());
  592. // Append the child kvov
  593. blockInner.appendChild(childKvov);
  594. }
  595. // Add blockInner
  596. kvov.appendChild(blockInner);
  597. }
  598. // Add closing bracket
  599. kvov.appendChild(templates.t_cBracket.cloneNode(true));
  600. break;
  601. case TYPE_BOOL:
  602. if (value)
  603. kvov.appendChild(templates.t_true.cloneNode(true));
  604. else
  605. kvov.appendChild(templates.t_false.cloneNode(true));
  606. break;
  607. case TYPE_NULL:
  608. kvov.appendChild(templates.t_null.cloneNode(true));
  609. break;
  610. }
  611. return kvov;
  612. }
  613. // Function to convert object to an HTML string
  614. function jsonObjToHTML(obj, jsonpFunctionName) {
  615. // spin(5) ;
  616. // Format object (using recursive kvov builder)
  617. let rootKvov = getKvovDOM(obj, false);
  618. // The whole DOM is now built.
  619. // Set class on root node to identify it
  620. rootKvov.classList.add('rootKvov');
  621. // Make div#formattedJson and append the root kvov
  622. let divFormattedJson = document.createElement('DIV');
  623. divFormattedJson.id = 'formattedJson';
  624. divFormattedJson.appendChild(rootKvov);
  625. // Convert it to an HTML string (shame about this step, but necessary for passing it through to the content page)
  626. let returnHTML = divFormattedJson.outerHTML;
  627. // Top and tail with JSONP padding if necessary
  628. if (jsonpFunctionName !== null) {
  629. returnHTML =
  630. '<div id="jsonpOpener">' + jsonpFunctionName + ' ( </div>' +
  631. returnHTML +
  632. '<div id="jsonpCloser">)</div>';
  633. }
  634. // Return the HTML
  635. return returnHTML;
  636. }
  637. // Listen for requests from content pages wanting to set up a port
  638. let postMessage = function (msg) {
  639. let jsonpFunctionName = null;
  640. if (msg.type === 'SENDING TEXT') {
  641. // Try to parse as JSON
  642. let obj,
  643. text = msg.text;
  644. try {
  645. obj = JSON.parse(text);
  646. }
  647. catch (e) {
  648. // Not JSON; could be JSONP though.
  649. // Try stripping 'padding' (if any), and try parsing it again
  650. text = text.trim();
  651. // Find where the first paren is (and exit if none)
  652. let indexOfParen;
  653. if (!(indexOfParen = text.indexOf('('))) {
  654. JsonFormatEntrance.postMessage(['NOT JSON', 'no opening parenthesis']);
  655. return;
  656. }
  657. // Get the substring up to the first "(", with any comments/whitespace stripped out
  658. let firstBit = removeComments(text.substring(0, indexOfParen)).trim();
  659. if (!firstBit.match(/^[a-zA-Z_$][\.\[\]'"0-9a-zA-Z_$]*$/)) {
  660. // The 'firstBit' is NOT a valid function identifier.
  661. JsonFormatEntrance.postMessage(['NOT JSON', 'first bit not a valid function name']);
  662. return;
  663. }
  664. // Find last parenthesis (exit if none)
  665. let indexOfLastParen;
  666. if (!(indexOfLastParen = text.lastIndexOf(')'))) {
  667. JsonFormatEntrance.postMessage(['NOT JSON', 'no closing paren']);
  668. return;
  669. }
  670. // Check that what's after the last parenthesis is just whitespace, comments, and possibly a semicolon (exit if anything else)
  671. let lastBit = removeComments(text.substring(indexOfLastParen + 1)).trim();
  672. if (lastBit !== "" && lastBit !== ';') {
  673. JsonFormatEntrance.postMessage(['NOT JSON', 'last closing paren followed by invalid characters']);
  674. return;
  675. }
  676. // So, it looks like a valid JS function call, but we don't know whether it's JSON inside the parentheses...
  677. // Check if the 'argument' is actually JSON (and record the parsed result)
  678. text = text.substring(indexOfParen + 1, indexOfLastParen);
  679. try {
  680. obj = JSON.parse(text);
  681. }
  682. catch (e2) {
  683. // Just some other text that happens to be in a function call.
  684. // Respond as not JSON, and exit
  685. JsonFormatEntrance.postMessage(['NOT JSON', 'looks like a function call, but the parameter is not valid JSON']);
  686. return;
  687. }
  688. jsonpFunctionName = firstBit;
  689. }
  690. // If still running, we now have obj, which is valid JSON.
  691. // Ensure it's not a number or string (technically valid JSON, but no point prettifying it)
  692. if (typeof obj !== 'object' && typeof obj !== 'array') {
  693. JsonFormatEntrance.postMessage(['NOT JSON', 'technically JSON but not an object or array']);
  694. return;
  695. }
  696. JsonFormatEntrance.postMessage(['FORMATTING']);
  697. try {
  698. // 有的页面设置了 安全策略,连localStorage都不能用,setTimeout开启多线程就更别说了
  699. localStorage.getItem('just test : Blocked script execution in xxx?');
  700. // 在非UI线程中操作:异步。。。
  701. setTimeout(function () {
  702. // Do formatting
  703. let html = jsonObjToHTML(obj, jsonpFunctionName);
  704. // Post the HTML string to the content script
  705. JsonFormatEntrance.postMessage(['FORMATTED', html]);
  706. }, 0);
  707. } catch (ex) {
  708. // 错误信息类似:Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag.
  709. let html = jsonObjToHTML(obj, jsonpFunctionName);
  710. JsonFormatEntrance.postMessage(['FORMATTED', html]);
  711. }
  712. }
  713. };
  714. return {
  715. postMessage: postMessage
  716. };
  717. })();
  718. module.exports = {
  719. format: JsonFormatEntrance.format
  720. };