index.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /**
  2. * FeHelper Code Compress
  3. */
  4. let editor = {};
  5. new Vue({
  6. el: '#pageContainer',
  7. data: {
  8. codeType: 'html',
  9. sourceContent: '',
  10. resultContent: '',
  11. hasError: false,
  12. compressInfo: ''
  13. },
  14. mounted: function () {
  15. editor = CodeMirror.fromTextArea(this.$refs.codeSource, {
  16. mode: "htmlmixed",
  17. lineNumbers: true,
  18. matchBrackets: true,
  19. styleActiveLine: true,
  20. lineWrapping: true
  21. });
  22. //输入框聚焦
  23. editor.focus();
  24. this.loadPatchHotfix();
  25. },
  26. methods: {
  27. loadPatchHotfix() {
  28. // 页面加载时自动获取并注入页面的补丁
  29. chrome.runtime.sendMessage({
  30. type: 'fh-dynamic-any-thing',
  31. thing: 'fh-get-tool-patch',
  32. toolName: 'code-compress'
  33. }, patch => {
  34. if (patch) {
  35. if (patch.css) {
  36. const style = document.createElement('style');
  37. style.textContent = patch.css;
  38. document.head.appendChild(style);
  39. }
  40. if (patch.js) {
  41. try {
  42. if (window.evalCore && window.evalCore.getEvalInstance) {
  43. window.evalCore.getEvalInstance(window)(patch.js);
  44. }
  45. } catch (e) {
  46. console.error('code-compress补丁JS执行失败', e);
  47. }
  48. }
  49. }
  50. });
  51. },
  52. compress: function () {
  53. this.hasError = false;
  54. this.compressInfo = '';
  55. this.sourceContent = editor.getValue().trim();
  56. if (!this.sourceContent) {
  57. alert('请先粘贴您需要压缩的代码');
  58. } else {
  59. if (this.codeType === 'js') {
  60. this.jsCompress(this.sourceContent);
  61. } else if (this.codeType === 'css') {
  62. this.cssCompress(this.sourceContent);
  63. } else {
  64. this.htmlCompress(this.sourceContent);
  65. }
  66. }
  67. },
  68. changeCodeType(ctype) {
  69. let editorMode = {
  70. css: 'text/css',
  71. js: {name: 'javascript', json: true},
  72. html: 'htmlmixed'
  73. };
  74. editor.setOption('mode', editorMode[ctype]);
  75. if (editor.getValue().trim()) {
  76. this.$nextTick(this.compress);
  77. }
  78. },
  79. buildCompressInfo(original, minified) {
  80. let commify = str => String(str).split('').reverse().join('').replace(/(...)(?!$)/g, '$1,').split('').reverse().join('');
  81. let diff = original.length - minified.length;
  82. let savings = original.length ? (100 * diff / minified.length).toFixed(2) : 0;
  83. this.compressInfo = '压缩前: <strong>' + commify(original.length) + '</strong>' +
  84. ',压缩后: <strong>' + commify(minified.length) + '</strong>' +
  85. ',压缩率: <strong>' + commify(diff) + ' (' + savings + '%)</strong>';
  86. },
  87. jsCompress(js) {
  88. // 判断是否为合法JSON,如果是则加t=前缀后用UglifyJS压缩,压缩后去掉t=前缀
  89. let isJSON = false;
  90. let jsonStr = js.trim();
  91. let result;
  92. try {
  93. if ((jsonStr.startsWith('{') || jsonStr.startsWith('[')) && (jsonStr.endsWith('}') || jsonStr.endsWith(']'))) {
  94. JSON.parse(jsonStr);
  95. isJSON = true;
  96. }
  97. } catch (e) {
  98. isJSON = false;
  99. }
  100. if (isJSON) {
  101. // 加t=前缀
  102. let jsWithPrefix = 't=' + jsonStr;
  103. result = UglifyJs3.compress(jsWithPrefix);
  104. this.hasError = !!result.error;
  105. if (!this.hasError && result.out.startsWith('t=')) {
  106. this.resultContent = result.out.substring(2); // 去掉t=
  107. } else {
  108. this.resultContent = result.out || result.error;
  109. }
  110. !this.hasError && this.buildCompressInfo(this.sourceContent, this.resultContent);
  111. return;
  112. }
  113. // 原有JS压缩逻辑
  114. result = UglifyJs3.compress(js);
  115. this.hasError = !!result.error;
  116. this.resultContent = result.out || result.error;
  117. !this.hasError && this.buildCompressInfo(this.sourceContent, this.resultContent);
  118. },
  119. cssCompress(css) {
  120. let res = css.replace(/\/\*(.|\n)*?\*\//g, "")
  121. .replace(/\s*([\{\}\:\;\,])\s*/g, "$1")
  122. .replace(/\,[\s\.\#\d]*\{/g, "{")
  123. .replace(/;\s*;/g, ";")
  124. .match(/^\s*(\S+(\s+\S+)*)\s*$/);
  125. this.resultContent = (res === null) ? css : res[1];
  126. this.buildCompressInfo(this.sourceContent, this.resultContent);
  127. },
  128. htmlCompress(html) {
  129. let options = {
  130. "caseSensitive": false,
  131. "collapseBooleanAttributes": true,
  132. "collapseInlineTagWhitespace": false,
  133. "collapseWhitespace": true,
  134. "conservativeCollapse": false,
  135. "decodeEntities": true,
  136. "html5": true,
  137. "includeAutoGeneratedTags": false,
  138. "keepClosingSlash": false,
  139. "minifyCSS": true,
  140. "minifyJS": true,
  141. "preserveLineBreaks": false,
  142. "preventAttributesEscaping": false,
  143. "processConditionalComments": true,
  144. "processScripts": ["text/html"],
  145. "removeAttributeQuotes": true,
  146. "removeComments": true,
  147. "removeEmptyAttributes": true,
  148. "removeEmptyElements": false,
  149. "removeOptionalTags": true,
  150. "removeRedundantAttributes": true,
  151. "removeScriptTypeAttributes": true,
  152. "removeStyleLinkTypeAttributes": true,
  153. "removeTagWhitespace": true,
  154. "sortAttributes": true,
  155. "sortClassName": true,
  156. "trimCustomFragments": true,
  157. "useShortDoctype": true
  158. };
  159. options.log = console.log;
  160. try {
  161. this.resultContent = require('html-minifier').minify(html, options);
  162. this.buildCompressInfo(this.sourceContent, this.resultContent);
  163. } catch (err) {
  164. this.hasError = true;
  165. this.resultContent = err;
  166. }
  167. },
  168. toast(content) {
  169. window.clearTimeout(window.feHelperAlertMsgTid);
  170. let elAlertMsg = document.querySelector("#fehelper_alertmsg");
  171. if (!elAlertMsg) {
  172. let elWrapper = document.createElement('div');
  173. elWrapper.innerHTML = '<div id="fehelper_alertmsg">' + content + '</div>';
  174. elAlertMsg = elWrapper.childNodes[0];
  175. document.body.appendChild(elAlertMsg);
  176. } else {
  177. elAlertMsg.innerHTML = content;
  178. elAlertMsg.style.display = 'block';
  179. }
  180. window.feHelperAlertMsgTid = window.setTimeout(function () {
  181. elAlertMsg.style.display = 'none';
  182. }, 3000);
  183. },
  184. copyToClipboard(text) {
  185. if (this.hasError) return false;
  186. let input = document.createElement('textarea');
  187. input.style.position = 'fixed';
  188. input.style.opacity = 0;
  189. input.value = text;
  190. document.body.appendChild(input);
  191. input.select();
  192. document.execCommand('Copy');
  193. document.body.removeChild(input);
  194. this.toast('压缩结果已复制成功,随处粘贴可用!');
  195. },
  196. loadExample(type,event) {
  197. if(event){
  198. event.preventDefault();
  199. }
  200. this.codeType = type;
  201. editor.setValue(EXAMPLES[type]);
  202. this.changeCodeType(type);
  203. },
  204. openOptionsPage: function(event) {
  205. event.preventDefault();
  206. event.stopPropagation();
  207. chrome.runtime.openOptionsPage();
  208. },
  209. openDonateModal: function(event ){
  210. event.preventDefault();
  211. event.stopPropagation();
  212. chrome.runtime.sendMessage({
  213. type: 'fh-dynamic-any-thing',
  214. thing: 'open-donate-modal',
  215. params: { toolName: 'code-compress' }
  216. });
  217. }
  218. }
  219. });