ui-button.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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));
  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) {
  121. const autoSave = await singlefile.ui.autosave.isEnabled(tab.id);
  122. const properties = await getCurrentProperties(tab.id, { autoSave });
  123. await refresh(tab.id, properties, true);
  124. if (singlefile.core.isAllowedURL(tab.url) && browser.browserAction && browser.browserAction.enable && browser.browserAction.disable) {
  125. if (singlefile.core.isAllowedURL(tab.url)) {
  126. try {
  127. await browser.browserAction.enable(tab.id);
  128. } catch (error) {
  129. /* ignored */
  130. }
  131. } else {
  132. try {
  133. await browser.browserAction.disable(tab.id);
  134. } catch (error) {
  135. /* ignored */
  136. }
  137. }
  138. }
  139. }
  140. async function getCurrentProperties(tabId, options) {
  141. if (options.autoSave) {
  142. return getProperties(tabId, options);
  143. } else {
  144. const tabsData = await singlefile.storage.getTemporary();
  145. const tabData = tabsData[tabId] && tabsData[tabId].button;
  146. if (tabData) {
  147. return getProperties(tabId, options, tabData.setBadgeText, tabData.setBadgeBackgroundColor, tabData.setTitle, tabData.setIcon);
  148. } else {
  149. return getProperties(tabId, options);
  150. }
  151. }
  152. }
  153. function getProperties(tabId, options, text, color, title = DEFAULT_TITLE, path = DEFAULT_ICON_PATH, progress = -1, barProgress = -1, autoColor = [208, 208, 208, 255]) {
  154. return {
  155. text: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveBadge") : (text || ""),
  156. color: options.autoSave ? autoColor : color || DEFAULT_COLOR,
  157. title: options.autoSave ? browser.i18n.getMessage("buttonAutoSaveActiveTooltip") : title,
  158. path: options.autoSave ? DEFAULT_ICON_PATH : path,
  159. progress: options.autoSave ? - 1 : progress,
  160. barProgress: options.autoSave ? - 1 : barProgress
  161. };
  162. }
  163. async function refresh(tabId, tabData, force) {
  164. const tabsData = await singlefile.storage.getTemporary();
  165. if (!tabsData[tabId]) {
  166. tabsData[tabId] = {};
  167. }
  168. if (!tabsData[tabId].pendingRefresh) {
  169. tabsData[tabId].pendingRefresh = Promise.resolve();
  170. }
  171. try {
  172. tabsData[tabId].pendingRefresh = tabsData[tabId].pendingRefresh.then(() => refreshAsync(tabId, tabsData, tabData, force));
  173. await tabsData[tabId].pendingRefresh;
  174. } catch (error) {
  175. /* ignored */
  176. }
  177. }
  178. async function refreshAsync(tabId, tabsData, tabData, force) {
  179. for (let property of BUTTON_PROPERTIES) {
  180. await refreshProperty(tabId, tabsData, property.name, property.browserActionMethod, tabData, force);
  181. }
  182. }
  183. async function refreshProperty(tabId, tabsData, property, browserActionMethod, tabData, force) {
  184. const value = tabData[property];
  185. const browserActionParameter = { tabId };
  186. if (browser.browserAction[browserActionMethod]) {
  187. browserActionParameter[property] = value;
  188. if (!tabsData[tabId]) {
  189. tabsData[tabId] = {};
  190. }
  191. if (!tabsData[tabId].button) {
  192. tabsData[tabId].button = {};
  193. }
  194. if (force || JSON.stringify(tabsData[tabId].button[browserActionMethod]) != JSON.stringify(value)) {
  195. tabsData[tabId].button[browserActionMethod] = value;
  196. await browser.browserAction[browserActionMethod](browserActionParameter);
  197. }
  198. }
  199. }
  200. })();