ui-button.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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 => 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. });
  59. if (browser.runtime.onMessageExternal) {
  60. browser.runtime.onMessageExternal.addListener(async message => {
  61. if (message.method == "enableAutoSave") {
  62. setAutoSaveActiveTabEnabled(message.enabled);
  63. }
  64. if (message.method == "isAutoSaveEnabled") {
  65. return isAutoSaveEnabled();
  66. }
  67. });
  68. }
  69. return {
  70. onInitialize,
  71. onProgress,
  72. onEnd,
  73. onError,
  74. refresh: (tabId, options) => refresh(tabId, getProperties(tabId, options))
  75. };
  76. async function setAutoSaveActiveTabEnabled(enabled) {
  77. const tabs = await browser.tabs.query({ currentWindow: true, active: true });
  78. const tab = tabs[0];
  79. if (tab) {
  80. const tabId = tab.id;
  81. const tabsData = await singlefile.storage.get();
  82. if (!tabsData[tabId]) {
  83. tabsData[tabId] = {};
  84. }
  85. tabsData[tabId].autoSave = enabled;
  86. await singlefile.storage.set(tabsData);
  87. refresh(tabId, getProperties(tabId, { autoSave: enabled }));
  88. }
  89. }
  90. async function isAutoSaveEnabled() {
  91. const tabs = await browser.tabs.query({ currentWindow: true, active: true });
  92. const tab = tabs[0];
  93. if (tab && singlefile.core.isAllowedURL(tab.url)) {
  94. const tabId = tab.id;
  95. const tabsData = await singlefile.storage.get();
  96. return tabsData.autoSaveAll || (tabsData.autoSaveUnpinned && !tab.pinned) || (tabsData[tabId] && tabsData[tabId].autoSave);
  97. }
  98. return false;
  99. }
  100. async function onInitialize(tabId, options, step) {
  101. if (step == 1) {
  102. const tabsData = await singlefile.storage.getTemporary();
  103. if (tabsData[tabId]) {
  104. tabsData[tabId].button = null;
  105. }
  106. }
  107. 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"));
  108. }
  109. function onError(tabId, options) {
  110. refresh(tabId, getProperties(tabId, options, browser.i18n.getMessage("buttonErrorBadge"), [229, 4, 12, 255]));
  111. }
  112. async function onEnd(tabId, options) {
  113. refresh(tabId, getProperties(tabId, options, browser.i18n.getMessage("buttonOKBadge"), [4, 229, 36, 255]));
  114. }
  115. function onProgress(tabId, index, maxIndex, options) {
  116. const progress = Math.max(Math.min(20, Math.floor((index / maxIndex) * 20)), 0);
  117. const barProgress = Math.min(Math.floor((index / maxIndex) * 8), 8);
  118. 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]));
  119. }
  120. async function onTabActivated(tab, reset) {
  121. const autoSave = await singlefile.ui.autosave.isEnabled(tab.id);
  122. if (reset) {
  123. const tabsData = await singlefile.storage.getTemporary();
  124. if (tabsData[tab.id]) {
  125. tabsData[tab.id].button = null;
  126. }
  127. }
  128. const properties = await getCurrentProperties(tab.id, { autoSave });
  129. await refresh(tab.id, properties, true);
  130. if (singlefile.core.isAllowedURL(tab.url) && browser.browserAction && browser.browserAction.enable && browser.browserAction.disable) {
  131. if (singlefile.core.isAllowedURL(tab.url)) {
  132. try {
  133. await browser.browserAction.enable(tab.id);
  134. } catch (error) {
  135. /* ignored */
  136. }
  137. } else {
  138. try {
  139. await browser.browserAction.disable(tab.id);
  140. } catch (error) {
  141. /* ignored */
  142. }
  143. }
  144. }
  145. }
  146. async function getCurrentProperties(tabId, options) {
  147. if (options.autoSave) {
  148. return getProperties(tabId, options);
  149. } else {
  150. const tabsData = await singlefile.storage.getTemporary();
  151. const tabData = tabsData[tabId] && tabsData[tabId].button;
  152. if (tabData) {
  153. return getProperties(tabId, options, tabData.setBadgeText, tabData.setBadgeBackgroundColor, tabData.setTitle, tabData.setIcon);
  154. } else {
  155. return getProperties(tabId, options);
  156. }
  157. }
  158. }
  159. function getProperties(tabId, options, text, color, title = DEFAULT_TITLE, path = DEFAULT_ICON_PATH, progress = -1, barProgress = -1, autoColor = [208, 208, 208, 255]) {
  160. return {
  161. text: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveBadge") : (text || ""),
  162. color: options.autoSave ? autoColor : color || DEFAULT_COLOR,
  163. title: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveTooltip") : title,
  164. path: options.autoSave ? DEFAULT_ICON_PATH : path,
  165. progress: options.autoSave ? - 1 : progress,
  166. barProgress: options.autoSave ? - 1 : barProgress
  167. };
  168. }
  169. async function refresh(tabId, tabData, force) {
  170. const tabsData = await singlefile.storage.getTemporary();
  171. if (!tabsData[tabId]) {
  172. tabsData[tabId] = {};
  173. }
  174. if (!tabsData[tabId].pendingRefresh) {
  175. tabsData[tabId].pendingRefresh = Promise.resolve();
  176. }
  177. try {
  178. tabsData[tabId].pendingRefresh = tabsData[tabId].pendingRefresh.then(() => refreshAsync(tabId, tabsData, tabData, force));
  179. await tabsData[tabId].pendingRefresh;
  180. } catch (error) {
  181. /* ignored */
  182. }
  183. }
  184. async function refreshAsync(tabId, tabsData, tabData, force) {
  185. for (const property of BUTTON_PROPERTIES) {
  186. await refreshProperty(tabId, tabsData, property.name, property.browserActionMethod, tabData, force);
  187. }
  188. }
  189. async function refreshProperty(tabId, tabsData, property, browserActionMethod, tabData, force) {
  190. const value = tabData[property];
  191. const browserActionParameter = { tabId };
  192. if (browser.browserAction[browserActionMethod]) {
  193. browserActionParameter[property] = value;
  194. if (!tabsData[tabId]) {
  195. tabsData[tabId] = {};
  196. }
  197. if (!tabsData[tabId].button) {
  198. tabsData[tabId].button = {};
  199. }
  200. if (force || JSON.stringify(tabsData[tabId].button[browserActionMethod]) != JSON.stringify(value)) {
  201. tabsData[tabId].button[browserActionMethod] = value;
  202. await browser.browserAction[browserActionMethod](browserActionParameter);
  203. }
  204. }
  205. }
  206. })();