index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /**
  2. * FeHelper Json Format Tools
  3. */
  4. // 一些全局变量
  5. let editor = {};
  6. let LOCAL_KEY_OF_LAYOUT = 'local-layout-key';
  7. let JSON_LINT = 'jsonformat:json-lint-switch';
  8. let EDIT_ON_CLICK = 'jsonformat:edit-on-click';
  9. let AUTO_DECODE = 'jsonformat:auto-decode';
  10. new Vue({
  11. el: '#pageContainer',
  12. data: {
  13. defaultResultTpl: '<div class="x-placeholder"><img src="../json-format/json-demo.jpg" alt="json-placeholder"></div>',
  14. placeHolder: '',
  15. jsonFormattedSource: '',
  16. errorMsg: '',
  17. errorJsonCode: '',
  18. errorPos: '',
  19. jfCallbackName_start: '',
  20. jfCallbackName_end: '',
  21. jsonLintSwitch: true,
  22. autoDecode: false,
  23. fireChange: true,
  24. overrideJson: false
  25. },
  26. mounted: function () {
  27. // 自动开关灯控制
  28. DarkModeMgr.turnLightAuto();
  29. this.placeHolder = this.defaultResultTpl;
  30. this.autoDecode = localStorage.getItem(AUTO_DECODE);
  31. this.autoDecode = this.autoDecode === 'true';
  32. this.jsonLintSwitch = (localStorage.getItem(JSON_LINT) !== 'false');
  33. this.overrideJson = (localStorage.getItem(EDIT_ON_CLICK) === 'true');
  34. this.changeLayout(localStorage.getItem(LOCAL_KEY_OF_LAYOUT));
  35. editor = CodeMirror.fromTextArea(this.$refs.jsonBox, {
  36. mode: "text/javascript",
  37. lineNumbers: true,
  38. matchBrackets: true,
  39. styleActiveLine: true,
  40. lineWrapping: true
  41. });
  42. //输入框聚焦
  43. editor.focus();
  44. // 格式化以后的JSON,点击以后可以重置原内容
  45. window._OnJsonItemClickByFH = (jsonTxt) => {
  46. if (this.overrideJson) {
  47. this.disableEditorChange(jsonTxt);
  48. }
  49. };
  50. editor.on('change', (editor, changes) => {
  51. this.jsonFormattedSource = editor.getValue().replace(/\n/gm, ' ');
  52. this.fireChange && this.format();
  53. });
  54. // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
  55. if (location.protocol === 'chrome-extension:') {
  56. chrome.runtime.onMessage.addListener((request, sender, callback) => {
  57. if (request.type === 'TAB_CREATED_OR_UPDATED' && request.content && request.event === location.pathname.split('/')[1]) {
  58. editor.setValue(request.content || this.defaultResultTpl);
  59. this.format();
  60. }
  61. callback && callback();
  62. return true;
  63. });
  64. }
  65. },
  66. methods: {
  67. format: function () {
  68. this.errorMsg = '';
  69. this.placeHolder = this.defaultResultTpl;
  70. this.jfCallbackName_start = '';
  71. this.jfCallbackName_end = '';
  72. let source = editor.getValue().replace(/\n/gm, ' ');
  73. if (!source) {
  74. return false;
  75. }
  76. // JSONP形式下的callback name
  77. let funcName = null;
  78. // json对象
  79. let jsonObj = null;
  80. // 下面校验给定字符串是否为一个合法的json
  81. try {
  82. // 再看看是不是jsonp的格式
  83. let reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/igm;
  84. let matches = reg.exec(source);
  85. if (matches != null) {
  86. funcName = matches[1];
  87. source = matches[2];
  88. }
  89. // 这里可能会throw exception
  90. jsonObj = JSON.parse(source);
  91. } catch (ex) {
  92. // new Function的方式,能自动给key补全双引号,但是不支持bigint,所以是下下策,放在try-catch里搞
  93. try {
  94. jsonObj = new Function("return " + source)();
  95. } catch (exx) {
  96. try {
  97. // 再给你一次机会,是不是下面这种情况: "{\"ret\":\"0\", \"msg\":\"ok\"}"
  98. jsonObj = new Function("return '" + source + "'")();
  99. if (typeof jsonObj === 'string') {
  100. try {
  101. // 确保bigint不会失真
  102. jsonObj = JSON.parse(jsonObj);
  103. } catch (ie) {
  104. // 最后给你一次机会,是个字符串,老夫给你再转一次
  105. jsonObj = new Function("return " + jsonObj)();
  106. }
  107. }
  108. } catch (exxx) {
  109. this.errorMsg = exxx.message;
  110. }
  111. }
  112. }
  113. // 是json格式,可以进行JSON自动格式化
  114. if (jsonObj != null && typeof jsonObj === "object" && !this.errorMsg.length) {
  115. try {
  116. let sortType = document.querySelectorAll('[name=jsonsort]:checked')[0].value;
  117. if (sortType !== '0') {
  118. jsonObj = JsonABC.sortObj(jsonObj, parseInt(sortType), true);
  119. }
  120. source = JSON.stringify(jsonObj);
  121. } catch (ex) {
  122. // 通过JSON反解不出来的,一定有问题
  123. this.errorMsg = ex.message;
  124. }
  125. if (!this.errorMsg.length) {
  126. if (this.autoDecode) {
  127. (async () => {
  128. let txt = await JsonEnDecode.urlDecodeByFetch(source);
  129. source = JsonEnDecode.uniDecode(txt);
  130. Formatter.format(source);
  131. })();
  132. } else {
  133. Formatter.format(source);
  134. }
  135. this.placeHolder = '';
  136. this.jsonFormattedSource = source;
  137. // 如果是JSONP格式的,需要把方法名也显示出来
  138. if (funcName != null) {
  139. this.jfCallbackName_start = funcName + '(';
  140. this.jfCallbackName_end = ')';
  141. } else {
  142. this.jfCallbackName_start = '';
  143. this.jfCallbackName_end = '';
  144. }
  145. this.$nextTick(() => {
  146. this.updateWrapperHeight();
  147. })
  148. }
  149. }
  150. if (this.errorMsg.length) {
  151. if (this.jsonLintSwitch) {
  152. return this.lintOn();
  153. } else {
  154. this.placeHolder = '<span class="x-error">' + this.errorMsg + '</span>';
  155. return false;
  156. }
  157. }
  158. return true;
  159. },
  160. compress: function () {
  161. if (this.format()) {
  162. let jsonTxt = this.jfCallbackName_start + this.jsonFormattedSource + this.jfCallbackName_end;
  163. this.disableEditorChange(jsonTxt);
  164. }
  165. },
  166. autoDecodeFn: function () {
  167. this.$nextTick(() => {
  168. localStorage.setItem(AUTO_DECODE, this.autoDecode);
  169. this.format();
  170. });
  171. },
  172. uniEncode: function () {
  173. editor.setValue(JsonEnDecode.uniEncode(editor.getValue()));
  174. },
  175. uniDecode: function () {
  176. editor.setValue(JsonEnDecode.uniDecode(editor.getValue()));
  177. },
  178. urlDecode: function () {
  179. JsonEnDecode.urlDecodeByFetch(editor.getValue()).then(text => editor.setValue(text));
  180. },
  181. updateWrapperHeight: function () {
  182. let curLayout = localStorage.getItem(LOCAL_KEY_OF_LAYOUT);
  183. let elPc = document.querySelector('#pageContainer');
  184. if (curLayout === 'up-down') {
  185. elPc.style.height = 'auto';
  186. } else {
  187. elPc.style.height = Math.max(elPc.scrollHeight, document.body.scrollHeight) + 'px';
  188. }
  189. },
  190. changeLayout: function (type) {
  191. let elPc = document.querySelector('#pageContainer');
  192. if (type === 'up-down') {
  193. elPc.classList.remove('layout-left-right');
  194. elPc.classList.add('layout-up-down');
  195. this.$refs.btnLeftRight.classList.remove('selected');
  196. this.$refs.btnUpDown.classList.add('selected');
  197. } else {
  198. elPc.classList.remove('layout-up-down');
  199. elPc.classList.add('layout-left-right');
  200. this.$refs.btnLeftRight.classList.add('selected');
  201. this.$refs.btnUpDown.classList.remove('selected');
  202. }
  203. localStorage.setItem(LOCAL_KEY_OF_LAYOUT, type);
  204. this.updateWrapperHeight();
  205. },
  206. setCache: function () {
  207. this.$nextTick(() => {
  208. localStorage.setItem(EDIT_ON_CLICK, this.overrideJson);
  209. });
  210. },
  211. lintOn: function () {
  212. this.$nextTick(() => {
  213. localStorage.setItem(JSON_LINT, this.jsonLintSwitch);
  214. });
  215. if (!editor.getValue().trim()) {
  216. return true;
  217. }
  218. this.$nextTick(() => {
  219. if (!this.jsonLintSwitch) {
  220. return;
  221. }
  222. let lintResult = JsonLint.lintDetect(editor.getValue());
  223. if (!isNaN(lintResult.line)) {
  224. this.placeHolder = '<div id="errorTips">' +
  225. '<div id="tipsBox">错误位置:' + (lintResult.line + 1) + '行,' + (lintResult.col + 1) + '列;缺少字符或字符不正确</div>' +
  226. '<div id="errorCode">' + lintResult.dom + '</div></div>';
  227. }
  228. });
  229. return false;
  230. },
  231. disableEditorChange: function (jsonTxt) {
  232. this.fireChange = false;
  233. this.$nextTick(() => {
  234. editor.setValue(jsonTxt);
  235. this.$nextTick(() => {
  236. this.fireChange = true;
  237. })
  238. })
  239. },
  240. openOptionsPage: function(){
  241. chrome.runtime.openOptionsPage();
  242. },
  243. setDemo: function () {
  244. let demo = '{"BigIntSupported":995815895020119788889,"date":"20180322","message":"Success !","status":200,"city":"北京","count":632,"data":{"shidu":"34%","pm25":73,"pm10":91,"quality":"良","wendu":"5","ganmao":"极少数敏感人群应减少户外活动","yesterday":{"date":"21日星期三","sunrise":"06:19","high":"高温 11.0℃","low":"低温 1.0℃","sunset":"18:26","aqi":85,"fx":"南风","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"},"forecast":[{"date":"22日星期四","sunrise":"06:17","high":"高温 17.0℃","low":"低温 1.0℃","sunset":"18:27","aqi":98,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"23日星期五","sunrise":"06:16","high":"高温 18.0℃","low":"低温 5.0℃","sunset":"18:28","aqi":118,"fx":"无持续风向","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"},{"date":"24日星期六","sunrise":"06:14","high":"高温 21.0℃","low":"低温 7.0℃","sunset":"18:29","aqi":52,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"25日星期日","sunrise":"06:13","high":"高温 22.0℃","low":"低温 7.0℃","sunset":"18:30","aqi":71,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"26日星期一","sunrise":"06:11","high":"高温 21.0℃","low":"低温 8.0℃","sunset":"18:31","aqi":97,"fx":"西南风","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"}]}}';
  245. editor.setValue(demo);
  246. }
  247. }
  248. });