helper.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import browser from 'webextension-polyfill';
  2. export async function getActiveTab() {
  3. try {
  4. const tabsQuery = {
  5. active: true,
  6. url: '*://*/*',
  7. };
  8. const window = await browser.windows.getLastFocused({
  9. populate: true,
  10. windowTypes: ['normal'],
  11. });
  12. const windowId = window.id;
  13. if (windowId) tabsQuery.windowId = windowId;
  14. else tabsQuery.lastFocusedWindow = true;
  15. const [tab] = await browser.tabs.query(tabsQuery);
  16. return tab;
  17. } catch (error) {
  18. console.error(error);
  19. return null;
  20. }
  21. }
  22. export function isXPath(str) {
  23. const regex = /^([(/@]|id\()/;
  24. return regex.test(str);
  25. }
  26. export function visibleInViewport(element) {
  27. const { top, left, bottom, right, height, width } =
  28. element.getBoundingClientRect();
  29. if (height === 0 || width === 0) return false;
  30. return (
  31. top >= 0 &&
  32. left >= 0 &&
  33. bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
  34. right <= (window.innerWidth || document.documentElement.clientWidth)
  35. );
  36. }
  37. export function sleep(timeout = 500) {
  38. return new Promise((resolve) => {
  39. setTimeout(() => {
  40. resolve();
  41. }, timeout);
  42. });
  43. }
  44. export function findTriggerBlock(drawflow = {}) {
  45. if (!drawflow) return null;
  46. if (drawflow.drawflow) {
  47. const blocks = Object.values(drawflow.drawflow?.Home?.data ?? {});
  48. if (!blocks) return null;
  49. return blocks.find(({ name }) => name === 'trigger');
  50. }
  51. if (drawflow.nodes) {
  52. return drawflow.nodes.find((node) => node.label === 'trigger');
  53. }
  54. return null;
  55. }
  56. export function throttle(callback, limit) {
  57. let waiting = false;
  58. return (...args) => {
  59. if (!waiting) {
  60. callback.apply(this, args);
  61. waiting = true;
  62. setTimeout(() => {
  63. waiting = false;
  64. }, limit);
  65. }
  66. };
  67. }
  68. export function convertArrObjTo2DArr(arr) {
  69. const keyIndex = new Map();
  70. const values = [[]];
  71. arr.forEach((obj) => {
  72. const keys = Object.keys(obj);
  73. const row = [];
  74. keys.forEach((key) => {
  75. if (!keyIndex.has(key)) {
  76. keyIndex.set(key, keyIndex.size);
  77. values[0].push(key);
  78. }
  79. const value = obj[key];
  80. const rowIndex = keyIndex.get(key);
  81. row[rowIndex] = typeof value === 'object' ? JSON.stringify(value) : value;
  82. });
  83. values.push([...row]);
  84. });
  85. return values;
  86. }
  87. export function convert2DArrayToArrayObj(values) {
  88. let keyIndex = 0;
  89. const keys = values.shift();
  90. const result = [];
  91. for (let columnIndex = 0; columnIndex < values.length; columnIndex += 1) {
  92. const currentColumn = {};
  93. for (
  94. let rowIndex = 0;
  95. rowIndex < values[columnIndex].length;
  96. rowIndex += 1
  97. ) {
  98. let key = keys[rowIndex];
  99. if (!key) {
  100. keyIndex += 1;
  101. key = `_row${keyIndex}`;
  102. keys.push(key);
  103. }
  104. currentColumn[key] = values[columnIndex][rowIndex];
  105. }
  106. result.push(currentColumn);
  107. }
  108. return result;
  109. }
  110. export function parseJSON(data, def) {
  111. try {
  112. const result = JSON.parse(data);
  113. return result;
  114. } catch (error) {
  115. return def;
  116. }
  117. }
  118. export function parseFlow(flow) {
  119. const obj = typeof flow === 'string' ? parseJSON(flow, {}) : flow;
  120. return obj;
  121. }
  122. export function replaceMustache(str, replacer) {
  123. /* eslint-disable-next-line */
  124. return str.replace(/\{\{(.*?)\}\}/g, replacer);
  125. }
  126. export function openFilePicker(acceptedFileTypes = [], attrs = {}) {
  127. return new Promise((resolve) => {
  128. const input = document.createElement('input');
  129. input.type = 'file';
  130. input.accept = Array.isArray(acceptedFileTypes)
  131. ? acceptedFileTypes.join(',')
  132. : acceptedFileTypes;
  133. Object.entries(attrs).forEach(([key, value]) => {
  134. input[key] = value;
  135. });
  136. input.onchange = (event) => {
  137. const { files } = event.target;
  138. const validFiles = [];
  139. Array.from(files).forEach((file) => {
  140. if (!acceptedFileTypes.includes(file.type)) return;
  141. validFiles.push(file);
  142. });
  143. resolve(validFiles);
  144. };
  145. input.click();
  146. });
  147. }
  148. export function fileSaver(filename, data) {
  149. const anchor = document.createElement('a');
  150. anchor.download = filename;
  151. anchor.href = data;
  152. anchor.dispatchEvent(new MouseEvent('click'));
  153. anchor.remove();
  154. }
  155. export function countDuration(started, ended) {
  156. const duration = Math.round((ended - started) / 1000);
  157. const minutes = Math.floor(duration / 60);
  158. const seconds = Math.floor(duration % 60);
  159. const getText = (num, suffix) => (num > 0 ? `${num}${suffix}` : '');
  160. return `${getText(minutes, 'm')} ${seconds}s`;
  161. }
  162. export function toCamelCase(str, capitalize = false) {
  163. const result = str.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
  164. return index === 0 && !capitalize
  165. ? letter.toLowerCase()
  166. : letter.toUpperCase();
  167. });
  168. return result.replace(/\s+|[-]/g, '');
  169. }
  170. export function isObject(obj) {
  171. return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
  172. }
  173. export function objectHasKey(obj, key) {
  174. return Object.prototype.hasOwnProperty.call(obj, key);
  175. }
  176. export function isWhitespace(str) {
  177. return !/\S/.test(str);
  178. }
  179. export function debounce(callback, time = 200) {
  180. let interval;
  181. return (...args) => {
  182. clearTimeout(interval);
  183. return new Promise((resolve) => {
  184. interval = setTimeout(() => {
  185. interval = null;
  186. callback(...args);
  187. resolve();
  188. }, time);
  189. });
  190. };
  191. }
  192. export async function clearCache(workflow) {
  193. try {
  194. await browser.storage.local.remove(`state:${workflow.id}`);
  195. const flows = parseJSON(workflow.drawflow, null);
  196. const blocks = flows && flows.drawflow.Home.data;
  197. if (blocks) {
  198. Object.values(blocks).forEach(({ name, id }) => {
  199. if (name !== 'loop-data') return;
  200. localStorage.removeItem(`index:${id}`);
  201. });
  202. }
  203. return true;
  204. } catch (error) {
  205. console.error(error);
  206. return false;
  207. }
  208. }
  209. export function arraySorter({ data, key, order = 'asc' }) {
  210. let runCounts = {};
  211. const copyData = data.slice();
  212. if (key === 'mostUsed') {
  213. runCounts = parseJSON(localStorage.getItem('runCounts'), {}) || {};
  214. }
  215. return copyData.sort((a, b) => {
  216. let comparison = 0;
  217. let itemA = a[key] || a;
  218. let itemB = b[key] || b;
  219. if (key === 'mostUsed') {
  220. itemA = runCounts[a.id] || 0;
  221. itemB = runCounts[b.id] || 0;
  222. }
  223. if (itemA > itemB) {
  224. comparison = 1;
  225. } else if (itemA < itemB) {
  226. comparison = -1;
  227. }
  228. return order === 'desc' ? comparison * -1 : comparison;
  229. });
  230. }