1
0

ui-editor.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * Copyright 2010-2020 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * The code in this file is free software: you can redistribute it and/or
  8. * modify it under the terms of the GNU Affero General Public License
  9. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * The code in this file is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  15. * General Public License for more details.
  16. *
  17. * As additional permission under GNU AGPL version 3 section 7, you may
  18. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  19. * AGPL normally required by section 4, provided you include this license
  20. * notice and a URL through which recipients can access the Corresponding
  21. * Source.
  22. */
  23. /* global browser, singlefile, window, document, prompt */
  24. singlefile.extension.ui.bg.editor = (() => {
  25. const editorElement = document.querySelector(".editor");
  26. const toolbarElement = document.querySelector(".toolbar");
  27. const highlightYellowButton = document.querySelector(".highlight-yellow-button");
  28. const highlightPinkButton = document.querySelector(".highlight-pink-button");
  29. const highlightBlueButton = document.querySelector(".highlight-blue-button");
  30. const highlightGreenButton = document.querySelector(".highlight-green-button");
  31. const highlightButtons = Array.from(document.querySelectorAll(".highlight-button"));
  32. const toggleNotesButton = document.querySelector(".toggle-notes-button");
  33. const toggleHighlightsButton = document.querySelector(".toggle-highlights-button");
  34. const removeHighlightButton = document.querySelector(".remove-highlight-button");
  35. const addYellowNoteButton = document.querySelector(".add-note-yellow-button");
  36. const addPinkNoteButton = document.querySelector(".add-note-pink-button");
  37. const addBlueNoteButton = document.querySelector(".add-note-blue-button");
  38. const addGreenNoteButton = document.querySelector(".add-note-green-button");
  39. const editPageButton = document.querySelector(".edit-page-button");
  40. const formatPageButton = document.querySelector(".format-page-button");
  41. const cutInnerPageButton = document.querySelector(".cut-inner-page-button");
  42. const cutOuterPageButton = document.querySelector(".cut-outer-page-button");
  43. const undoCutPageButton = document.querySelector(".undo-cut-page-button");
  44. const undoAllCutPageButton = document.querySelector(".undo-all-cut-page-button");
  45. const redoCutPageButton = document.querySelector(".redo-cut-page-button");
  46. const savePageButton = document.querySelector(".save-page-button");
  47. const printPageButton = document.querySelector(".print-page-button");
  48. let tabData, tabDataContents = [];
  49. addYellowNoteButton.title = browser.i18n.getMessage("editorAddYellowNote");
  50. addPinkNoteButton.title = browser.i18n.getMessage("editorAddPinkNote");
  51. addBlueNoteButton.title = browser.i18n.getMessage("editorAddBlueNote");
  52. addGreenNoteButton.title = browser.i18n.getMessage("editorAddGreenNote");
  53. highlightYellowButton.title = browser.i18n.getMessage("editorHighlightYellow");
  54. highlightPinkButton.title = browser.i18n.getMessage("editorHighlightPink");
  55. highlightBlueButton.title = browser.i18n.getMessage("editorHighlightBlue");
  56. highlightGreenButton.title = browser.i18n.getMessage("editorHighlightGreen");
  57. toggleNotesButton.title = browser.i18n.getMessage("editorToggleNotes");
  58. toggleHighlightsButton.title = browser.i18n.getMessage("editorToggleHighlights");
  59. removeHighlightButton.title = browser.i18n.getMessage("editorRemoveHighlight");
  60. editPageButton.title = browser.i18n.getMessage("editorEditPage");
  61. formatPageButton.title = browser.i18n.getMessage("editorFormatPage");
  62. cutInnerPageButton.title = browser.i18n.getMessage("editorCutInnerPage");
  63. cutOuterPageButton.title = browser.i18n.getMessage("editorCutOuterPage");
  64. undoCutPageButton.title = browser.i18n.getMessage("editorUndoCutPage");
  65. undoAllCutPageButton.title = browser.i18n.getMessage("editorUndoAllCutPage");
  66. redoCutPageButton.title = browser.i18n.getMessage("editorRedoCutPage");
  67. savePageButton.title = browser.i18n.getMessage("editorSavePage");
  68. printPageButton.title = browser.i18n.getMessage("editorPrintPage");
  69. addYellowNoteButton.onclick = () => editorElement.contentWindow.postMessage(JSON.stringify({ method: "addNote", color: "note-yellow" }), "*");
  70. addPinkNoteButton.onclick = () => editorElement.contentWindow.postMessage(JSON.stringify({ method: "addNote", color: "note-pink" }), "*");
  71. addBlueNoteButton.onclick = () => editorElement.contentWindow.postMessage(JSON.stringify({ method: "addNote", color: "note-blue" }), "*");
  72. addGreenNoteButton.onclick = () => editorElement.contentWindow.postMessage(JSON.stringify({ method: "addNote", color: "note-green" }), "*");
  73. editorElement.contentWindow.focus();
  74. document.onclick = () => editorElement.contentWindow.focus();
  75. highlightButtons.forEach(highlightButton => {
  76. highlightButton.onclick = () => {
  77. if (toolbarElement.classList.contains("cut-inner-mode")) {
  78. disableCutInnerPage();
  79. }
  80. if (toolbarElement.classList.contains("cut-outer-mode")) {
  81. disableCutOuterPage();
  82. }
  83. if (toolbarElement.classList.contains("remove-highlight-mode")) {
  84. disableRemoveHighlights();
  85. }
  86. const disabled = highlightButton.classList.contains("highlight-disabled");
  87. resetHighlightButtons();
  88. if (disabled) {
  89. highlightButton.classList.remove("highlight-disabled");
  90. editorElement.contentWindow.postMessage(JSON.stringify({ method: "enableHighlight", color: "single-file-highlight-" + highlightButton.dataset.color }), "*");
  91. } else {
  92. highlightButton.classList.add("highlight-disabled");
  93. }
  94. };
  95. });
  96. toggleNotesButton.onclick = () => {
  97. if (toggleNotesButton.getAttribute("src") == "/extension/ui/resources/button_note_visible.png") {
  98. toggleNotesButton.src = "/extension/ui/resources/button_note_hidden.png";
  99. editorElement.contentWindow.postMessage(JSON.stringify({ method: "hideNotes" }), "*");
  100. } else {
  101. toggleNotesButton.src = "/extension/ui/resources/button_note_visible.png";
  102. editorElement.contentWindow.postMessage(JSON.stringify({ method: "displayNotes" }), "*");
  103. }
  104. };
  105. toggleHighlightsButton.onclick = () => {
  106. if (toggleHighlightsButton.getAttribute("src") == "/extension/ui/resources/button_highlighter_visible.png") {
  107. toggleHighlightsButton.src = "/extension/ui/resources/button_highlighter_hidden.png";
  108. editorElement.contentWindow.postMessage(JSON.stringify({ method: "hideHighlights" }), "*");
  109. } else {
  110. displayHighlights();
  111. }
  112. };
  113. removeHighlightButton.onclick = () => {
  114. if (toolbarElement.classList.contains("cut-inner-mode")) {
  115. disableCutInnerPage();
  116. }
  117. if (toolbarElement.classList.contains("cut-outer-mode")) {
  118. disableCutOuterPage();
  119. }
  120. if (removeHighlightButton.classList.contains("remove-highlight-disabled")) {
  121. removeHighlightButton.classList.remove("remove-highlight-disabled");
  122. toolbarElement.classList.add("remove-highlight-mode");
  123. resetHighlightButtons();
  124. displayHighlights();
  125. editorElement.contentWindow.postMessage(JSON.stringify({ method: "enableRemoveHighlights" }), "*");
  126. editorElement.contentWindow.postMessage(JSON.stringify({ method: "displayHighlights" }), "*");
  127. } else {
  128. disableRemoveHighlights();
  129. }
  130. };
  131. editPageButton.onclick = () => {
  132. if (toolbarElement.classList.contains("cut-inner-mode")) {
  133. disableCutInnerPage();
  134. }
  135. if (toolbarElement.classList.contains("cut-outer-mode")) {
  136. disableCutOuterPage();
  137. }
  138. if (editPageButton.classList.contains("edit-disabled")) {
  139. enableEditPage();
  140. } else {
  141. disableEditPage();
  142. }
  143. };
  144. formatPageButton.onclick = () => {
  145. if (formatPageButton.classList.contains("format-disabled")) {
  146. formatPage();
  147. } else {
  148. cancelFormatPage();
  149. }
  150. };
  151. cutInnerPageButton.onclick = () => {
  152. if (toolbarElement.classList.contains("edit-mode")) {
  153. disableEditPage();
  154. }
  155. if (toolbarElement.classList.contains("cut-outer-mode")) {
  156. disableCutOuterPage();
  157. }
  158. if (cutInnerPageButton.classList.contains("cut-disabled")) {
  159. enableCutInnerPage();
  160. } else {
  161. disableCutInnerPage();
  162. }
  163. };
  164. cutOuterPageButton.onclick = () => {
  165. if (toolbarElement.classList.contains("edit-mode")) {
  166. disableEditPage();
  167. }
  168. if (toolbarElement.classList.contains("cut-inner-mode")) {
  169. disableCutInnerPage();
  170. }
  171. if (cutOuterPageButton.classList.contains("cut-disabled")) {
  172. enableCutOuterPage();
  173. } else {
  174. disableCutOuterPage();
  175. }
  176. };
  177. undoCutPageButton.onclick = () => {
  178. editorElement.contentWindow.postMessage(JSON.stringify({ method: "undoCutPage" }), "*");
  179. };
  180. undoAllCutPageButton.onclick = () => {
  181. editorElement.contentWindow.postMessage(JSON.stringify({ method: "undoAllCutPage" }), "*");
  182. };
  183. redoCutPageButton.onclick = () => {
  184. editorElement.contentWindow.postMessage(JSON.stringify({ method: "redoCutPage" }), "*");
  185. };
  186. savePageButton.onclick = () => {
  187. savePage();
  188. };
  189. printPageButton.onclick = () => {
  190. editorElement.contentWindow.postMessage(JSON.stringify({ method: "printPage" }), "*");
  191. };
  192. let updatedResources = {};
  193. window.onmessage = event => {
  194. const message = JSON.parse(event.data);
  195. if (message.method == "setMetadata") {
  196. document.title = "[SingleFile] " + message.title;
  197. if (message.icon) {
  198. const linkElement = document.createElement("link");
  199. linkElement.rel = "icon";
  200. linkElement.href = message.icon;
  201. document.head.appendChild(linkElement);
  202. }
  203. }
  204. if (message.method == "setContent") {
  205. const pageData = {
  206. content: message.content,
  207. filename: tabData.filename
  208. };
  209. tabData.options.openEditor = false;
  210. singlefile.extension.core.content.download.downloadPage(pageData, tabData.options);
  211. }
  212. if (message.method == "disableFormatPage") {
  213. tabData.options.disableFormatPage = true;
  214. formatPageButton.remove();
  215. }
  216. if (message.method == "onUpdate") {
  217. tabData.docSaved = message.saved;
  218. }
  219. if (message.method == "onInit") {
  220. const defaultEditorMode = tabData.options.defaultEditorMode;
  221. if (defaultEditorMode == "edit") {
  222. enableEditPage();
  223. } else if (defaultEditorMode == "format" && !tabData.options.disableFormatPage) {
  224. formatPage();
  225. } else if (defaultEditorMode == "cut") {
  226. enableCutInnerPage();
  227. } else if (defaultEditorMode == "cut-external") {
  228. enableCutOuterPage();
  229. }
  230. }
  231. if (message.method == "savePage") {
  232. savePage();
  233. }
  234. };
  235. window.onload = () => {
  236. browser.runtime.sendMessage({ method: "editor.getTabData" });
  237. browser.runtime.onMessage.addListener(message => {
  238. if (message.method == "devtools.resourceCommitted") {
  239. updatedResources[message.url] = { content: message.content, type: message.type, encoding: message.encoding };
  240. return Promise.resolve({});
  241. }
  242. if (message.method == "content.save") {
  243. tabData.options = message.options;
  244. savePage();
  245. browser.runtime.sendMessage({ method: "ui.processInit" });
  246. return Promise.resolve({});
  247. }
  248. if (message.method == "common.promptValueRequest") {
  249. browser.runtime.sendMessage({ method: "tabs.promptValueResponse", value: prompt(message.promptMessage) });
  250. return Promise.resolve({});
  251. }
  252. if (message.method == "editor.setTabData") {
  253. if (message.truncated) {
  254. tabDataContents.push(message.content);
  255. } else {
  256. tabDataContents = [message.content];
  257. }
  258. if (!message.truncated || message.finished) {
  259. tabData = JSON.parse(tabDataContents.join(""));
  260. tabData.docSaved = true;
  261. tabDataContents = [];
  262. editorElement.contentWindow.postMessage(JSON.stringify({ method: "init", content: tabData.content }), "*");
  263. delete tabData.content;
  264. }
  265. return Promise.resolve({});
  266. }
  267. });
  268. };
  269. window.onbeforeunload = event => {
  270. if (tabData.options.warnUnsavedPage && !tabData.docSaved) {
  271. event.preventDefault();
  272. event.returnValue = "";
  273. }
  274. };
  275. function disableEditPage() {
  276. editPageButton.classList.add("edit-disabled");
  277. toolbarElement.classList.remove("edit-mode");
  278. editorElement.contentWindow.postMessage(JSON.stringify({ method: "disableEditPage" }), "*");
  279. }
  280. function disableCutInnerPage() {
  281. cutInnerPageButton.classList.add("cut-disabled");
  282. toolbarElement.classList.remove("cut-inner-mode");
  283. editorElement.contentWindow.postMessage(JSON.stringify({ method: "disableCutInnerPage" }), "*");
  284. }
  285. function disableCutOuterPage() {
  286. cutOuterPageButton.classList.add("cut-disabled");
  287. toolbarElement.classList.remove("cut-outer-mode");
  288. editorElement.contentWindow.postMessage(JSON.stringify({ method: "disableCutOuterPage" }), "*");
  289. }
  290. function resetHighlightButtons() {
  291. highlightButtons.forEach(highlightButton => highlightButton.classList.add("highlight-disabled"));
  292. editorElement.contentWindow.postMessage(JSON.stringify({ method: "disableHighlight" }), "*");
  293. }
  294. function disableRemoveHighlights() {
  295. toolbarElement.classList.remove("remove-highlight-mode");
  296. removeHighlightButton.classList.add("remove-highlight-disabled");
  297. editorElement.contentWindow.postMessage(JSON.stringify({ method: "disableRemoveHighlights" }), "*");
  298. }
  299. function displayHighlights() {
  300. toggleHighlightsButton.src = "/extension/ui/resources/button_highlighter_visible.png";
  301. editorElement.contentWindow.postMessage(JSON.stringify({ method: "displayHighlights" }), "*");
  302. }
  303. function enableEditPage() {
  304. editPageButton.classList.remove("edit-disabled");
  305. toolbarElement.classList.add("edit-mode");
  306. editorElement.contentWindow.postMessage(JSON.stringify({ method: "enableEditPage" }), "*");
  307. }
  308. function formatPage() {
  309. formatPageButton.classList.remove("format-disabled");
  310. updatedResources = {};
  311. editorElement.contentWindow.postMessage(JSON.stringify({ method: tabData.options.applySystemTheme ? "formatPage" : "formatPageNoTheme" }), "*");
  312. }
  313. function cancelFormatPage() {
  314. formatPageButton.classList.add("format-disabled");
  315. updatedResources = {};
  316. editorElement.contentWindow.postMessage(JSON.stringify({ method: "cancelFormatPage" }), "*");
  317. }
  318. function enableCutInnerPage() {
  319. cutInnerPageButton.classList.remove("cut-disabled");
  320. toolbarElement.classList.add("cut-inner-mode");
  321. resetHighlightButtons();
  322. disableRemoveHighlights();
  323. editorElement.contentWindow.postMessage(JSON.stringify({ method: "enableCutInnerPage" }), "*");
  324. }
  325. function enableCutOuterPage() {
  326. cutOuterPageButton.classList.remove("cut-disabled");
  327. toolbarElement.classList.add("cut-outer-mode");
  328. resetHighlightButtons();
  329. disableRemoveHighlights();
  330. editorElement.contentWindow.postMessage(JSON.stringify({ method: "enableCutOuterPage" }), "*");
  331. }
  332. function savePage() {
  333. editorElement.contentWindow.postMessage(JSON.stringify({ method: "getContent", compressHTML: tabData.options.compressHTML, updatedResources }), "*");
  334. }
  335. return {};
  336. })();