index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /**
  2. * FeHelper HTML转Markdown
  3. */
  4. let editor = null;
  5. let hashtoTimeoutId;
  6. let previewElm;
  7. let previewTextArea;
  8. new Vue({
  9. el: '#pageContainer',
  10. data: {
  11. showPreview: false,
  12. previewText: '效果预览',
  13. codeType: 'Markdown',
  14. nextCodeType: 'HTML',
  15. toolName: {
  16. HTML: 'HTML转Markdown',
  17. Markdown: 'Markdown编辑器'
  18. }
  19. },
  20. mounted: function () {
  21. this.init();
  22. this.loadPatchHotfix();
  23. },
  24. methods: {
  25. loadPatchHotfix() {
  26. // 页面加载时自动获取并注入页面的补丁
  27. chrome.runtime.sendMessage({
  28. type: 'fh-dynamic-any-thing',
  29. thing: 'fh-get-tool-patch',
  30. toolName: 'html2markdown'
  31. }, patch => {
  32. if (patch) {
  33. if (patch.css) {
  34. const style = document.createElement('style');
  35. style.textContent = patch.css;
  36. document.head.appendChild(style);
  37. }
  38. if (patch.js) {
  39. try {
  40. if (window.evalCore && window.evalCore.getEvalInstance) {
  41. window.evalCore.getEvalInstance(window)(patch.js);
  42. }
  43. } catch (e) {
  44. console.error('html2markdown补丁JS执行失败', e);
  45. }
  46. }
  47. }
  48. });
  49. },
  50. trans: function () {
  51. editor.setValue('');
  52. this.codeType = {HTML: 'Markdown', Markdown: 'HTML'}[this.codeType];
  53. this.nextCodeType = {HTML: 'Markdown', Markdown: 'HTML'}[this.nextCodeType];
  54. let classList = this.$refs.modMarkdownBox.classList;
  55. if (this.codeType === 'HTML') {
  56. classList.add('mode-h2m');
  57. previewElm.innerHTML = `<textarea disabled></textarea>`;
  58. previewTextArea = previewElm.querySelector('textarea');
  59. } else {
  60. classList.remove('mode-h2m');
  61. previewElm.innerHTML = '';
  62. }
  63. },
  64. /**
  65. * 初始化ctrl+s的保存
  66. */
  67. init() {
  68. previewElm = this.$refs.boxPreview;
  69. // ===========================editor初始化
  70. editor = CodeMirror.fromTextArea(this.$refs.elEditor, {
  71. mode: "gfm",
  72. lineNumbers: true,
  73. matchBrackets: true,
  74. lineWrapping: true,
  75. theme: 'default'
  76. });
  77. editor.on('change', this.updateHashAndPreview);
  78. // ===========================支持save-as
  79. window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  80. navigator.saveBlob = navigator.saveBlob || navigator.msSaveBlob || navigator.mozSaveBlob || navigator.webkitSaveBlob;
  81. window.saveAs = window.saveAs || window.webkitSaveAs || window.mozSaveAs || window.msSaveAs;
  82. document.addEventListener('keydown', (e) => {
  83. if (e.keyCode === 83 && (e.ctrlKey || e.metaKey)) {
  84. e.preventDefault();
  85. this.saveMarkdown('md');
  86. return false;
  87. }
  88. });
  89. // ===========================支持页面拖拽识别
  90. document.addEventListener('drop', function (e) {
  91. e.preventDefault();
  92. e.stopPropagation();
  93. let theFile = e.dataTransfer.files[0];
  94. let theReader = new FileReader();
  95. theReader.onload = function (e) {
  96. editor.setValue(e.target.result);
  97. };
  98. theReader.readAsText(theFile);
  99. }, false);
  100. this.initWithHash();
  101. },
  102. /**
  103. * 根据Hash进行更新编辑器
  104. */
  105. initWithHash() {
  106. if (this.codeType === 'HTML') return;
  107. if (window.location.hash) {
  108. let h = window.location.hash.replace(/^#/, '');
  109. if (h.slice(0, 5) === 'view:') {
  110. let val = decodeURIComponent(escape(RawDeflate.inflate(atob(h.slice(5)))));
  111. previewElm.innerHTML = marked(val);
  112. previewElm.querySelectorAll('pre code').forEach((block) => {
  113. hljs.highlightBlock(block);
  114. });
  115. document.body.className = 'view';
  116. } else {
  117. editor.setValue(decodeURIComponent(escape(RawDeflate.inflate(atob(h)))));
  118. this.updateHashAndPreview(editor);
  119. editor.focus();
  120. }
  121. } else {
  122. this.updateHashAndPreview(editor);
  123. editor.focus();
  124. }
  125. },
  126. /**
  127. * 更新预览区域
  128. */
  129. updateHashAndPreview() {
  130. try {
  131. if (this.codeType === 'HTML') {
  132. previewTextArea.value = h2m(editor.getValue(), {
  133. converter: 'CommonMark' // CommonMark | MarkdownExtra
  134. });
  135. } else {
  136. previewElm.innerHTML = marked(editor.getValue());
  137. previewElm.querySelectorAll('pre code').forEach((block) => {
  138. hljs.highlightBlock(block);
  139. });
  140. clearTimeout(hashtoTimeoutId);
  141. hashtoTimeoutId = setTimeout(function () {
  142. window.location.hash = btoa(RawDeflate.deflate(unescape(encodeURIComponent(editor.getValue()))))
  143. }, 1000);
  144. }
  145. } catch (e) {
  146. console.log(e);
  147. }
  148. },
  149. saveMarkdown(type) {
  150. let date = new Date();
  151. let name = "FH-" + date.getFullYear() + (date.getMonth() + 1) + date.getDate()
  152. + date.getHours() + date.getMinutes() + date.getSeconds() + `.${type}`;
  153. let code = editor.getValue();
  154. if (this.codeType === 'HTML') {
  155. if (type !== 'html') {
  156. code = previewTextArea.value;
  157. }
  158. } else {
  159. if (type === 'html') {
  160. code = DemoTpl.exportHtml.replace('#title#', name).replace('#style#', DemoTpl.exportCss).replace('#html#', this.getParsedHtml());
  161. }
  162. }
  163. let blob = new Blob([code], {type: type === 'md' ? 'text/plain' : 'text/html'});
  164. if (window.saveAs) {
  165. window.saveAs(blob, name);
  166. } else if (navigator.saveBlob) {
  167. navigator.saveBlob(blob, name);
  168. } else {
  169. let url = URL.createObjectURL(blob);
  170. let link = document.createElement("a");
  171. link.setAttribute("href", url);
  172. link.setAttribute("download", name);
  173. let event = document.createEvent('MouseEvents');
  174. event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
  175. link.dispatchEvent(event);
  176. }
  177. },
  178. /**
  179. * 获取编译后的
  180. * @returns {*}
  181. */
  182. getParsedHtml() {
  183. return previewElm.innerHTML;
  184. },
  185. insert(type) {
  186. let textConfig = {
  187. b: '** text-here **',
  188. i: '* text-here *',
  189. quote: '\n> text-here ',
  190. code: '\n```javascript\n\n\n```\n',
  191. 'unordered-list': '\n\n- text-here\n- text-here\n- text-here\n',
  192. 'ordered-list': '\n\n1. text-here\n2. text-here\n3. text-here\n',
  193. link: '\n[text-here](your-link-url)',
  194. image: '\n![text-here](your-image-src)'
  195. };
  196. editor.replaceSelection(textConfig[type] || '');
  197. editor.focus();
  198. },
  199. getResult: function () {
  200. this.$refs.rstCode.select();
  201. },
  202. setDemo: function () {
  203. editor.setValue(DemoTpl[this.codeType.toLowerCase()]);
  204. },
  205. // 导入内容
  206. importContent: function () {
  207. let that = this;
  208. let fileInput = document.getElementById('fileInput');
  209. if (!fileInput) {
  210. fileInput = document.createElement('input');
  211. fileInput.id = 'fileInput';
  212. fileInput.type = 'file';
  213. fileInput.accept = {HTML: 'text/html', Markdown: 'text/x-markdown'}[that.codeType];
  214. fileInput.style.cssText = 'position:relative;top:-1000px;left:-1000px;';
  215. fileInput.onchange = function (event) {
  216. let reader = new FileReader();
  217. reader.readAsText(fileInput.files[0], 'utf-8');
  218. reader.onload = (evt) => {
  219. editor.setValue(evt.target.result);
  220. document.body.removeChild(fileInput);
  221. };
  222. };
  223. document.body.appendChild(fileInput);
  224. }
  225. fileInput.click();
  226. },
  227. togglePreview() {
  228. let classList = this.$refs.modMarkdownBox.classList;
  229. let closeClass = 'preview-closed';
  230. if (classList.contains(closeClass)) {
  231. classList.remove(closeClass);
  232. } else {
  233. classList.add(closeClass);
  234. }
  235. },
  236. // 通过调用系统打印的形式,打印为pdf
  237. exportContent: function (previewMode) {
  238. let newContent = "<html><head><meta charset='utf-8'/><title></title>" +
  239. "<style>" + DemoTpl.printCss + "</style>" +
  240. "</head><body class='markdown-body'>" + this.getParsedHtml() + "</body></html>";
  241. let newWin = window.open();
  242. newWin.focus();
  243. newWin.document.write(newContent);
  244. if (!previewMode) {
  245. newWin.print();
  246. newWin.document.close();
  247. newWin.close();
  248. }
  249. },
  250. openOptionsPage: function(event) {
  251. event.preventDefault();
  252. event.stopPropagation();
  253. chrome.runtime.openOptionsPage();
  254. },
  255. openDonateModal: function(event) {
  256. event.preventDefault();
  257. event.stopPropagation();
  258. chrome.runtime.sendMessage({
  259. type: 'fh-dynamic-any-thing',
  260. thing: 'open-donate-modal',
  261. params: { toolName: 'html2markdown' }
  262. });
  263. }
  264. }
  265. });