index.js 8.6 KB

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