ui-button.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Copyright 2018 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * SingleFile is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * SingleFile 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
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with SingleFile. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /* global browser, singlefile */
  21. singlefile.ui.button = (() => {
  22. const DEFAULT_ICON_PATH = "/extension/ui/resources/icon_16.png";
  23. const WAIT_ICON_PATH_PREFIX = "/extension/ui/resources/icon_16_wait";
  24. const DEFAULT_TITLE = browser.i18n.getMessage("buttonDefaultTooltip");
  25. const DEFAULT_COLOR = [2, 147, 20, 255];
  26. const BUTTON_PROPERTIES = [{ name: "color", browserActionMethod: "setBadgeBackgroundColor" }, { name: "path", browserActionMethod: "setIcon" }, { name: "text", browserActionMethod: "setBadgeText" }, { name: "title", browserActionMethod: "setTitle" }];
  27. browser.browserAction.onClicked.addListener(async tab => {
  28. if (singlefile.core.isAllowedURL(tab.url)) {
  29. const tabs = await browser.tabs.query({ currentWindow: true, highlighted: true });
  30. if (!tabs.length) {
  31. singlefile.ui.saveTab(tab);
  32. } else {
  33. tabs.forEach(tab => (tab.active || tab.highlighted) && singlefile.core.isAllowedURL(tab.url) && singlefile.ui.saveTab(tab));
  34. }
  35. }
  36. });
  37. browser.tabs.onActivated.addListener(async activeInfo => {
  38. const tab = await browser.tabs.get(activeInfo.tabId);
  39. await onTabActivated(tab);
  40. });
  41. browser.tabs.onCreated.addListener(onTabActivated);
  42. browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => onTabActivated(tab, changeInfo.status == "loading"));
  43. browser.runtime.onMessage.addListener((request, sender) => {
  44. if (request.processProgress) {
  45. if (request.maxIndex) {
  46. onProgress(sender.tab.id, request.index, request.maxIndex, request.options);
  47. }
  48. }
  49. if (request.processEnd) {
  50. onEnd(sender.tab.id, request.options);
  51. }
  52. if (request.processError) {
  53. if (request.error) {
  54. console.error("Initialization error", request.error); // eslint-disable-line no-console
  55. }
  56. onError(sender.tab.id, request.options);
  57. }
  58. if (request.processCancelled) {
  59. onCancelled(sender.tab.id, request.options);
  60. }
  61. });
  62. if (browser.runtime.onMessageExternal) {
  63. browser.runtime.onMessageExternal.addListener(async message => {
  64. if (message.method == "enableAutoSave") {
  65. setAutoSaveActiveTabEnabled(message.enabled);
  66. }
  67. if (message.method == "isAutoSaveEnabled") {
  68. return isAutoSaveEnabled();
  69. }
  70. });
  71. }
  72. return {
  73. onInitialize,
  74. onProgress,
  75. onEnd,
  76. onError,
  77. refresh: (tabId, options) => refresh(tabId, getProperties(tabId, options))
  78. };
  79. async function setAutoSaveActiveTabEnabled(enabled) {
  80. const tabs = await browser.tabs.query({ currentWindow: true, active: true });
  81. const tab = tabs[0];
  82. if (tab) {
  83. const tabId = tab.id;
  84. const tabsData = await singlefile.storage.get();
  85. if (!tabsData[tabId]) {
  86. tabsData[tabId] = {};
  87. }
  88. tabsData[tabId].autoSave = enabled;
  89. await singlefile.storage.set(tabsData);
  90. refresh(tabId, getProperties(tabId, { autoSave: enabled }));
  91. }
  92. }
  93. async function isAutoSaveEnabled() {
  94. const tabs = await browser.tabs.query({ currentWindow: true, active: true });
  95. const tab = tabs[0];
  96. if (tab && singlefile.core.isAllowedURL(tab.url)) {
  97. const tabId = tab.id;
  98. const tabsData = await singlefile.storage.get();
  99. return tabsData.autoSaveAll || (tabsData.autoSaveUnpinned && !tab.pinned) || (tabsData[tabId] && tabsData[tabId].autoSave);
  100. }
  101. return false;
  102. }
  103. async function onInitialize(tabId, options, step) {
  104. if (step == 1) {
  105. const tabsData = await singlefile.storage.getTemporary();
  106. if (tabsData[tabId]) {
  107. tabsData[tabId].button = null;
  108. }
  109. }
  110. refresh(tabId, getProperties(tabId, options, browser.i18n.getMessage("buttonInitializingBadge"), step == 1 ? DEFAULT_COLOR : [4, 229, 36, 255], browser.i18n.getMessage("buttonInitializingTooltip") + " (" + step + "/2)", WAIT_ICON_PATH_PREFIX + "0.png"));
  111. }
  112. function onError(tabId, options) {
  113. refresh(tabId, getProperties(tabId, options, browser.i18n.getMessage("buttonErrorBadge"), [229, 4, 12, 255]));
  114. }
  115. function onCancelled(tabId, options) {
  116. refresh(tabId, getProperties(tabId, options, "", DEFAULT_COLOR, DEFAULT_TITLE));
  117. }
  118. async function onEnd(tabId, options) {
  119. refresh(tabId, getProperties(tabId, options, browser.i18n.getMessage("buttonOKBadge"), [4, 229, 36, 255]));
  120. }
  121. function onProgress(tabId, index, maxIndex, options) {
  122. const progress = Math.max(Math.min(20, Math.floor((index / maxIndex) * 20)), 0);
  123. const barProgress = Math.min(Math.floor((index / maxIndex) * 8), 8);
  124. refresh(tabId, getProperties(tabId, options, "", [4, 229, 36, 255], browser.i18n.getMessage("buttonSaveProgressTooltip") + (progress * 5) + "%", WAIT_ICON_PATH_PREFIX + barProgress + ".png", progress, barProgress, [128, 128, 128, 255]));
  125. }
  126. async function onTabActivated(tab, reset) {
  127. const autoSave = await singlefile.ui.autosave.isEnabled(tab.id);
  128. if (reset) {
  129. const tabsData = await singlefile.storage.getTemporary();
  130. if (tabsData[tab.id]) {
  131. tabsData[tab.id].button = null;
  132. }
  133. }
  134. const properties = await getCurrentProperties(tab.id, { autoSave });
  135. await refresh(tab.id, properties, true);
  136. if (singlefile.core.isAllowedURL(tab.url) && browser.browserAction && browser.browserAction.enable && browser.browserAction.disable) {
  137. if (singlefile.core.isAllowedURL(tab.url)) {
  138. try {
  139. await browser.browserAction.enable(tab.id);
  140. } catch (error) {
  141. /* ignored */
  142. }
  143. } else {
  144. try {
  145. await browser.browserAction.disable(tab.id);
  146. } catch (error) {
  147. /* ignored */
  148. }
  149. }
  150. }
  151. }
  152. async function getCurrentProperties(tabId, options) {
  153. if (options.autoSave) {
  154. return getProperties(tabId, options);
  155. } else {
  156. const tabsData = await singlefile.storage.getTemporary();
  157. const tabData = tabsData[tabId] && tabsData[tabId].button;
  158. if (tabData) {
  159. return getProperties(tabId, options, tabData.setBadgeText, tabData.setBadgeBackgroundColor, tabData.setTitle, tabData.setIcon);
  160. } else {
  161. return getProperties(tabId, options);
  162. }
  163. }
  164. }
  165. function getProperties(tabId, options, text, color, title = DEFAULT_TITLE, path = DEFAULT_ICON_PATH, progress = -1, barProgress = -1, autoColor = [208, 208, 208, 255]) {
  166. return {
  167. text: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveBadge") : (text || ""),
  168. color: options.autoSave ? autoColor : color || DEFAULT_COLOR,
  169. title: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveTooltip") : title,
  170. path: options.autoSave ? DEFAULT_ICON_PATH : path,
  171. progress: options.autoSave ? - 1 : progress,
  172. barProgress: options.autoSave ? - 1 : barProgress
  173. };
  174. }
  175. async function refresh(tabId, tabData, force) {
  176. const tabsData = await singlefile.storage.getTemporary();
  177. if (!tabsData[tabId]) {
  178. tabsData[tabId] = {};
  179. }
  180. if (!tabsData[tabId].pendingRefresh) {
  181. tabsData[tabId].pendingRefresh = Promise.resolve();
  182. }
  183. try {
  184. tabsData[tabId].pendingRefresh = tabsData[tabId].pendingRefresh.then(() => refreshAsync(tabId, tabsData, tabData, force));
  185. await tabsData[tabId].pendingRefresh;
  186. } catch (error) {
  187. /* ignored */
  188. }
  189. }
  190. async function refreshAsync(tabId, tabsData, tabData, force) {
  191. for (const property of BUTTON_PROPERTIES) {
  192. await refreshProperty(tabId, tabsData, property.name, property.browserActionMethod, tabData, force);
  193. }
  194. }
  195. async function refreshProperty(tabId, tabsData, property, browserActionMethod, tabData, force) {
  196. const value = tabData[property];
  197. const browserActionParameter = { tabId };
  198. if (browser.browserAction[browserActionMethod]) {
  199. browserActionParameter[property] = value;
  200. if (!tabsData[tabId]) {
  201. tabsData[tabId] = {};
  202. }
  203. if (!tabsData[tabId].button) {
  204. tabsData[tabId].button = {};
  205. }
  206. if (force || JSON.stringify(tabsData[tabId].button[browserActionMethod]) != JSON.stringify(value)) {
  207. tabsData[tabId].button[browserActionMethod] = value;
  208. await browser.browserAction[browserActionMethod](browserActionParameter);
  209. }
  210. }
  211. }
  212. })();