From 630a9716b830c46180194962fc825369a4867732 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:27:45 +0700 Subject: [PATCH 01/46] New types --- lib/types/ICommand.ts | 17 +++++++++++++ lib/types/ISavedSession.ts | 13 ++++++++++ lib/types/ISession.ts | 5 ++++ lib/types/ISessionState.ts | 4 ++++ lib/types/ITab.ts | 26 ++++++++++++++++++++ lib/types/ITabManager.ts | 4 ++++ lib/types/ITabManagerState.ts | 45 +++++++++++++++++++++++++++++++++++ lib/types/ITabState.ts | 9 +++++++ lib/types/IWindow.ts | 32 +++++++++++++++++++++++++ lib/types/IWindowState.ts | 10 ++++++++ lib/types/index.ts | 10 ++++++++ 11 files changed, 175 insertions(+) create mode 100644 lib/types/ICommand.ts create mode 100644 lib/types/ISavedSession.ts create mode 100644 lib/types/ISession.ts create mode 100644 lib/types/ISessionState.ts create mode 100644 lib/types/ITab.ts create mode 100644 lib/types/ITabManager.ts create mode 100644 lib/types/ITabManagerState.ts create mode 100644 lib/types/ITabState.ts create mode 100644 lib/types/IWindow.ts create mode 100644 lib/types/IWindowState.ts create mode 100644 lib/types/index.ts diff --git a/lib/types/ICommand.ts b/lib/types/ICommand.ts new file mode 100644 index 00000000..f1300cd3 --- /dev/null +++ b/lib/types/ICommand.ts @@ -0,0 +1,17 @@ +import {ISavedSession} from "@views/Session"; +import * as browser from "webextension-polyfill"; + +export interface ICommand +{ + command: string, + window_ids?: number[], + window_id?: number, + tab_id?: number, + color?: string, + name?: string, + session?: ISavedSession, + tab?: browser.Tabs.Tab, + saved_tab?: browser.Tabs.OnActivatedActiveInfoType, + tabs?: browser.Tabs.Tab[], + incognito?: boolean +} \ No newline at end of file diff --git a/lib/types/ISavedSession.ts b/lib/types/ISavedSession.ts new file mode 100644 index 00000000..2ad562dc --- /dev/null +++ b/lib/types/ISavedSession.ts @@ -0,0 +1,13 @@ +import * as browser from "webextension-polyfill"; + +export interface ISavedSession { + tabs: browser.Tabs.Tab[], + windowsInfo: browser.Windows.Window, + name: string, + color: string, + date: number, + sessionStartTime: number, + id: string, + customName: boolean, + incognito: boolean +} \ No newline at end of file diff --git a/lib/types/ISession.ts b/lib/types/ISession.ts new file mode 100644 index 00000000..def9a646 --- /dev/null +++ b/lib/types/ISession.ts @@ -0,0 +1,5 @@ +import {IWindow, ISavedSession} from "@types"; + +export interface ISession extends IWindow { + session: ISavedSession +} \ No newline at end of file diff --git a/lib/types/ISessionState.ts b/lib/types/ISessionState.ts new file mode 100644 index 00000000..7c16a4fd --- /dev/null +++ b/lib/types/ISessionState.ts @@ -0,0 +1,4 @@ +export interface ISessionState { + name: string, + color: string, +} \ No newline at end of file diff --git a/lib/types/ITab.ts b/lib/types/ITab.ts new file mode 100644 index 00000000..940bd0ff --- /dev/null +++ b/lib/types/ITab.ts @@ -0,0 +1,26 @@ +import * as browser from "webextension-polyfill"; +import {DragEvent, MouseEvent} from "react"; + +export interface ITab { + tab: browser.Tabs.Tab, + window: browser.Windows.Window, + selected: boolean, + hidden: boolean, + id: string, + + searchActive: boolean, + layout: string, + draggable: boolean, + + middleClick: (tabId: number) => void, + + hoverHandler: (tab: browser.Tabs.Tab) => void, + parentUpdate?: () => void, + select: (id: number) => void, + selectTo?: (id: number) => void, + drag?: (e: DragEvent, id: number) => void, + drop?: (id: number, before: boolean) => void, + dropWindow?: (windowId: number) => void, + dragFavicon?: (icon?: string) => string + click?: (e: MouseEvent, index: number) => void +} \ No newline at end of file diff --git a/lib/types/ITabManager.ts b/lib/types/ITabManager.ts new file mode 100644 index 00000000..9d736bf4 --- /dev/null +++ b/lib/types/ITabManager.ts @@ -0,0 +1,4 @@ +export interface ITabManager +{ + optionsActive: boolean +} \ No newline at end of file diff --git a/lib/types/ITabManagerState.ts b/lib/types/ITabManagerState.ts new file mode 100644 index 00000000..0dcc364b --- /dev/null +++ b/lib/types/ITabManagerState.ts @@ -0,0 +1,45 @@ +import * as browser from "webextension-polyfill"; +import {ISavedSession} from "@views/Session"; + +export interface ITabManagerState { + tabCount: number, + hiddenCount: number, + + animations: boolean, + badge: boolean, + compact: boolean, + dark: boolean, + filterTabs: boolean, + hideWindows: boolean, + lastOpenWindow: number, + layout: string, + openInOwnTab: boolean, + sessionsFeature: boolean, + tabHeight: number, + tabLimit: number, + tabWidth: number, + tabactions: boolean, + windowTitles: boolean, + + windows: browser.Windows.Window[], + sessions: ISavedSession[], + selection: Set, + hiddenTabs: Set, + tabsbyid: Map, + windowsbyid: Map, + + lastSelect: number, + searchLen: number, + height: number, + hasScrollBar: boolean, + focusUpdates: number, + topText: string, + bottomText: string, + lastDirection: string, + optionsActive: boolean, + dupTabs: boolean, + dragFavicon: string, + colorsActive: number, + + resetTimeout: number +} \ No newline at end of file diff --git a/lib/types/ITabState.ts b/lib/types/ITabState.ts new file mode 100644 index 00000000..29c209b5 --- /dev/null +++ b/lib/types/ITabState.ts @@ -0,0 +1,9 @@ +import {RefObject} from "react"; + +export interface ITabState { + draggingOver: string, + dragFavIcon: string, + favIcon: string, + hovered: boolean, + tabRef: RefObject +} \ No newline at end of file diff --git a/lib/types/IWindow.ts b/lib/types/IWindow.ts new file mode 100644 index 00000000..fa969757 --- /dev/null +++ b/lib/types/IWindow.ts @@ -0,0 +1,32 @@ +import * as browser from "webextension-polyfill"; +import {MouseEvent} from "react"; +import * as React from "react"; + +export interface IWindow { + window?: browser.Windows.Window, + windowTitles: boolean, + tabs: browser.Tabs.Tab[], + searchActive: boolean, + layout: string, + tabactions: boolean, + sessionsFeature?: boolean, + hoverIcon: (e: MouseEvent | string) => void, + hiddenTabs: Set, + selection: Set, + filterTabs: boolean, + lastOpenWindow: number, + incognito: boolean, + draggable: boolean, + + hoverHandler: (tab: browser.Tabs.Tab) => void, + scrollTo: (what: string, id: number) => void, + parentUpdate: () => void, + toggleColors: (active: boolean, windowId: number) => void, + tabMiddleClick: (tabId: number) => void, + select: (id: number) => void, + selectTo?: (id: number, tabs: browser.Tabs.Tab[]) => void, + drag?: (e: React.DragEvent, id: number) => void, + drop?: (id: number, before: boolean) => void, + dropWindow?: (windowId: number) => void, + dragFavicon?: (icon: string) => string +} \ No newline at end of file diff --git a/lib/types/IWindowState.ts b/lib/types/IWindowState.ts new file mode 100644 index 00000000..4716e8bc --- /dev/null +++ b/lib/types/IWindowState.ts @@ -0,0 +1,10 @@ +export interface IWindowState { + windowTitles: string[]; + name: string, + auto_name: string, + color: string, + tabs: number, + colorActive: boolean, + hover: boolean, + dirty: boolean +} \ No newline at end of file diff --git a/lib/types/index.ts b/lib/types/index.ts new file mode 100644 index 00000000..3a195ebd --- /dev/null +++ b/lib/types/index.ts @@ -0,0 +1,10 @@ +export * from './ICommand'; +export * from './ITabManager'; +export * from './ITabManagerState'; +export * from './ITab'; +export * from './ITabState'; +export * from './IWindow'; +export * from './IWindowState'; +export * from './ISession'; +export * from './ISessionState'; +export * from './ISavedSession'; \ No newline at end of file From 7ea2cae06ff04187a38af321da698f3817367b5d Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:27:54 +0700 Subject: [PATCH 02/46] String library --- lib/strings/strings.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/strings/strings.ts diff --git a/lib/strings/strings.ts b/lib/strings/strings.ts new file mode 100644 index 00000000..9779c90c --- /dev/null +++ b/lib/strings/strings.ts @@ -0,0 +1,40 @@ +"use strict"; + +// commands +export const reload_popup_controls = "reload_popup_controls"; +export const update_tab_count = "update_tab_count"; +export const discard_tabs = "discard_tabs"; +export const move_tabs_to_window = "move_tabs_to_window"; +export const focus_on_tab_and_window = "focus_on_tab_and_window"; +export const focus_on_tab_and_window_delayed = "focus_on_tab_and_window_delayed"; +export const focus_on_window = "focus_on_window"; +export const focus_on_window_delayed = "focus_on_window_delayed"; +export const set_window_color = "set_window_color"; +export const set_window_name = "set_window_name"; +export const create_window_with_tabs = "create_window_with_tabs"; +export const create_window_with_session_tabs = "create_window_with_session_tabs"; +export const close_tabs = "close_tabs"; +export const switch_to_previous_active_tab = "switch_to_previous_active_tab"; +export const refresh_windows = "refresh_windows"; + +// context menues +export const open_in_own_tab = "open_in_own_tab"; +export const open_popup = "open_popup"; +export const open_sidebar = "open_sidebar"; +export const sep1 = "sep1"; +export const support_menu = "support_menu"; +export const review = "review"; +export const donate = "donate"; +export const patron = "patron"; +export const twitter = "twitter"; +export const code_menu = "code_menu"; +export const changelog = "changelog"; +export const options = "options"; +export const source = "source"; +export const report = "report"; +export const send = "send"; + +// storage keys +export const windowHashes = "windowHashes"; +export const windowColors = "windowColors"; +export const windowNames = "windowNames"; \ No newline at end of file From 29301c757511341342f5b0d2f390ed77d7ea5712 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:29:52 +0700 Subject: [PATCH 03/46] Move migration script to typescript --- lib/helpers/{migrate.js => migrate.ts} | 42 ++++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) rename lib/helpers/{migrate.js => migrate.ts} (65%) diff --git a/lib/helpers/migrate.js b/lib/helpers/migrate.ts similarity index 65% rename from lib/helpers/migrate.js rename to lib/helpers/migrate.ts index 98d00d95..71cc3e60 100644 --- a/lib/helpers/migrate.js +++ b/lib/helpers/migrate.ts @@ -1,23 +1,24 @@ "use strict"; import { toBoolean } from "./utils.js"; +import * as S from "@strings"; +import * as browser from 'webextension-polyfill'; +import {ISavedSession} from "@types"; -var browser = browser || chrome; - -var stringkeys = [ +const stringkeys = [ "layout", "version" ]; -var jsonkeys = [ +const jsonkeys = [ "tabLimit", "tabWidth", "tabHeight", "windowAge", - "windowNames", - "windowColors" + S.windowNames, + S.windowColors ]; -var boolkeys = [ +const boolkeys = [ "openInOwnTab", "animations", "windowTitles", @@ -32,7 +33,7 @@ var boolkeys = [ (async function () { - var needsMigration = false; + let needsMigration = false; for (const key of stringkeys) { if (!!localStorage[key]) { needsMigration = true; break; } @@ -47,19 +48,21 @@ var boolkeys = [ } if (needsMigration) { - var values = await browser.storage.local.get(null); - values = values || {}; - // delete all values that don't have a tabs array - for (const key in values) { - if (!!values[key].tabs) { - console.log("session deleting " + key); - await browser.storage.local.remove(key); - } else { - delete values[key]; + let keyValue = {}; + let values : Record = await browser.storage.local.get(null); + if (!!values) { + // delete all values that don't have a tabs array + for (const key in values) { + if (!!(values[key] as ISavedSession).tabs) { + console.log("session deleting " + key); + await browser.storage.local.remove(key); + } else { + delete values[key]; + } } + keyValue["sessions"] = values; } - var keyValue = {}; - keyValue["sessions"] = values; + for (const key of stringkeys) { if (!!localStorage[key]) keyValue[key] = localStorage[key]; } @@ -77,6 +80,5 @@ var boolkeys = [ for (const key of stringkeys) localStorage.removeItem(key); for (const key of boolkeys) localStorage.removeItem(key); for (const key of jsonkeys) localStorage.removeItem(key); - } })(); \ No newline at end of file From 17a92cd3935a047642a4394c8339985af70f60d6 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:32:16 +0700 Subject: [PATCH 04/46] Move from babel to esbuild --- build.mjs | 28 ++++++++++++++++++++++++++++ package.json | 35 +++++++++++++++++++---------------- watch.mjs | 29 +++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 build.mjs create mode 100644 watch.mjs diff --git a/build.mjs b/build.mjs new file mode 100644 index 00000000..c2350939 --- /dev/null +++ b/build.mjs @@ -0,0 +1,28 @@ +import * as esbuild from 'esbuild' + +await esbuild.build({ + entryPoints: ['lib/popup/popup.tsx'], + bundle: true, + sourcemap: true, + target: 'chrome88', + outfile: 'outlib/popup/popup.js', + define: { __VERSION__: '"' + process.env.npm_package_version + '"' } +}) + +await esbuild.build({ + entryPoints: ['lib/service_worker/service_worker.ts'], + bundle: true, + sourcemap: true, + target: 'chrome88', + outfile: 'outlib/service_worker/service_worker.js', + define: {__VERSION__: '"' + process.env.npm_package_version + '"'} +}) + +await esbuild.build({ + entryPoints: ['lib/popup/options.js'], + bundle: true, + sourcemap: true, + target: 'chrome88', + outfile: 'outlib/popup/options.js', + define: {__VERSION__: '"' + process.env.npm_package_version + '"'} +}) diff --git a/package.json b/package.json index a7a1acff..9b7f9a7d 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "watch": "npx babel --watch lib --out-dir outlib --source-maps true", - "build": "babel lib --out-dir outlib --source-maps true" + "watch": "node watch.mjs", + "build": "node build.mjs" }, "repository": { "type": "git", @@ -22,19 +22,22 @@ }, "homepage": "https://github.com/stefanXO/Tab-Manager#readme", "devDependencies": { - "babel-cli": "^6.26.0", - "babel-core": "^6.26.3", - "babel-plugin-inline-replace-variables": "^1.3.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-react-createelement-to-jsx": "^1.1.0", - "babel-preset-browser": "^1.0.1", - "babel-preset-es2015": "^6.24.1", - "babel-preset-es2017": "^6.24.1", - "babel-preset-react": "^6.24.1", - "babel-preset-react-app": "^3.1.2", - "crx3": "^1.1.3" + "@types/chrome": "^0.0.272", + "@types/node": "^22.7.2", + "@types/react": "^18.3.9", + "@types/react-dom": "^18.3.0", + "@types/webextension-polyfill": "^0.12.1", + "crx3": "^1.1.3", + "esbuild": "0.24.0", + "webextension-polyfill": "^0.12.0" }, "dependencies": { - "babel-preset-es2016": "^6.24.1" - } -} \ No newline at end of file + "@types/firefox-webext-browser": "^120.0.4", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-jsx": "^1.0.0" + }, + "browserslist": [ + "Chrome >= 88" + ] +} diff --git a/watch.mjs b/watch.mjs new file mode 100644 index 00000000..cd14bdc3 --- /dev/null +++ b/watch.mjs @@ -0,0 +1,29 @@ +import * as esbuild from 'esbuild'; + +async function watch() { + const ctx = await esbuild.context({ + entryPoints: [ + 'lib/service_worker/service_worker.ts', // Main service worker entry point + 'lib/popup/popup.tsx' // Popup script (JSX or JS) + ], + bundle: true, + sourcemap: true, // Generate source maps for easier debugging + target: 'chrome88', // Set browser target version + outdir: 'outlib', // Output directory + plugins: [{ + name: 'rebuild-notify', + setup(build) { + build.onEnd(result => { + console.log(`build ended with ${result.errors.length} errors`); + // HERE: somehow restart the server from here, e.g., by sending a signal that you trap and react to inside the server. + }) + }, + }], + define: {__VERSION__: '"' + process.env.npm_package_version + '"'} + }); + + await ctx.watch(); // Watch mode + console.log('Watching for changes...'); +} + +watch(); From 976fdf06394cbc63962d6570363871c5f974fc0a Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:33:03 +0700 Subject: [PATCH 05/46] Move storage helper to ts, add support for maps --- lib/helpers/storage.js | 23 -------------------- lib/helpers/storage.ts | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 23 deletions(-) delete mode 100644 lib/helpers/storage.js create mode 100644 lib/helpers/storage.ts diff --git a/lib/helpers/storage.js b/lib/helpers/storage.js deleted file mode 100644 index d91e9d2d..00000000 --- a/lib/helpers/storage.js +++ /dev/null @@ -1,23 +0,0 @@ -var browser = browser || chrome; - -export async function getLocalStorage(key, default_value = null){ - return new Promise((resolve, reject) => { - browser.storage.local.get([key]).then(result => { - if (result[key] === undefined) { - resolve(default_value); - } else { - resolve(result[key]); - } - }); - }); -}; - -export async function setLocalStorage(key, value){ - var obj = {}; - obj[key] = value; - return browser.storage.local.set(obj); -}; - -export async function removeLocalStorage(key){ - return browser.storage.local.remove(key); -}; \ No newline at end of file diff --git a/lib/helpers/storage.ts b/lib/helpers/storage.ts new file mode 100644 index 00000000..4da7ea41 --- /dev/null +++ b/lib/helpers/storage.ts @@ -0,0 +1,49 @@ +import * as browser from 'webextension-polyfill'; + +export async function getLocalStorage(key, default_value = null){ + const result = await browser.storage.local.get([key]); + return result[key] === undefined ? default_value : result[key]; +} + +export async function getLocalStorageMap(key: string): Promise>{ + const result = await browser.storage.local.get([key]); + if (result[key] === undefined) { + return new Map(); + } + let newMap = new Map(); + let obj: Object = result[key]; + for (const [k, v] of Object.entries(obj)) { + let key = parseInt(k); + newMap.set(key as T, v as V); + } + return newMap; +} + +export async function getLocalStorageStringMap(key : string) : Promise> { + const result = await browser.storage.local.get([key]); + if (result[key] === undefined) { + return new Map(); + } + let newMap = new Map(); + let obj : Object = result[key]; + for (const [k, v] of Object.entries(obj)) { + newMap.set(k as T, v as V); + } + return newMap; +} + +export async function setLocalStorage(key : string, value){ + const obj = {}; + obj[key] = value; + return browser.storage.local.set(obj); +} + +export async function setLocalStorageMap(key : string, value : Map) { + const obj = {}; + obj[key] = Object.fromEntries(value); + return browser.storage.local.set(obj); +} + +export async function removeLocalStorage(key){ + return browser.storage.local.remove(key); +} \ No newline at end of file From 8db0fda2b930c2b1afc6391f71fca163cc92d96b Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:33:27 +0700 Subject: [PATCH 06/46] Move utils to ts --- lib/helpers/{utils.js => utils.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename lib/helpers/{utils.js => utils.ts} (94%) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.ts similarity index 94% rename from lib/helpers/utils.js rename to lib/helpers/utils.ts index 3ab04b07..f03252e1 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.ts @@ -2,7 +2,7 @@ // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. -export function debounce(func, wait, immediate) { +export function debounce(func, wait, immediate = false) { var timeout; return function () { var context = this, args = arguments; @@ -32,7 +32,7 @@ export function isInViewport(element, ofElement) { return rect.top >= 0 && rect.left >= 0 && rect.bottom <= ofElement.height && rect.right <= ofElement.width; } -export function stringHashcode(string) { +export function stringHashcode(string) : number { var hash = 0; for (var i = 0; i < string.length; i++) { var code = string.charCodeAt(i); From d05dac90df8f1ea5564d3ca0dd5ced5c4aef7067 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:40:14 +0700 Subject: [PATCH 07/46] Move actions to ts --- lib/service_worker/background/actions.js | 85 --------------- lib/service_worker/background/actions.ts | 131 +++++++++++++++++++++++ 2 files changed, 131 insertions(+), 85 deletions(-) delete mode 100644 lib/service_worker/background/actions.js create mode 100644 lib/service_worker/background/actions.ts diff --git a/lib/service_worker/background/actions.js b/lib/service_worker/background/actions.js deleted file mode 100644 index 62108113..00000000 --- a/lib/service_worker/background/actions.js +++ /dev/null @@ -1,85 +0,0 @@ -var browser = browser || chrome; - -function handleMessages(request, sender, sendResponse) { - switch (request.command) { - case "reload_popup_controls": - setupPopup(); - break; - case "update_tab_count": - updateTabCount(); - break; - case "discard_tabs": - discardTabs(request.tabs); - break; - case "move_tabs_to_window": - moveTabsToWindow(request.window_id, request.tabs); - break; - case "focus_on_tab_and_window": - focusOnTabAndWindow(request.tab); - break; - case "focus_on_tab_and_window_delayed": - focusOnTabAndWindowDelayed(request.tab); - break; - case "focus_on_window": - focusOnWindow(request.window_id); - break; - case "focus_on_window_delayed": - focusOnWindowDelayed(request.window_id); - break; - case "set_window_color": - setWindowColor(request.window_id, request.color); - break; - case "set_window_name": - setWindowName(request.window_id, request.name); - break; - case "create_window_with_tabs": - createWindowWithTabs(request.tabs); - break; - case "create_window_with_session_tabs": - createWindowWithSessionTabs(request.window, request.tab_id); - break; - case "close_tabs": - closeTabs(request.tabs); - break; - } -} - -function handleCommands(command) { - if (command == "switch_to_previous_active_tab") { - if (!!globalTabsActive && globalTabsActive.length > 1) { - focusOnTabAndWindow(globalTabsActive[globalTabsActive.length - 2]); - } - } -} - -async function setWindowColor(windowId, color) { - var colors = await getLocalStorage("windowColors", {}); - if (typeof colors !== 'object') colors = {}; - colors[windowId] = color; - await setLocalStorage("windowColors", colors); - await updateWindowHash(windowId); - browser.runtime.sendMessage({ - command: "refresh_windows", - window_ids: [windowId] - }); -} - -async function setWindowName(windowId, name) { - var names = await getLocalStorage("windowNames", {}); - if (typeof names !== 'object') names = {}; - names[windowId] = name; - await setLocalStorage("windowNames", names); - await updateWindowHash(windowId); - browser.runtime.sendMessage({ - command: "refresh_windows", - window_ids: [windowId] - }); -} - -async function updateWindowHash(windowId) { - var window = await browser.windows.get(windowId, {populate: true}); - var hash = hashcode(window); - var hashes = await getLocalStorage("windowHashes", {}); - hashes[windowId] = hash; - await setLocalStorage("windowHashes", hashes); -} \ No newline at end of file diff --git a/lib/service_worker/background/actions.ts b/lib/service_worker/background/actions.ts new file mode 100644 index 00000000..9a11f425 --- /dev/null +++ b/lib/service_worker/background/actions.ts @@ -0,0 +1,131 @@ +"use strict"; + +import { globalTabsActive } from '@context' +import * as S from "@strings"; +import { focusOnWindow, focusOnWindowDelayed, createWindowWithTabs, createWindowWithSessionTabs, hashcode } from '@background/windows'; +import { getLocalStorageMap, setLocalStorageMap } from "@helpers/storage"; +import { setupPopup } from "@ui/open"; +import { updateTabCount, discardTabs, moveTabsToWindow, closeTabs, focusOnTabAndWindow, focusOnTabAndWindowDelayed } from "@background/tabs"; +import * as browser from 'webextension-polyfill'; +import { ICommand } from '@types'; + +export async function handleMessages(message, sender, sendResponse) { + const request = message as ICommand; + + switch (request.command) { + case S.reload_popup_controls: + setupPopup(); + break; + case S.update_tab_count: + updateTabCount(); + break; + case S.discard_tabs: + discardTabs(request.tabs); + break; + case S.move_tabs_to_window: + moveTabsToWindow(request.window_id, request.tabs); + break; + case S.focus_on_tab_and_window: + if (!!request.tab) { + focusOnTabAndWindow(request.tab.id, request.tab.windowId); + } else { + focusOnTabAndWindow(request.saved_tab.tabId, request.saved_tab.windowId); + } + break; + case S.focus_on_tab_and_window_delayed: + if (!!request.tab) { + focusOnTabAndWindowDelayed(request.tab.id, request.tab.windowId); + } else { + focusOnTabAndWindowDelayed(request.saved_tab.tabId, request.saved_tab.windowId); + } + break; + case S.focus_on_window: + focusOnWindow(request.window_id); + break; + case S.focus_on_window_delayed: + focusOnWindowDelayed(request.window_id); + break; + case S.set_window_color: + setWindowColor(request.window_id, request.color); + break; + case S.set_window_name: + setWindowName(request.window_id, request.name); + break; + case S.create_window_with_tabs: + createWindowWithTabs(request.tabs, request.incognito); + break; + case S.create_window_with_session_tabs: + createWindowWithSessionTabs(request.session, request.tab_id); + break; + case S.close_tabs: + closeTabs(request.tabs); + break; + } +} + +export function handleCommands(command : string) { + if (command === S.switch_to_previous_active_tab) { + if (!!globalTabsActive && globalTabsActive.length > 1) { + var _tab = globalTabsActive[globalTabsActive.length - 2]; + focusOnTabAndWindow(_tab.tabId, _tab.windowId); + } + } +} + +export function trackLastTab(tab : browser.Tabs.OnActivatedActiveInfoType) { + if (!!tab && !!tab.tabId) { + if (!!globalTabsActive && globalTabsActive.length > 0) { + var lastActive = globalTabsActive[globalTabsActive.length - 1]; + if (!!lastActive && lastActive.tabId === tab.tabId && lastActive.windowId === tab.windowId) { + return; + } + } + while (globalTabsActive.length > 20) { + globalTabsActive.shift(); + } + for (let i = globalTabsActive.length - 1; i >= 0; i--) { + if (globalTabsActive[i].tabId === tab.tabId) { + globalTabsActive.splice(i, 1); + } + } + globalTabsActive.push(tab); + } +} + +export async function setWindowColor(windowId : number, color : string) { + var colors : Map = await getLocalStorageMap(S.windowColors); + if (!!color) { + colors.set(windowId, color); + } else { + colors.delete(windowId); + } + await setLocalStorageMap(S.windowColors, colors); + await updateWindowHash(windowId); + browser.runtime.sendMessage({ + command: S.refresh_windows, + window_ids: [windowId] + }); +} + +export async function setWindowName(windowId: number, name : string) { + var names : Map = await getLocalStorageMap(S.windowNames); + if (!!name) { + names.set(windowId, name); + } else { + names.delete(windowId); + } + await setLocalStorageMap(S.windowNames, names); + await updateWindowHash(windowId); + browser.runtime.sendMessage({ + command: S.refresh_windows, + window_ids: [windowId] + }); +} + +async function updateWindowHash(windowId : number) { + const window = await browser.windows.get(windowId, {populate: true}); + const hash = hashcode(window); + const hashes : Map = await getLocalStorageMap(S.windowHashes); + hashes.set(windowId, hash); + await setLocalStorageMap(S.windowHashes, hashes); +} \ No newline at end of file From 9062ec34c349ae4dcb5b440f426656561152240d Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:40:51 +0700 Subject: [PATCH 08/46] Move tabs to ts --- .../background/{tabs.js => tabs.ts} | 70 +++++++------------ 1 file changed, 26 insertions(+), 44 deletions(-) rename lib/service_worker/background/{tabs.js => tabs.ts} (74%) diff --git a/lib/service_worker/background/tabs.js b/lib/service_worker/background/tabs.ts similarity index 74% rename from lib/service_worker/background/tabs.js rename to lib/service_worker/background/tabs.ts index d2e1165d..81ada837 100644 --- a/lib/service_worker/background/tabs.js +++ b/lib/service_worker/background/tabs.ts @@ -1,6 +1,13 @@ -var browser = browser || chrome; +"use strict"; -async function setupTabListeners() { +import {getLocalStorage} from "@helpers/storage"; +import {trackLastTab} from "@background/actions" +import {globalTabsActive} from '@context'; +import {debounce} from "@helpers/utils"; +import {checkWindow, createWindowWithTabs} from '@background/windows'; +import * as browser from 'webextension-polyfill'; + +export async function setupTabListeners() { browser.tabs.onCreated.removeListener(tabAdded); browser.tabs.onUpdated.removeListener(tabCountChanged); browser.tabs.onRemoved.removeListener(tabCountChanged); @@ -47,7 +54,7 @@ export async function discardTabs(tabs) { export async function closeTabs(tabs) { for (const tab of tabs) { - await browser.tabs.remove(item.id); + await browser.tabs.remove(tab.id); } } @@ -58,45 +65,36 @@ export async function moveTabsToWindow(windowId, tabs) { } } -export function focusOnTabAndWindowDelayed(tab) { - var _tab = JSON.parse(JSON.stringify(tab)); - setTimeout(focusOnTabAndWindow.bind(this, _tab), 125); +export function focusOnTabAndWindowDelayed(tabId: number, windowId: number) { + setTimeout(focusOnTabAndWindow.bind(this, tabId, windowId), 125); } -export async function focusOnTabAndWindow(tab) { - var windowId = tab.windowId; - var tabId; - if (!!tab.tabId) { - tabId = tab.tabId; - } else { - tabId = tab.id; - } - +export async function focusOnTabAndWindow(tabId : number, windowId : number) { await browser.windows.update(windowId, {focused: true}); await browser.tabs.update(tabId, {active: true}); await tabActiveChanged({tabId: tabId, windowId: windowId}); } export async function updateTabCount() { - var run = true; + let run = true; - var badge = await getLocalStorage("badge", true); + const badge = await getLocalStorage("badge", true); if (!badge) run = false; if (run) { - var result = await browser.tabs.query({}); - var count = 0; + let result = await browser.tabs.query({}); + let count = 0; if (!!result && !!result.length) { count = result.length; } await browser.action.setBadgeText({text: count + ""}); await browser.action.setBadgeBackgroundColor({color: "purple"}); - var _to_remove = []; + const _to_remove : number[] = []; if (!!globalTabsActive) { for (let i = 0; i < globalTabsActive.length; i++) { - var t = globalTabsActive[i]; - var found = false; + const t = globalTabsActive[i]; + let found = false; if (!!result && !!result.length) { for (let j = 0; j < result.length; j++) { if (result[j].id === t.tabId) found = true; @@ -108,7 +106,6 @@ export async function updateTabCount() { while (_to_remove.length > 0) { let index = _to_remove.pop(); - // console.log("removing", toRemove[i]); if (!!globalTabsActive && globalTabsActive.length > 0) { if (!!globalTabsActive[index]) globalTabsActive.splice(index, 1); } @@ -123,11 +120,13 @@ function tabCountChanged() { updateTabCountDebounce(); } +export const updateTabCountDebounce = debounce(updateTabCount, 250); + async function tabAdded(tab) { - var tabLimit = await getLocalStorage("tabLimit", 0); + const tabLimit = await getLocalStorage("tabLimit", 0); if (tabLimit > 0) { if (tab.id !== browser.tabs.TAB_ID_NONE) { - var tabCount = await browser.tabs.query({currentWindow: true}); + const tabCount = await browser.tabs.query({currentWindow: true}); if (tabCount.length > tabLimit) { await createWindowWithTabs([tab], tab.incognito); } @@ -136,25 +135,8 @@ async function tabAdded(tab) { updateTabCountDebounce(); } -function tabActiveChanged(tab) { - if (!!tab && !!tab.tabId) { - if (!globalTabsActive) globalTabsActive = []; - if (!!globalTabsActive && globalTabsActive.length > 0) { - var lastActive = globalTabsActive[globalTabsActive.length - 1]; - if (!!lastActive && lastActive.tabId == tab.tabId && lastActive.windowId == tab.windowId) { - return; - } - } - while (globalTabsActive.length > 20) { - globalTabsActive.shift(); - } - for (var i = globalTabsActive.length - 1; i >= 0; i--) { - if (globalTabsActive[i].tabId == tab.tabId) { - globalTabsActive.splice(i, 1); - } - } - globalTabsActive.push(tab); - } +function tabActiveChanged(tab : browser.Tabs.OnActivatedActiveInfoType) { + trackLastTab(tab); updateTabCountDebounce(); } From 497204867ff50bcb67eb8a4bce51d7be5feadbca Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:42:24 +0700 Subject: [PATCH 09/46] Move window tracking to ts --- lib/service_worker/background/tracking.js | 116 ---------------------- lib/service_worker/background/tracking.ts | 112 +++++++++++++++++++++ 2 files changed, 112 insertions(+), 116 deletions(-) delete mode 100644 lib/service_worker/background/tracking.js create mode 100644 lib/service_worker/background/tracking.ts diff --git a/lib/service_worker/background/tracking.js b/lib/service_worker/background/tracking.js deleted file mode 100644 index 241441e8..00000000 --- a/lib/service_worker/background/tracking.js +++ /dev/null @@ -1,116 +0,0 @@ -var browser = browser || chrome; - -async function cleanUp() { - var activewindows = await browser.windows.getAll({populate: true}); - var windowids = []; - for (let _w of activewindows) { - windowids.push(_w.id); - } - // console.log("window ids...", windowids); - - var windows = await getLocalStorage("windowAge", []); - if (!(windows instanceof Array)) windows = []; - - // console.log("before", JSON.parse(JSON.stringify(windows))); - for (let i = windows.length - 1; i >= 0; i--) { - if (windowids.indexOf(windows[i]) < 0) { - // console.log("did not find", windows[i], i); - windows.splice(i, 1); - } - } - - // console.log("after", JSON.parse(JSON.stringify(windows))); - await setLocalStorage("windowAge", windows); - - var names = await getLocalStorage("windowNames", {}); - var colors = await getLocalStorage("windowColors", {}); - var to_check = new Set(); - var exists = new Set(); - var to_refresh = []; - - // console.log("before", JSON.parse(JSON.stringify(names))); - for (let n_id in names) { - if (windowids.indexOf(parseInt(n_id)) < 0) { - // console.log("did not find", n_id); - to_check.add(n_id); - } else { - exists.add(n_id); - } - } - - for (let c_id in colors) { - if (windowids.indexOf(parseInt(c_id)) < 0) { - // console.log("did not find", c_id); - to_check.add(c_id); - } else { - exists.add(c_id); - } - } - - if (to_check.size > 0) { - var hashes = await getLocalStorage("windowHashes", {}); - console.log(hashes); - console.log(names); - console.log(colors); - - // delete hashes with empty values - for (let _h_id in hashes) if (!hashes[_h_id]) delete hashes[_h_id]; - for (let _c_id in colors) if (!colors[_c_id]) delete colors[_c_id]; - - var found = false; - - for (let w of activewindows) { - var windowhash = hashcode(w); - for (let id in hashes) { - if (!to_check.has(id)) continue; - if (exists.has(id)) continue; - if (w.id === id) break; - if (hashes[id] === windowhash) { - console.log("found by hash, old id " + id + " new id " + w.id); - to_refresh.push(w.id); - if (!!names[id]) { - names[w.id] = names[id]; - delete names[id]; - } - if (!!colors[id]) { - colors[w.id] = colors[id]; - delete colors[id]; - } - hashes[w.id] = names[id]; - delete hashes[id]; - found = true; - to_check.delete(id); - break; - } - } - } - - var save = false; - if (remove_old) { - for (const _id of to_check) { - delete colors[_id]; - delete names[_id]; - delete hashes[_id]; - save = true; - } - } - - if (found || save) { - await setLocalStorage("windowNames", names); - await setLocalStorage("windowColors", colors); - await setLocalStorage("windowHashes", hashes); - if (found) { - browser.runtime.sendMessage({ - command: "refresh_windows", - window_ids: to_refresh - }); - } - } - - console.log(to_check) - console.log(exists) - console.log(hashes); - console.log(names); - console.log(colors); - } -} \ No newline at end of file diff --git a/lib/service_worker/background/tracking.ts b/lib/service_worker/background/tracking.ts new file mode 100644 index 00000000..1b485e97 --- /dev/null +++ b/lib/service_worker/background/tracking.ts @@ -0,0 +1,112 @@ +"use strict"; + +import {hashcode} from "@background/windows" +import {debounce} from "@helpers/utils"; +import {getLocalStorageMap, setLocalStorageMap, getLocalStorage, setLocalStorage} from "@helpers/storage"; +import * as S from "@strings"; +import * as browser from 'webextension-polyfill'; +import {ICommand} from "@types"; + +export const cleanupDebounce = debounce(cleanUp, 500); + +export async function cleanUp(remove_old = false) { + let activewindows = await browser.windows.getAll({populate: true}); + let windowids: number[] = []; + for (let _w of activewindows) { + windowids.push(_w.id); + } + // console.log("window ids...", windowids); + + let windows = await getLocalStorage("windowAge", []); + if (!(windows instanceof Array)) windows = []; + + // console.log("before", JSON.parse(JSON.stringify(windows))); + for (let i = windows.length - 1; i >= 0; i--) { + if (windowids.indexOf(windows[i]) < 0) { + // console.log("did not find", windows[i], i); + windows.splice(i, 1); + } + } + + // console.log("after", JSON.parse(JSON.stringify(windows))); + await setLocalStorage("windowAge", windows); + + let names : Map = await getLocalStorageMap(S.windowNames); + let colors : Map = await getLocalStorageMap(S.windowColors); + let to_check = new Set(); + let exists = new Set(); + let to_refresh : number[] = []; + + // console.log("before", JSON.parse(JSON.stringify(names))); + for (const [id, _name] of names) { + if (windowids.indexOf(id) < 0) { + // console.log("did not find", id); + to_check.add(id); + } else { + exists.add(id); + } + } + + for (const [id, _color] of colors) { + if (windowids.indexOf(id) < 0) { + // console.log("did not find", id); + to_check.add(id); + } else { + exists.add(id); + } + } + + if (to_check.size > 0) { + let hashes : Map = await getLocalStorageMap(S.windowHashes); + let found = false; + + for (let w of activewindows) { + const windowhash = hashcode(w); + for (const [id, _hash] of hashes) { + if (!to_check.has(id)) continue; + if (exists.has(id)) continue; + if (w.id === id) break; + if (_hash === windowhash) { + console.log("found by hash, old id " + id + " new id " + w.id); + to_refresh.push(w.id); + if (!!names.get(id)) { + names.set(w.id, names.get(id)); + names.delete(id); + } + if (!!colors.get(id)) { + colors.set(w.id, colors.get(id)); + colors.delete(id); + } + hashes.set(w.id, _hash); + hashes.delete(id); + found = true; + to_check.delete(id); + break; + } + } + } + + let save = false; + if (remove_old) { + for (const _id of to_check) { + console.log("should delete from to check " + _id); + colors.delete(_id); + names.delete(_id); + hashes.delete(_id); + save = true; + } + } + + if (found || save) { + await setLocalStorageMap(S.windowNames, names); + await setLocalStorageMap(S.windowColors, colors); + await setLocalStorageMap(S.windowHashes, hashes); + if (found) { + browser.runtime.sendMessage({ + command: S.refresh_windows, + window_ids: to_refresh + }); + } + } + } +} \ No newline at end of file From d100d1f08b1ac436dd2c8959667aec76a4f854f6 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:43:23 +0700 Subject: [PATCH 10/46] Move windows service worker to ts --- .../background/{windows.js => windows.ts} | 194 +++++++++--------- 1 file changed, 101 insertions(+), 93 deletions(-) rename lib/service_worker/background/{windows.js => windows.ts} (56%) diff --git a/lib/service_worker/background/windows.js b/lib/service_worker/background/windows.ts similarity index 56% rename from lib/service_worker/background/windows.js rename to lib/service_worker/background/windows.ts index c3e33371..0d55a24d 100644 --- a/lib/service_worker/background/windows.js +++ b/lib/service_worker/background/windows.ts @@ -1,6 +1,14 @@ -var browser = browser || chrome; +"use strict"; -async function setupWindowListeners() { +import {cleanupDebounce} from "@background/tracking"; +import {getLocalStorage, getLocalStorageMap, setLocalStorage, setLocalStorageMap} from "@helpers/storage"; +import {is_in_bounds, stringHashcode} from "@helpers/utils"; +import {setWindowColor, setWindowName} from "@background/actions"; +import * as S from "@strings"; +import * as browser from 'webextension-polyfill'; +import {ISavedSession} from "@types"; + +export async function setupWindowListeners() { browser.windows.onFocusChanged.removeListener(windowFocus); browser.windows.onCreated.removeListener(windowCreated); browser.windows.onRemoved.removeListener(windowRemoved); @@ -10,7 +18,7 @@ async function setupWindowListeners() { browser.windows.onRemoved.addListener(windowRemoved); } -export async function createWindowWithTabs(tabs, isIncognito) { +export async function createWindowWithTabs(tabs, isIncognito = false) { var pinnedIndex = 0; var firstTab = tabs.shift(); var t = []; @@ -31,16 +39,23 @@ export async function createWindowWithTabs(tabs, isIncognito) { i++; var oldTab = await browser.tabs.get(oldTabId); var tabPinned = oldTab.pinned; - var movedTabs = []; + var movedTabs : browser.Tabs.Tab | browser.Tabs.Tab[] = []; if (!tabPinned) { movedTabs = await browser.tabs.move(oldTabId, {windowId: w.id, index: -1}); } else { movedTabs = await browser.tabs.move(oldTabId, {windowId: w.id, index: pinnedIndex++}); } - if (movedTabs.length > 0) { - var newTab = movedTabs[0]; + + let firstTab : browser.Tabs.Tab; + if (Array.isArray(movedTabs)) { + firstTab = movedTabs[0]; + } else { + firstTab = movedTabs; + } + + if (!!firstTab) { if (tabPinned) { - await browser.tabs.update(newTab.id, {pinned: tabPinned}); + await browser.tabs.update(firstTab.id, {pinned: tabPinned}); } } } @@ -48,15 +63,15 @@ export async function createWindowWithTabs(tabs, isIncognito) { await browser.windows.update(w.id, {focused: true}); } -export async function createWindowWithSessionTabs(window, tabId) { +export async function createWindowWithSessionTabs(session: ISavedSession, tabId: number) { - var customName = false; - if (window && window.name && window.customName) { - customName = window.name; + var customName : string; + if (session && session.name && session.customName) { + customName = session.name; } var color = "default"; - if (window && window.color) { - color = window.color; + if (session && session.color) { + color = session.color; } var whitelistWindow = ["left", "top", "width", "height", "incognito", "type"]; @@ -71,12 +86,12 @@ export async function createWindowWithSessionTabs(window, tabId) { whitelistTab = ["url", "active", "pinned", "index"]; } - var filteredWindow = Object.keys(window.windowsInfo) + var filteredWindow : browser.Windows.CreateCreateDataType = Object.keys(session.windowsInfo) .filter(function (key) { return whitelistWindow.includes(key); }) .reduce(function (obj, key) { - obj[key] = window.windowsInfo[key]; + obj[key] = session.windowsInfo[key]; return obj; }, {}); @@ -89,37 +104,41 @@ export async function createWindowWithSessionTabs(window, tabId) { // console.log("filtered window", filteredWindow); - var newWindow = await browser.windows.create(filteredWindow).catch(function (error) { + const newWindow = await browser.windows.create(filteredWindow).catch(function (error) { console.error(error); console.log(error); console.log(error.message); }); - var emptyTab = newWindow.tabs[0].id; + if (!newWindow) return; - for (let i = 0; i < window.tabs.length; i++) { - var newTab = Object.keys(window.tabs[i]) + let emptyTab = newWindow.tabs[0].id; + + for (let i = 0; i < session.tabs.length; i++) { + let newTab = Object.keys(session.tabs[i]) .filter(function (key) { return whitelistTab.includes(key); }) .reduce(function (obj, key) { - obj[key] = window.tabs[i][key]; + obj[key] = session.tabs[i][key]; return obj; }, {}); - if (tabId != null && tabId !== newTab.index) { + var fTab : browser.Tabs.Tab = newTab as browser.Tabs.Tab; + + if (tabId != null && tabId !== fTab.index) { continue; } - newTab.windowId = newWindow.id; + fTab.windowId = newWindow.id; if (navigator.userAgent.search("Firefox") > -1) { - if (!!newTab.url && newTab.url.search("about:") > -1) { - console.log("filtered by about: url", newTab.url); - newTab.url = ""; + if (!!fTab.url && fTab.url.search("about:") > -1) { + console.log("filtered by about: url", fTab.url); + fTab.url = ""; } } try { - await browser.tabs.create(newTab).catch(function (error) { + await browser.tabs.create(fTab).catch(function (error) { console.error(error); console.log(error); console.log(error.message); @@ -138,85 +157,75 @@ export async function createWindowWithSessionTabs(window, tabId) { if (customName) { console.log("setting name"); - setWindowName(newWindow.id, customName); + await setWindowName(newWindow.id, customName); } if (color !== "default") { console.log("setting color"); - setWindowColor(newWindow.id, color); + await setWindowColor(newWindow.id, color); } await browser.windows.update(newWindow.id, {focused: true}); } -export function focusOnWindowDelayed(windowId) { +export function focusOnWindowDelayed(windowId: number) { setTimeout(focusOnWindow.bind(this, windowId), 125); } -export async function focusOnWindow(windowId) { +export async function focusOnWindow(windowId : number) { await browser.windows.update(windowId, {focused: true}); } -async function hideWindows(windowId) { +async function hideWindows(windowId : number) { if (navigator.userAgent.search("Firefox") > -1) return; if (!windowId || windowId < 0) return; - var hideWindows = await getLocalStorage("hideWindows", false); - if (!hideWindows) return; - - var result = await browser.permissions.contains({permissions: ['system.display']}); - if (result) { - // The extension has the permissions. - chrome.system.display.getInfo(async function (windowId, displaylayouts) { - var globalDisplayInfo = context.globalDisplayInfo; - globalDisplayInfo.clear(); - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - try { - for (let _iterator = displaylayouts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var displaylayout = _step.value; - globalDisplayInfo.push(displaylayout.bounds); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - var windows = await browser.windows.getAll({populate: true}); - var monitor = -1; - for (let i = windows.length - 1; i >= 0; i--) { - if (windows[i].id === windowId) { - for (let a in globalDisplayInfo) { - var result = is_in_bounds(windows[i], globalDisplayInfo[a]); - if (result) { - monitor = a; - } - } + let hide_windows = await getLocalStorage("hideWindows", false); + if (!hide_windows) return; + + let has_permission = await browser.permissions.contains({permissions: ['system.display']}); + if (!has_permission) return; + + let displaylayouts = await chrome.system.display.getInfo(); + let monitor_bounds = []; + + try { + for (let displaylayout of displaylayouts) { + monitor_bounds.push(displaylayout.bounds); + } + } catch (err) { + console.error(err); + return; + } + + let windows = await browser.windows.getAll({populate: true}); + let monitor = null; + + for (let window of windows) { + if (window.id === windowId) { + for (let bounds_index in monitor_bounds) { + let _monitor = monitor_bounds[bounds_index]; + let _is_in_bounds = is_in_bounds(window, _monitor); + if (_is_in_bounds) { + monitor = _monitor; + break; } } + } + } - for (let i = windows.length - 1; i >= 0; i--) { - if (windows[i].id !== windowId) { - if (is_in_bounds(windows[i], globalDisplayInfo[monitor])) { - await browser.windows.update(windows[i].id, {"state": "minimized"}); - } - } + if (monitor == null) return; + + for (let window of windows) { + if (window.id !== windowId) { + if (is_in_bounds(window, monitor)) { + await browser.windows.update(window.id, {"state": "minimized"}); } - }.bind(null, windowId)); + } } } -export async function windowActive(windowId) { +export async function windowActive(windowId : number) { if (windowId < 0) return; var windows = []; @@ -277,39 +286,38 @@ async function windowRemoved(windowId) { // console.log("onRemoved", windowId); } -export async function checkWindow(windowId) { +export async function checkWindow(windowId : number) { if (!windowId) return; - var storage = await browser.storage.local.get(['windowNames', 'windowColors', 'windowHashes']); - - var names = storage.windowNames || {}; - var colors = storage.windowColors || {}; - var hashes = storage.windowHashes || {}; + const colors: Map = await getLocalStorageMap(S.windowColors); + const names: Map = await getLocalStorageMap(S.windowNames); if (!names[windowId] && !colors[windowId]) return; + const hashes: Map = await getLocalStorageMap(S.windowHashes); + try { - var window = await browser.windows.get(windowId, {populate: true}); + const window = await browser.windows.get(windowId, {populate: true}); let newHash = hashcode(window); - hashes[windowId] = newHash; - await setLocalStorage('windowHashes', hashes); + hashes.set(windowId, newHash); + await setLocalStorageMap(S.windowHashes, hashes); } catch (e) { console.log(e); } } -export function hashcode(window) { - var urls = []; +export function hashcode(window) : number { + let urls = []; for (let i = 0; i < window.tabs.length; i++) { if (!window.tabs[i].url) continue; urls.push(window.tabs[i].url); } urls.sort(); - var hash = 0; + let hash = 0; for (let i = 0; i < urls.length; i++) { - var code = stringHashcode(urls[i]); + const code = stringHashcode(urls[i]); hash = ((hash << 5) - hash) + code; hash = hash & hash; // Convert to 32bit integer } From 9c4a7df01585356963435fdbe3eeeeee26bfcfbb Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:43:34 +0700 Subject: [PATCH 11/46] Shared array --- lib/service_worker/context.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/service_worker/context.ts diff --git a/lib/service_worker/context.ts b/lib/service_worker/context.ts new file mode 100644 index 00000000..de1778ef --- /dev/null +++ b/lib/service_worker/context.ts @@ -0,0 +1,3 @@ +"use strict"; + +export let globalTabsActive : browser.tabs._OnActivatedActiveInfo[] = []; \ No newline at end of file From 1f4ea72b36544ba597f94ee59584b2f1120d33e1 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:44:50 +0700 Subject: [PATCH 12/46] Move service worker to ts --- lib/service_worker/service_worker.js | 71 ---------------------------- lib/service_worker/service_worker.ts | 60 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 71 deletions(-) delete mode 100644 lib/service_worker/service_worker.js create mode 100644 lib/service_worker/service_worker.ts diff --git a/lib/service_worker/service_worker.js b/lib/service_worker/service_worker.js deleted file mode 100644 index 4c6ae78a..00000000 --- a/lib/service_worker/service_worker.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; - -importScripts('../../vendor/babel-polyfill.js'); -importScripts('../../vendor/browser-polyfill.min.js'); - -importScripts('../helpers/storage.js'); -importScripts('../helpers/utils.js'); - -importScripts('./background/actions.js'); -importScripts('./background/windows.js'); -importScripts('./background/tabs.js'); -importScripts('./background/tracking.js'); - -importScripts('./ui/context_menus.js'); -importScripts('./ui/open.js'); - -var browser = browser || chrome; -var globalTabsActive = []; -var globalDisplayInfo = []; - -browser.runtime.onStartup.addListener( - async function () { - console.log(" ON STARTUP"); - } -); - -browser.runtime.onSuspend.addListener( - async function () { - console.log(" ON SUSPEND"); - } -); - -var updateTabCountDebounce = debounce(updateTabCount, 250); -var cleanupDebounce = debounce(cleanUp, 500); - -async function openSidebar() { - await browser.sidebarAction.open(); -} - -async function setup() { - await setupContextMenus(); - await setupPopup(); - await setupTabListeners(); - await setupWindowListeners(); - - updateTabCountDebounce(); - - setTimeout(cleanupDebounce, 2500); -} - -browser.commands.onCommand.addListener(handleCommands); -browser.runtime.onMessage.addListener(handleMessages); - -(async function () { - var windows = await browser.windows.getAll({ populate: true }); - await setLocalStorage("windowAge", []); - if (!!windows && windows.length > 0) { - windows.sort(function (a, b) { - if (a.id < b.id) return 1; - if (a.id > b.id) return -1; - return 0; - }); - for (var i = 0; i < windows.length; i++) { - if (!!windows[i].id) await windowActive(windows[i].id); - }; - } -})(); - -setInterval(setup, 300000); - -setup(); \ No newline at end of file diff --git a/lib/service_worker/service_worker.ts b/lib/service_worker/service_worker.ts new file mode 100644 index 00000000..ad446360 --- /dev/null +++ b/lib/service_worker/service_worker.ts @@ -0,0 +1,60 @@ +"use strict"; + +import { setLocalStorage } from "@helpers/storage"; +import * as _a from "@background/actions"; +import * as _w from '@background/windows'; +import * as _t from '@background/tabs'; +import { cleanupDebounce, cleanUp } from '@background/tracking'; + +import * as _c from '@ui/context_menus'; +import * as _o from '@ui/open'; +import {debounce} from "@helpers/utils"; +import * as browser from 'webextension-polyfill'; + +browser.runtime.onStartup.addListener( + async function () { + console.log(" ON STARTUP"); + } +); + +browser.runtime.onSuspend.addListener( + async function () { + console.log(" ON SUSPEND"); + } +); + +browser.commands.onCommand.addListener(_a.handleCommands); +browser.runtime.onMessage.addListener(_a.handleMessages); + +(async function () { + var windows = await browser.windows.getAll({ populate: true }); + await setLocalStorage("windowAge", []); + if (!!windows && windows.length > 0) { + windows.sort(function (a, b) { + if (a.id < b.id) return 1; + if (a.id > b.id) return -1; + return 0; + }); + for (let i = 0; i < windows.length; i++) { + if (!!windows[i].id) await _w.windowActive(windows[i].id); + } + } +})(); + +export const setupDebounced = debounce(setup, 2000); + +async function setup() { + await _c.setupContextMenus(); + await _o.setupPopup(); + await _t.setupTabListeners(); + await _w.setupWindowListeners(); + + _t.updateTabCountDebounce(); + + setTimeout(cleanupDebounce, 2500); + setTimeout(cleanUp.bind(this, true), 200000); +} + +setInterval(setupDebounced, 300000); + +setup(); \ No newline at end of file From 353105562049abf6f39b4e5da0e6be39f90fdfda Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:46:36 +0700 Subject: [PATCH 13/46] Move context menu creation to ts --- lib/service_worker/ui/context_menus.js | 138 --------------------- lib/service_worker/ui/context_menus.ts | 163 +++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 138 deletions(-) delete mode 100644 lib/service_worker/ui/context_menus.js create mode 100644 lib/service_worker/ui/context_menus.ts diff --git a/lib/service_worker/ui/context_menus.js b/lib/service_worker/ui/context_menus.js deleted file mode 100644 index 5c2343fc..00000000 --- a/lib/service_worker/ui/context_menus.js +++ /dev/null @@ -1,138 +0,0 @@ -var browser = browser || chrome; - -async function setupContextMenus() { - await browser.contextMenus.removeAll(); - browser.contextMenus.create({ - id: "open_in_own_tab", - title: "📔 Open in own tab", - contexts: ["action"] - }); - - if (!!browser.action.openPopup) { - browser.contextMenus.create({ - id: "open_popup", - title: "📑 Open popup", - contexts: ["action"] - }); - } - - if (!!browser.sidebarAction) { - browser.contextMenus.create({ - id: "open_sidebar", - title: "🗂 Open sidebar", - contexts: ["action"] - }); - } - - browser.contextMenus.create({ - id: "sep1", - type: "separator", - contexts: ["action"] - }); - - browser.contextMenus.create({ - title: "😍 Support this extension", - id: "support_menu", - "contexts": ["action"] - }); - - browser.contextMenus.create({ - id: "review", - title: "⭐ Leave a review", - "contexts": ["action"], - parentId: "support_menu" - }); - - browser.contextMenus.create({ - id: "donate", - title: "☕ Donate to keep Extensions Alive", - "contexts": ["action"], - parentId: "support_menu" - }); - - browser.contextMenus.create({ - id: "patron", - title: "💰 Become a Patron", - "contexts": ["action"], - parentId: "support_menu" - }); - - browser.contextMenus.create({ - id: "twitter", - title: "🐦 Follow on Twitter", - "contexts": ["action"], - parentId: "support_menu" - }); - - browser.contextMenus.create({ - title: "🤔 Issues and Suggestions", - id: "code_menu", - "contexts": ["action"] - }); - - browser.contextMenus.create({ - id: "changelog", - title: "🆕 View recent changes", - "contexts": ["action"], - parentId: "code_menu" - }); - - browser.contextMenus.create({ - id: "options", - title: "⚙ Edit Options", - "contexts": ["action"], - parentId: "code_menu" - }); - - browser.contextMenus.create({ - id: "source", - title: "💻 View source code", - "contexts": ["action"], - parentId: "code_menu" - }); - - browser.contextMenus.create({ - id: "report", - title: "🤔 Report an issue", - "contexts": ["action"], - parentId: "code_menu" - }); - - browser.contextMenus.create({ - id: "send", - title: "💡 Send a suggestion", - "contexts": ["action"], - parentId: "code_menu" - }); - - browser.contextMenus.onClicked.addListener( - function (info, tab) { - if (info.menuItemId == "open_in_own_tab") openAsOwnTab(); - if (info.menuItemId == "open_popup") openPopup(); - if (info.menuItemId == "open_sidebar") openSidebar(); - - if (info.menuItemId == "donate") browser.tabs.create({url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=67TZLSEGYQFFW'}); - if (info.menuItemId == "patron") browser.tabs.create({url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=67TZLSEGYQFFW'}); - if (info.menuItemId == "changelog") browser.tabs.create({url: 'changelog.html'}); - if (info.menuItemId == "options") browser.tabs.create({url: 'options.html'}); - if (info.menuItemId == "report") browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus/issues'}); - - if (info.menuItemId == "source") browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus'}); - - if (info.menuItemId == "twitter") browser.tabs.create({url: 'https://www.twitter.com/mastef'}); - - if (info.menuItemId == "send") { - browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus/issues'}); - browser.tabs.create({url: 'mailto:markus+tmp@stefanxo.com'}); - } - - if (info.menuItemId == "review") { - if (navigator.userAgent.search("Firefox") > -1) { - browser.tabs.create({url: 'https://addons.mozilla.org/en-US/firefox/addon/tab-manager-plus-for-firefox/'}); - } else { - browser.tabs.create({url: 'https://chrome.google.com/webstore/detail/tab-manager-plus-for-chro/cnkdjjdmfiffagllbiiilooaoofcoeff'}); - } - } - } - ); -} \ No newline at end of file diff --git a/lib/service_worker/ui/context_menus.ts b/lib/service_worker/ui/context_menus.ts new file mode 100644 index 00000000..6f0afef4 --- /dev/null +++ b/lib/service_worker/ui/context_menus.ts @@ -0,0 +1,163 @@ +"use strict"; + +import {openPopup, openAsOwnTab, openSidebar} from "@ui/open"; +import * as S from "@strings"; +import * as browser from 'webextension-polyfill'; + +export async function setupContextMenus() { + await browser.contextMenus.removeAll(); + + browser.contextMenus.create({ + id: S.open_in_own_tab, + title: "📔 Open in own tab", + contexts: ["action"] + }); + + if (!!browser.action.openPopup) { + browser.contextMenus.create({ + id: S.open_popup, + title: "📑 Open popup", + contexts: ["action"] + }); + } + + if (!!browser.sidebarAction) { + browser.contextMenus.create({ + id: S.open_sidebar, + title: "🗂 Open sidebar", + contexts: ["action"] + }); + } + + browser.contextMenus.create({ + id: S.sep1, + type: "separator", + contexts: ["action"] + }); + + browser.contextMenus.create({ + title: "😍 Support this extension", + id: S.support_menu, + "contexts": ["action"] + }); + + browser.contextMenus.create({ + id: S.review, + title: "⭐ Leave a review", + "contexts": ["action"], + parentId: "support_menu" + }); + + browser.contextMenus.create({ + id: S.donate, + title: "☕ Donate to keep Extensions Alive", + "contexts": ["action"], + parentId: "support_menu" + }); + + browser.contextMenus.create({ + id: S.patron, + title: "💰 Become a Patron", + "contexts": ["action"], + parentId: "support_menu" + }); + + browser.contextMenus.create({ + id: S.twitter, + title: "🐦 Follow on Twitter", + "contexts": ["action"], + parentId: "support_menu" + }); + + browser.contextMenus.create({ + title: "🤔 Issues and Suggestions", + id: S.code_menu, + "contexts": ["action"] + }); + + browser.contextMenus.create({ + id: S.changelog, + title: "🆕 View recent changes", + "contexts": ["action"], + parentId: "code_menu" + }); + + browser.contextMenus.create({ + id: S.options, + title: "⚙ Edit Options", + "contexts": ["action"], + parentId: "code_menu" + }); + + browser.contextMenus.create({ + id: S.source, + title: "💻 View source code", + "contexts": ["action"], + parentId: "code_menu" + }); + + browser.contextMenus.create({ + id: S.report, + title: "🤔 Report an issue", + "contexts": ["action"], + parentId: "code_menu" + }); + + browser.contextMenus.create({ + id: S.send, + title: "💡 Send a suggestion", + "contexts": ["action"], + parentId: "code_menu" + }); + + browser.contextMenus.onClicked.removeListener(contextListeners); + browser.contextMenus.onClicked.addListener(contextListeners); +} + +async function contextListeners(info: browser.Menus.OnClickData, tab?: browser.Tabs.Tab) +{ + switch (info.menuItemId) { + case S.open_in_own_tab: + await openAsOwnTab(); + break; + case S.open_popup: + await openPopup(); + break; + case S.open_sidebar: + await openSidebar(); + break; + case S.donate: + await browser.tabs.create({url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=67TZLSEGYQFFW'}); + break; + case S.patron: + await browser.tabs.create({url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=67TZLSEGYQFFW'}); + break; + case S.changelog: + await browser.tabs.create({url: 'changelog.html'}); + break; + case S.options: + await browser.tabs.create({url: 'options.html'}); + break; + case S.report: + await browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus/issues'}); + break; + case S.source: + await browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus'}); + break; + case S.twitter: + await browser.tabs.create({url: 'https://www.twitter.com/mastef'}); + break; + case S.send: + await browser.tabs.create({url: 'https://github.com/stefanXO/Tab-Manager-Plus/issues'}); + await browser.tabs.create({url: 'mailto:markus+tmp@stefanxo.com'}); + break; + case S.review: + if (navigator.userAgent.search("Firefox") > -1) { + await browser.tabs.create({url: 'https://addons.mozilla.org/en-US/firefox/addon/tab-manager-plus-for-firefox/'}); + } else { + await browser.tabs.create({url: 'https://chrome.google.com/webstore/detail/tab-manager-plus-for-chro/cnkdjjdmfiffagllbiiilooaoofcoeff'}); + } + break; + + } +} \ No newline at end of file From b7189260189c2c23529e294c61963cc92f79cccc Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:48:09 +0700 Subject: [PATCH 14/46] Move popup opener to ts --- lib/service_worker/ui/open.js | 56 ------------------------------ lib/service_worker/ui/open.ts | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 56 deletions(-) delete mode 100644 lib/service_worker/ui/open.js create mode 100644 lib/service_worker/ui/open.ts diff --git a/lib/service_worker/ui/open.js b/lib/service_worker/ui/open.js deleted file mode 100644 index 8a3d4d23..00000000 --- a/lib/service_worker/ui/open.js +++ /dev/null @@ -1,56 +0,0 @@ -var browser = browser || chrome; - -async function openPopup() { - var openInOwnTab = await getLocalStorage("openInOwnTab", false); - if (openInOwnTab) { - await browser.action.setPopup({popup: "popup.html?popup=true"}); - await browser.action.openPopup(); - await browser.action.setPopup({popup: ""}); - } else { - await browser.action.openPopup(); - } -} - -async function openAsOwnTab() { - var popup_page = browser.runtime.getURL("popup.html"); - var tabs = await browser.tabs.query({}); - - var currentTab; - var previousTab; - if (!!globalTabsActive && globalTabsActive.length > 1) { - currentTab = globalTabsActive[globalTabsActive.length - 1]; - previousTab = globalTabsActive[globalTabsActive.length - 2]; - } - - for (var i = 0; i < tabs.length; i++) { - var tab = tabs[i]; - if (tab.url.indexOf("popup.html") > -1 && tab.url.indexOf(popup_page) > -1) { - if (currentTab && currentTab.tabId && tab.id == currentTab.tabId && previousTab && previousTab.tabId) { - return focusOnTabAndWindow(previousTab); - } else { - return browser.windows.update(tab.windowId, {focused: true}).then( - function () { - browser.tabs.highlight({windowId: tab.windowId, tabs: tab.index}); - }.bind(this) - ); - } - } - } - return browser.tabs.create({url: "popup.html"}); -} - -async function setupPopup() { - - var openInOwnTab = await getLocalStorage("openInOwnTab", false); - - await browser.action.onClicked.removeListener(openAsOwnTab); - if (openInOwnTab) { - await browser.action.setPopup({popup: ""}); - await browser.action.onClicked.addListener(openAsOwnTab); - } else { - await browser.action.setPopup({popup: "popup.html?popup=true"}); - } - if (browser.sidebarAction) { - browser.sidebarAction.setPanel({panel: "popup.html?panel=true"}); - } -} \ No newline at end of file diff --git a/lib/service_worker/ui/open.ts b/lib/service_worker/ui/open.ts new file mode 100644 index 00000000..cc11e9d6 --- /dev/null +++ b/lib/service_worker/ui/open.ts @@ -0,0 +1,65 @@ +"use strict"; + +import {getLocalStorage} from "@helpers/storage"; +import {globalTabsActive} from '@context'; +import {focusOnTabAndWindow} from "@background/tabs"; +import * as browser from 'webextension-polyfill'; + +export async function openSidebar() { + await browser.sidebarAction.open(); +} + +export async function openPopup() { + const openInOwnTab : boolean = await getLocalStorage("openInOwnTab", false); + if (openInOwnTab) { + await browser.action.setPopup({popup: "popup.html?popup=true"}); + await browser.action.openPopup(); + await browser.action.setPopup({popup: ""}); + } else { + await browser.action.openPopup(); + } +} + +export async function openAsOwnTab() { + const popup_page = await browser.runtime.getURL("popup.html"); + const tabs = await browser.tabs.query({}); + + let currentTab : browser.Tabs.OnActivatedActiveInfoType; + let previousTab : browser.Tabs.OnActivatedActiveInfoType; + + if (!!globalTabsActive && globalTabsActive.length > 1) { + currentTab = globalTabsActive[globalTabsActive.length - 1]; + previousTab = globalTabsActive[globalTabsActive.length - 2]; + } + + for (var i = 0; i < tabs.length; i++) { + const tab = tabs[i]; + if (tab.url.indexOf("popup.html") > -1 && tab.url.indexOf(popup_page) > -1) { + if (currentTab && currentTab.tabId && tab.id === currentTab.tabId && previousTab && previousTab.tabId) { + await focusOnTabAndWindow(previousTab.tabId, previousTab.windowId); + return; + } else { + await browser.windows.update(tab.windowId, {focused: true}); + await browser.tabs.highlight({windowId: tab.windowId, tabs: tab.index}); + return; + } + } + } + await browser.tabs.create({url: "popup.html"}); +} + +export async function setupPopup() { + + const openInOwnTab = await getLocalStorage("openInOwnTab", false); + + browser.action.onClicked.removeListener(openAsOwnTab); + if (openInOwnTab) { + await browser.action.setPopup({popup: ""}); + browser.action.onClicked.addListener(openAsOwnTab); + } else { + await browser.action.setPopup({popup: "popup.html?popup=true"}); + } + if (browser.sidebarAction) { + await browser.sidebarAction.setPanel({panel: "popup.html?panel=true"}); + } +} \ No newline at end of file From 79cb4b6bf48de0c0ea6ce7accd4d76972fe4879a Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:49:10 +0700 Subject: [PATCH 15/46] Move popup from jsx to tsx --- lib/popup/{popup.jsx => popup.tsx} | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) rename lib/popup/{popup.jsx => popup.tsx} (80%) diff --git a/lib/popup/popup.jsx b/lib/popup/popup.tsx similarity index 80% rename from lib/popup/popup.jsx rename to lib/popup/popup.tsx index efb74183..da040b2a 100644 --- a/lib/popup/popup.jsx +++ b/lib/popup/popup.tsx @@ -1,5 +1,20 @@ "use strict"; +import '@helpers/migrate'; +import {getLocalStorage} from "@helpers/storage"; +import {TabManager} from '@views'; +import * as React from 'react'; +import * as ReactDOM from "react-dom"; + +declare global { + interface Window { + loaded: boolean; + inPopup: boolean; + inPanel: boolean; + optionPage: boolean; + } +} + window.loaded = false; window.inPopup = window.location.search.indexOf("?popup") > -1; window.inPanel = window.location.search.indexOf("?panel") > -1; @@ -31,8 +46,7 @@ async function loadApp() { var root = document.getElementById("root"); if (root != null) { - var _height = document.body.style.height.split("px")[0]; - _height = parseInt(_height) || 0; + var _height = parseInt(document.body.style.height.split("px")[0]) || 0; if (_height < 300) { _height = 400; document.body.style.minHeight = _height + "px"; @@ -64,5 +78,7 @@ async function loadApp() { ReactDOM.render(, document.getElementById("TMP")); } -window.addEventListener("contextmenu", function (e) {e.preventDefault();}); +window.addEventListener("contextmenu", function (e) { + e.preventDefault(); +}); From b9798c02adc442c48b605337d5f2bf04d97f830f Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:53:06 +0700 Subject: [PATCH 16/46] Move TabManager from jsx to tsx --- .../views/{TabManager.jsx => TabManager.tsx} | 892 ++++++++++-------- 1 file changed, 495 insertions(+), 397 deletions(-) rename lib/popup/views/{TabManager.jsx => TabManager.tsx} (69%) diff --git a/lib/popup/views/TabManager.jsx b/lib/popup/views/TabManager.tsx similarity index 69% rename from lib/popup/views/TabManager.jsx rename to lib/popup/views/TabManager.tsx index 89f7566c..589ad656 100644 --- a/lib/popup/views/TabManager.jsx +++ b/lib/popup/views/TabManager.tsx @@ -1,44 +1,36 @@ -"use strict"; +import {getLocalStorage, setLocalStorage} from "@helpers/storage"; +import {debounce, maybePluralize} from "@helpers/utils"; +import {Window, Session, TabOptions, Tab} from "@views"; +import * as React from "react"; +import * as S from "@strings"; +import * as browser from 'webextension-polyfill'; +import {ICommand, ITabManager, ITabManagerState, ISavedSession} from "@types"; -var browser = browser || chrome; +const {setTimeout, clearTimeout} = window -class TabManager extends React.Component { +export class TabManager extends React.Component { constructor(props) { super(props); //this.update(); - if (navigator.userAgent.search("Firefox") > -1) { - } else { - var check = browser.permissions.contains({ permissions: ["system.display"] }); - check.then( - function(result) { - if (result) { - // The extension has the permissions. - } else { - setLocalStorage("hideWindows", false); - this.state.hideWindows = false; - } - }.bind(this) - ); - } - - var layout = "blocks"; - var animations = true; - var windowTitles = true; - var compact = false; - var dark = false; - var tabactions = true; - var badge = true; - var sessionsFeature = false; - var hideWindows = false; - var filterTabs = false; - var tabLimit = 0; - var openInOwnTab = false; - var tabWidth = 800; - var tabHeight = 600; - - var closeTimeout; - var resetTimeout; + let layout = "blocks"; + let animations = true; + let windowTitles = true; + let compact = false; + let dark = false; + let tabactions = true; + let badge = true; + let sessionsFeature = false; + let hideWindows = false; + let filterTabs = false; + let tabLimit = 0; + let openInOwnTab = false; + let tabWidth = 800; + let tabHeight = 600; + + let resetTimeout = -1; + + // var closeTimeout; this.state = { layout: layout, @@ -57,24 +49,27 @@ class TabManager extends React.Component { lastOpenWindow: -1, windows: [], sessions: [], - selection: {}, - lastSelect: false, - hiddenTabs: {}, - tabsbyid: {}, - windowsbyid: {}, - closeTimeout: closeTimeout, + selection: new Set(), + lastSelect: 0, + hiddenTabs: new Set(), + tabsbyid: new Map(), + windowsbyid: new Map(), resetTimeout: resetTimeout, height: 600, hasScrollBar: false, focusUpdates: 0, topText: "", bottomText: "", - lastDirection: false, + lastDirection: "", optionsActive: !!this.props.optionsActive, filterTabs: filterTabs, dupTabs: false, dragFavicon: "", - colorsActive: false + colorsActive: 0, + + tabCount: 0, + hiddenCount: 0, + searchLen: 0 }; this.addWindow = this.addWindow.bind(this); @@ -123,7 +118,11 @@ class TabManager extends React.Component { this.toggleWindowTitles = this.toggleWindowTitles.bind(this); this.update = this.update.bind(this); this.windowTitlesText = this.windowTitlesText.bind(this); - + this.onTabDetached = this.onTabDetached.bind(this); + this.onTabAttached = this.onTabAttached.bind(this); + this.onTabRemoved = this.onTabRemoved.bind(this); + this.onTabCreated = this.onTabCreated.bind(this); + this.dirtyWindow = this.dirtyWindow.bind(this); } UNSAFE_componentWillMount() { this.update(); @@ -164,24 +163,24 @@ class TabManager extends React.Component { if (typeof storage["hideWindows"] === "undefined") storage["hideWindows"] = hideWindows; if (typeof storage["filter-tabs"] === "undefined") storage["filter-tabs"] = filterTabs; - storage["version"] = __VERSION__; + storage["version"] = "__VERSION__"; await browser.storage.local.set(storage); - layout = storage["layout"]; - tabLimit = storage["tabLimit"]; - tabWidth = storage["tabWidth"]; - tabHeight = storage["tabHeight"]; - openInOwnTab = storage["openInOwnTab"]; - animations = storage["animations"]; - windowTitles = storage["windowTitles"]; - compact = storage["compact"]; - dark = storage["dark"]; - tabactions = storage["tabactions"]; - badge = storage["badge"]; - sessionsFeature = storage["sessionsFeature"]; - hideWindows = storage["hideWindows"]; - filterTabs = storage["filter-tabs"]; + layout = storage["layout"] as string; + tabLimit = storage["tabLimit"] as number; + tabWidth = storage["tabWidth"] as number; + tabHeight = storage["tabHeight"] as number; + openInOwnTab = storage["openInOwnTab"] as boolean; + animations = storage["animations"] as boolean; + windowTitles = storage["windowTitles"] as boolean; + compact = storage["compact"] as boolean; + dark = storage["dark"] as boolean; + tabactions = storage["tabactions"] as boolean; + badge = storage["badge"] as boolean; + sessionsFeature = storage["sessionsFeature"] as boolean; + hideWindows = storage["hideWindows"] as boolean; + filterTabs = storage["filter-tabs"] as boolean; if (dark) { document.body.className = "dark"; @@ -207,35 +206,40 @@ class TabManager extends React.Component { }); } - hoverHandler(tab) { + hoverHandler(tab : browser.Tabs.Tab) { this.setState({ topText: tab.title || "" }); - this.setState({ bottomText: tab.url || "" }); + this.setState({ bottomText: tab.url || tab.pendingUrl || "" }); // clearTimeout(this.state.closeTimeout); // this.state.closeTimeout = setTimeout(function () { // window.close(); // }, 100000); - clearTimeout(this.state.resetTimeout); - this.state.resetTimeout = setTimeout( + var _reset_timeout = this.state.resetTimeout; + clearTimeout(_reset_timeout); + _reset_timeout = setTimeout( function() { this.setState({ topText: "", bottomText: "" }); this.update(); }.bind(this), 15000 ); + this.setState({resetTimeout: _reset_timeout}); //this.update(); } - hoverIcon(e) { - if (e && e.nativeEvent) { - e.nativeEvent.preventDefault(); - e.nativeEvent.stopPropagation(); - } - + hoverIcon(e : React.MouseEvent | string) { var text = ""; - if(e && e.target && !!e.target.title) { - text = e.target.title; - } else if (typeof (e) === "string") { + if (typeof (e) === "string") { text = e; + } else { + if (e && e.nativeEvent) { + e.nativeEvent.preventDefault(); + e.nativeEvent.stopPropagation(); + } + + if (e && e.target && !!(e.target as HTMLDivElement).title) { + text = (e.target as HTMLDivElement).title; + } } + var bottom = " "; if (text.indexOf("\n") > -1) { var a = text.split("\n"); @@ -248,13 +252,13 @@ class TabManager extends React.Component { this.forceUpdate(); } render() { - var _this = this; + let _this = this; - // var hiddenCount = this.state.hiddenCount || 0; - var tabCount = this.state.tabCount || 0; + // let hiddenCount = this.state.hiddenCount || 0; + let tabCount = this.state.tabCount; - var haveMin = false; - var haveSess = false; + let haveMin = false; + let haveSess = false; for (let i = this.state.windows.length - 1; i >= 0; i--) { if (this.state.windows[i].state === "minimized") haveMin = true; @@ -265,7 +269,7 @@ class TabManager extends React.Component { // disable session window if we have filtering enabled // and filter active if (haveSess && this.state.filterTabs) { - if (this.state.searchLen > 0 || Object.keys(this.state.hiddenTabs).length > 0) { + if (this.state.searchLen > 0 || this.state.hiddenTabs.size > 0) { haveSess = false; } } @@ -286,7 +290,7 @@ class TabManager extends React.Component { tabIndex={0} > {!this.state.optionsActive &&
- {this.state.windows.map(function(window) { + {this.state.windows.map(function(window : browser.Windows.Window) { if (window.state === "minimized") return; if (!!this.state.colorsActive && this.state.colorsActive !== window.id) return; return ( @@ -367,12 +371,12 @@ class TabManager extends React.Component {
{haveSess - ? this.state.sessions.map(function(window) { + ? this.state.sessions.map(function(window : ISavedSession) { if (!!this.state.colorsActive && this.state.colorsActive !== window.id) return; return ( ); @@ -465,7 +470,7 @@ class TabManager extends React.Component { - +
0 - ? "Close selected tabs\nWill close " + maybePluralize(Object.keys(this.state.selection).length, 'tab') + this.state.selection.size > 0 + ? "Close selected tabs\nWill close " + maybePluralize(this.state.selection.size, 'tab') : "Close current Tab" } onClick={this.deleteTabs} @@ -487,12 +492,12 @@ class TabManager extends React.Component {
0 - ? "Discard selected tabs\nWill discard " + maybePluralize(Object.keys(this.state.selection).length, 'tab') + " - freeing memory" + this.state.selection.size > 0 + ? "Discard selected tabs\nWill discard " + maybePluralize(this.state.selection.size, 'tab') + " - freeing memory" : "Select tabs to discard them and free memory" } style={ - Object.keys(this.state.selection).length > 0 + this.state.selection.size > 0 ? {} : { opacity: 0.25 } } @@ -502,8 +507,8 @@ class TabManager extends React.Component {
0 - ? "Pin selected tabs\nWill pin " + maybePluralize(Object.keys(this.state.selection).length, 'tab') + this.state.selection.size > 0 + ? "Pin selected tabs\nWill pin " + maybePluralize(this.state.selection.size, 'tab') : "Pin current Tab" } onClick={this.pinTabs} @@ -517,7 +522,7 @@ class TabManager extends React.Component { (this.state.searchLen > 0 ? "\n" + (this.state.filterTabs ? "Will reveal " : "Will hide ") + - maybePluralize((Object.keys(this.state.tabsbyid).length - Object.keys(this.state.selection).length), 'tab') + maybePluralize((this.state.tabsbyid.size - this.state.selection.size), 'tab') : "") } onClick={this.toggleFilterMismatchedTabs} @@ -526,8 +531,8 @@ class TabManager extends React.Component {
0 - ? "Move tabs to new window\nWill move " + maybePluralize(Object.keys(this.state.selection).length, 'selected tab') + " to it" + this.state.selection.size > 0 + ? "Move tabs to new window\nWill move " + maybePluralize(this.state.selection.size, 'selected tab') + " to it" : "Open new empty window" } onClick={this.addWindow} @@ -553,17 +558,30 @@ class TabManager extends React.Component { { await this.loadStorage(); - var _this = this; + if (navigator.userAgent.search("Firefox") > -1) { + } else { + let result = await browser.permissions.contains({permissions: ["system.display"]}); + if (!result) { + setLocalStorage("hideWindows", false); + this.setState({ + hideWindows: false + }); + } + } + + let _this = this; - var runUpdate = debounce(this.update, 250); + let runUpdate = debounce(this.update, 250); runUpdate = runUpdate.bind(this); - var runTabUpdate = (tabid, changeinfo, tab) => { + var runTabUpdate = async (tabid, changeinfo, tab) => { + this.dirtyWindow(tab.windowId); + if (!!_this.refs["window" + tab.windowId]) { - var window = _this.refs["window" + tab.windowId]; + var window = _this.refs["window" + tab.windowId] as Window; if (!!window.refs["tab" + tabid]) { - var _tabref = window.refs["tab" + tabid]; - _tabref.checkSettings(); + var _tabref = window.refs["tab" + tabid] as Tab; + await _tabref.checkSettings(); } } } @@ -576,18 +594,27 @@ class TabManager extends React.Component { browser.tabs.onReplaced.addListener(runUpdate); browser.tabs.onDetached.addListener(runUpdate); browser.tabs.onAttached.addListener(runUpdate); + + browser.tabs.onCreated.addListener(this.onTabCreated); + browser.tabs.onDetached.addListener(this.onTabDetached); + browser.tabs.onAttached.addListener(this.onTabAttached); + browser.tabs.onRemoved.addListener(this.onTabRemoved); + browser.tabs.onActivated.addListener(runUpdate); browser.windows.onFocusChanged.addListener(runUpdate); browser.windows.onCreated.addListener(runUpdate); browser.windows.onRemoved.addListener(runUpdate); - browser.runtime.onMessage.addListener(function (request, sender, sendResponse) { + browser.runtime.onMessage.addListener(async function (message, sender, sendResponse) { + const request = message as ICommand; + console.log(request.command); switch (request.command) { - case "refresh_windows": - for (let window_id of request.window_ids) { + case S.refresh_windows: + let window_ids : number[] = request.window_ids; + for (let window_id of window_ids) { if (!_this.refs["window" + window_id]) continue; - _this.refs["window" + window_id].checkSettings(); + (_this.refs["window" + window_id] as Window).checkSettings(); } break; } @@ -596,9 +623,9 @@ class TabManager extends React.Component { browser.storage.onChanged.addListener(this.sessionSync); - this.sessionSync(); + await this.sessionSync(); - this.refs.root.focus(); + (this.refs.root as HTMLElement).focus(); this.focusRoot(); setTimeout(async function() { @@ -620,20 +647,22 @@ class TabManager extends React.Component { // box.focus(); } async sessionSync() { - var values = await getLocalStorage('sessions', {}); - // console.log(values); - var sessions = []; + let values = await getLocalStorage('sessions', {}); + //console.log(values); + let sessions : ISavedSession[] = []; for (let key in values) { let sess = values[key]; if (sess.id && sess.tabs && sess.windowsInfo) { sessions.push(sess); } } - this.state.sessions = sessions; - this.update(); + this.setState({ + sessions: sessions + }); + await this.update(); } focusRoot() { - this.state.focusUpdates++; + this.setState({ focusUpdates: (this.state.focusUpdates + 1) }); setTimeout( function() { if (document.activeElement === document.body) { @@ -645,11 +674,12 @@ class TabManager extends React.Component { 500 ); } - dragFavicon(val) { - if (!val) { + dragFavicon(icon : string) : string { + if (!icon) { return this.state.dragFavicon; } else { - this.state.dragFavicon = val; + this.setState({ dragFavicon: icon }); + return icon; } } rateExtension() { @@ -665,22 +695,44 @@ class TabManager extends React.Component { this.forceUpdate(); } toggleOptions() { - this.state.optionsActive = !this.state.optionsActive; + this.setState({ optionsActive: !this.state.optionsActive }); this.forceUpdate(); } - toggleColors(active, windowId) { - if(!!active) { - this.state.colorsActive = windowId; - }else{ - this.state.colorsActive = false; - } + toggleColors(active : boolean, windowId : number) { + this.setState({ + colorsActive: !!active ? windowId : 0 + }) console.log("colorsActive", active, windowId, this.state.colorsActive); this.forceUpdate(); } + onTabCreated(tab : browser.Tabs.Tab) { + this.dirtyWindow(tab.windowId); + } + + onTabRemoved(tabId : number, removeInfo : browser.Tabs.OnRemovedRemoveInfoType) { + this.dirtyWindow(removeInfo.windowId); + } + + onTabDetached(tabId : number, detachInfo : browser.Tabs.OnDetachedDetachInfoType) { + const windowId = detachInfo.oldWindowId; + this.dirtyWindow(windowId); + } + + onTabAttached(tabId : number, attachInfo: browser.Tabs.OnAttachedAttachInfoType) { + const windowId = attachInfo.newWindowId; + this.dirtyWindow(windowId); + } + + dirtyWindow(windowId : number) { + const window = this.refs['window' + windowId] as Window; + if (!window) return; + window.setState({dirty: true}); + } + async update() { - var windows = await browser.windows.getAll({ populate: true }); - var sort_windows = await getLocalStorage("windowAge", []); + const windows : browser.Windows.Window[] = await browser.windows.getAll({ populate: true }); + const sort_windows = await getLocalStorage("windowAge", []); windows.sort(function(a, b) { var aSort = sort_windows.indexOf(a.id); @@ -692,26 +744,29 @@ class TabManager extends React.Component { return 0; }); - this.state.lastOpenWindow = windows[0].id; - this.state.windows = windows; - this.state.windowsbyid = {}; - this.state.tabsbyid = {}; + this.state.windowsbyid.clear(); + this.state.tabsbyid.clear(); + + this.setState({ + lastOpenWindow: windows[0].id, + windows: windows + }); + let tabCount = 0; for (const window of windows) { - this.state.windowsbyid[window.id] = window; + this.state.windowsbyid.set(window.id, window); for (const tab of window.tabs) { - this.state.tabsbyid[tab.id] = tab; + this.state.tabsbyid.set(tab.id, tab); tabCount++; } } - for (const id in this.state.selection) { - if (!this.state.tabsbyid[id]) { - delete this.state.selection[id]; - this.state.lastSelect = id; + for (let id of this.state.selection.keys()) { + if (!this.state.tabsbyid.has(id)) { + this.state.selection.delete(id); + this.setState({lastSelect: id}); } } - this.state.tabCount = tabCount; this.setState({ tabCount: tabCount }); @@ -719,30 +774,30 @@ class TabManager extends React.Component { // this.forceUpdate(); } async deleteTabs() { - var _this = this; - var tabs = Object.keys(this.state.selection).map(function(id) { - return _this.state.tabsbyid[id]; + const _this = this; + const tabs: browser.Tabs.Tab[] = [...this.state.selection.keys()].map(function(id) { + return _this.state.tabsbyid.get(id); }); if (tabs.length) { - browser.runtime.sendMessage({command: "close_tabs", tabs: tabs}); + browser.runtime.sendMessage({command: S.close_tabs, tabs: tabs}); } else { - var t = await browser.tabs.query({ currentWindow: true, active: true }); + const t = await browser.tabs.query({ currentWindow: true, active: true }); if (t && t.length > 0) { await browser.tabs.remove(t[0].id); } } this.forceUpdate(); } - deleteTab(tabId) { + deleteTab(tabId : number) { browser.tabs.remove(tabId); } async discardTabs() { - var _this = this; - var tabs = Object.keys(this.state.selection).map(function(id) { - return _this.state.tabsbyid[id]; + const _this = this; + const tabs : browser.Tabs.Tab[] = [...this.state.selection.keys()].map(function(id) { + return _this.state.tabsbyid.get(id); }); if (tabs.length) { - browser.runtime.sendMessage({command: "discard_tabs", tabs: tabs}); + browser.runtime.sendMessage({command: S.discard_tabs, tabs: tabs}); } this.clearSelection(); } @@ -750,30 +805,43 @@ class TabManager extends React.Component { browser.tabs.discard(tabId); } async addWindow() { - var _this = this; - var count = Object.keys(this.state.selection).length; - var tabs = Object.keys(this.state.selection).map(function(id) { - return _this.state.tabsbyid[id]; + const _this = this; + const count = this.state.selection.size; + const tabs : browser.Tabs.Tab[] = [...this.state.selection.keys()].map(function(id) { + return _this.state.tabsbyid.get(id); + }); + + const incognito_tabs = tabs.filter(function(tab) { + return tab.incognito; + }); + + const normal_tabs = tabs.filter(function(tab) { + return !tab.incognito; }); if (count === 0) { await browser.windows.create({}); } else if (count === 1) { if (navigator.userAgent.search("Firefox") > -1) { - browser.runtime.sendMessage({command: "focus_on_tab_and_window_delayed", tab: tabs[0]}); + await browser.runtime.sendMessage({command: S.focus_on_tab_and_window_delayed, tab: tabs[0]}); }else{ - browser.runtime.sendMessage({command: "focus_on_tab_and_window", tab: tabs[0]}); + await browser.runtime.sendMessage({command: S.focus_on_tab_and_window, tab: tabs[0]}); } } else { - browser.runtime.sendMessage({command: "create_window_with_tabs", tabs: tabs}); + if (normal_tabs.length > 0) { + await browser.runtime.sendMessage({command: S.create_window_with_tabs, tabs: normal_tabs, incognito: false}); + } + if (incognito_tabs.length > 0) { + await browser.runtime.sendMessage({command: S.create_window_with_tabs, tabs: incognito_tabs, incognito: true}); + } } if (!!window.inPopup) window.close(); } async pinTabs() { - var _this = this; - var tabs = Object.keys(this.state.selection) + const _this = this; + const tabs : browser.Tabs.Tab[] = [...this.state.selection.keys()] .map(function(id) { - return _this.state.tabsbyid[id]; + return _this.state.tabsbyid.get(id); }) .sort(function(a, b) { return a.index - b.index; @@ -784,31 +852,38 @@ class TabManager extends React.Component { await browser.tabs.update(tabs[i].id, { pinned: !tabs[0].pinned }); } } else { - var t = await browser.tabs.query({ currentWindow: true, active: true }); + const t = await browser.tabs.query({ currentWindow: true, active: true }); if (t && t.length > 0) { await browser.tabs.update(t[0].id, { pinned: !t[0].pinned }); } } } highlightDuplicates(e) { - this.state.selection = {}; - this.state.hiddenTabs = {}; - this.state.searchLen = 0; - this.state.dupTabs = !this.state.dupTabs; - this.refs.searchbox.value = ""; - if (!this.state.dupTabs) { - this.state.hiddenCount = 0; + this.state.selection.clear(); + this.state.hiddenTabs.clear(); + + let searchLen = 0; + const dupTabs = !this.state.dupTabs; + + (this.refs.searchbox as HTMLInputElement).value = ""; + + if (!dupTabs) { + this.setState({ + hiddenCount: 0, + dupTabs: dupTabs, + searchLen: searchLen + }); this.forceUpdate(); return; } - var hiddenCount = this.state.hiddenCount || 0; - var idList = this.state.tabsbyid; - var dup = []; - for (const id in idList) { - var tab = this.state.tabsbyid[id]; - for (const id2 in idList) { + let hiddenCount = this.state.hiddenCount || 0; + const idList : number[] = [...this.state.tabsbyid.keys()]; + const dup = []; + for (const id of idList) { + var tab = this.state.tabsbyid.get(id); + for (const id2 of idList) { if (id === id2) continue; - var tab2 = this.state.tabsbyid[id2]; + var tab2 = this.state.tabsbyid.get(id2); if (tab.url === tab2.url) { dup.push(id); break; @@ -816,19 +891,23 @@ class TabManager extends React.Component { } } for (const dupItem of dup) { - this.state.searchLen++; - hiddenCount -= this.state.hiddenTabs[dupItem] || 0; - this.state.selection[dupItem] = true; - delete this.state.hiddenTabs[dupItem]; - this.state.lastSelect = dupItem; + searchLen++; + hiddenCount -= this.state.hiddenTabs.has(dupItem) ? 1 : 0; + this.state.selection.add(dupItem); + this.state.hiddenTabs.delete(dupItem); + this.setState({ + lastSelect: dupItem + }); } - for (const tab_id in idList) { - // var tab = this.state.tabsbyid[tab_id]; + for (const tab_id of idList) { + // var tab = this.state.tabsbyid.get(tab_id); if (dup.indexOf(tab_id) === -1) { - hiddenCount += 1 - (this.state.hiddenTabs[tab_id] || 0); - this.state.hiddenTabs[tab_id] = true; - delete this.state.selection[tab_id]; - this.state.lastSelect = tab_id; + hiddenCount += 1 - (this.state.hiddenTabs.has(tab_id) ? 1 : 0); + this.state.hiddenTabs.add(tab_id); + this.state.selection.delete(tab_id); + this.setState({ + lastSelect: tab_id + }); } } if (dup.length === 0) { @@ -842,16 +921,23 @@ class TabManager extends React.Component { bottomText: "Press enter to move them to a new window" }); } - this.state.hiddenCount = hiddenCount; + this.setState({ + hiddenCount: hiddenCount + }); + this.setState({ + searchLen: searchLen, + dupTabs: dupTabs + }); + this.forceUpdate(); } search(e) { - var hiddenCount = this.state.hiddenCount || 0; - var searchQuery = e.target.value || ""; - var searchLen = searchQuery.length; + let hiddenCount = this.state.hiddenCount || 0; + const searchQuery = e.target.value || ""; + const searchLen = searchQuery.length; - var searchType = "normal"; - var searchTerms = []; + let searchType = "normal"; + let searchTerms = []; if(searchQuery.indexOf(" ") === -1) { searchType = "normal"; }else if(searchQuery.indexOf(" OR ") > -1) { @@ -866,29 +952,29 @@ class TabManager extends React.Component { } if (!searchLen) { - this.state.selection = {}; - this.state.hiddenTabs = {}; + this.state.selection.clear(); + this.state.hiddenTabs.clear(); hiddenCount = 0; } else { - var idList; - var lastSearchLen = this.state.searchLen; - idList = this.state.tabsbyid; + let idList : number[]; + const lastSearchLen = this.state.searchLen; + idList = [ ...this.state.tabsbyid.keys() ]; if(searchType === "normal") { if (!lastSearchLen) { - idList = this.state.tabsbyid; + idList = [ ...this.state.tabsbyid.keys() ]; } else if (lastSearchLen > searchLen) { - idList = this.state.hiddenTabs; + idList = [ ...this.state.hiddenTabs.keys() ]; } else if (lastSearchLen < searchLen) { - idList = this.state.selection; + idList = [ ...this.state.selection.keys() ]; } } - for (const id in idList) { - var tab = this.state.tabsbyid[id]; - var tabSearchTerm; + for (const id of idList) { + const tab = this.state.tabsbyid.get(id); + let tabSearchTerm; if (!!tab.title) tabSearchTerm = tab.title; if (!!tab.url) tabSearchTerm += " " + tab.url; tabSearchTerm = tabSearchTerm.toLowerCase(); - var match = false; + let match = false; if(searchType === "normal") { match = (tabSearchTerm.indexOf(e.target.value.toLowerCase()) >= 0); }else if(searchType === "OR") { @@ -900,7 +986,7 @@ class TabManager extends React.Component { } } }else if(searchType === "AND") { - var andMatch = true; + let andMatch = true; for (let searchAND of searchTerms) { searchAND = searchAND.trim().toLowerCase(); if(tabSearchTerm.indexOf(searchAND) >= 0) { @@ -913,21 +999,26 @@ class TabManager extends React.Component { match = andMatch; } if (match) { - hiddenCount -= this.state.hiddenTabs[id] || 0; - this.state.selection[id] = true; - delete this.state.hiddenTabs[id]; - this.state.lastSelect = id; + hiddenCount -= this.state.hiddenTabs.has(id) ? 1 : 0; + this.state.selection.add(id); + this.state.hiddenTabs.delete(id); } else { - hiddenCount += 1 - (this.state.hiddenTabs[id] || 0); - this.state.hiddenTabs[id] = true; - delete this.state.selection[id]; - this.state.lastSelect = id; + hiddenCount += 1 - (this.state.hiddenTabs.has(id) ? 1 : 0); + this.state.hiddenTabs.add(id); + this.state.selection.delete(id); } + this.setState({ + lastSelect: id + }); } } - this.state.hiddenCount = hiddenCount; - this.state.searchLen = searchLen; - var matches = Object.keys(this.state.selection).length; + + this.setState({ + hiddenCount: hiddenCount, + searchLen: searchLen + }) + + const matches = this.state.selection.size; // var matchtext = ""; if (matches === 0 && searchLen > 0) { this.setState({ @@ -941,21 +1032,21 @@ class TabManager extends React.Component { }); } else if (matches > 1) { this.setState({ - topText: Object.keys(this.state.selection).length + " matches for '" + searchQuery + "'", + topText: this.state.selection.size + " matches for '" + searchQuery + "'", bottomText: "Press enter to move them to a new window" }); } else if (matches === 1) { this.setState({ - topText: Object.keys(this.state.selection).length + " match for '" + searchQuery + "'", + topText: this.state.selection.size + " match for '" + searchQuery + "'", bottomText: "Press enter to switch to the tab" }); } this.forceUpdate(); } clearSelection() { - this.state.selection = {}; + this.state.selection.clear(); this.setState({ - lastSelect: false + lastSelect: 0 }); } checkKey(e) { @@ -963,14 +1054,18 @@ class TabManager extends React.Component { if (e.keyCode === 13) this.addWindow(); // escape key if (e.keyCode === 27) { - if(this.state.searchLen > 0 || Object.keys(this.state.selection).length > 0) { + if(this.state.searchLen > 0 || this.state.selection.size > 0) { // stop popup from closing if we have search text or selection active e.nativeEvent.preventDefault(); e.nativeEvent.stopPropagation(); } - this.state.hiddenTabs = {}; - this.state.searchLen = 0; - this.refs.searchbox.value = ""; + + this.state.hiddenTabs.clear(); + this.setState({ + searchLen: 0 + }); + + (this.refs.searchbox as HTMLInputElement).value = ""; this.clearSelection(); } // any typed keys @@ -984,8 +1079,11 @@ class TabManager extends React.Component { e.keyCode === 32 ) { if (document.activeElement !== this.refs.searchbox) { - if (document.activeElement.type !== "text" && document.activeElement.type !== "input") { - this.refs.searchbox.focus(); + var activeInputElement = document.activeElement as HTMLInputElement; + console.log(activeInputElement); + console.log(this.refs.searchbox); + if (activeInputElement.type !== "text" && activeInputElement.type !== "input") { + (this.refs.searchbox as HTMLElement)?.focus(); } } } @@ -998,10 +1096,12 @@ class TabManager extends React.Component { */ if (e.keyCode >= 37 && e.keyCode <= 40) { if (document.activeElement !== this.refs.windowcontainer && document.activeElement !== this.refs.searchbox) { - this.refs.windowcontainer.focus(); + console.log(activeInputElement); + console.log(this.refs.windowcontainer); + (this.refs.windowcontainer as HTMLElement)?.focus(); } - if (document.activeElement !== this.refs.searchbox || !this.refs.searchbox.value) { + if (document.activeElement !== this.refs.searchbox || !((this.refs.searchbox as HTMLInputElement).value)) { let goLeft = e.keyCode === 37; let goRight = e.keyCode === 39; let goUp = e.keyCode === 38; @@ -1018,45 +1118,49 @@ class TabManager extends React.Component { } const altKey = e.nativeEvent.metaKey || e.nativeEvent.altKey || e.nativeEvent.shiftKey || e.nativeEvent.ctrlKey; if (goLeft || goRight) { - let selectedTabs = Object.keys(this.state.selection); + let selectedTabs = [...this.state.selection.keys()]; if (!altKey && selectedTabs.length > 1) { } else { let found = false; let selectedNext = false; - let selectedTab = false; - let first = false; - let prev = false; - let last = false; + let selectedTab = 0; + let first = 0; + let prev = 0; + let last = 0; if (selectedTabs.length === 1) { selectedTab = selectedTabs[0]; // console.log("one tab", selectedTab); } else if (selectedTabs.length > 1) { - if (this.state.lastSelect) { + if (!!this.state.lastSelect) { selectedTab = this.state.lastSelect; // console.log("more tabs, last", selectedTab); } else { selectedTab = selectedTabs[0]; // console.log("more tabs, first", selectedTab); } - } else if (selectedTabs.length === 0 && this.state.lastSelect) { + } else if (selectedTabs.length === 0 && !!this.state.lastSelect) { selectedTab = this.state.lastSelect; // console.log("no tabs, last", selectedTab); } - if (this.state.lastDirection) { + if (!!this.state.lastDirection) { if (goRight && this.state.lastDirection === "goRight") { } else if (goLeft && this.state.lastDirection === "goLeft") { } else if (selectedTabs.length > 1) { // console.log("turned back, last", this.state.lastSelect, selectedTab); this.select(this.state.lastSelect); - this.state.lastDirection = false; + this.setState({ + lastDirection: "" + }); found = true; } else { - this.state.lastDirection = false; + this.setState({ + lastDirection: "" + }); } } if (!this.state.lastDirection) { - if (goRight) this.state.lastDirection = "goRight"; - if (goLeft) this.state.lastDirection = "goLeft"; + if (goRight) this.setState({ lastDirection: "goRight" }); + if (goLeft) this.setState({ lastDirection: "goLeft" }); } for (const _w of this.state.windows) { if (found) break; @@ -1065,7 +1169,7 @@ class TabManager extends React.Component { last = _t.id; if (!first) first = _t.id; if (!selectedTab) { - if (!altKey) this.state.selection = {}; + if (!altKey) this.state.selection.clear(); this.select(_t.id); found = true; break; @@ -1073,14 +1177,14 @@ class TabManager extends React.Component { // console.log("select next one", selectedNext); if (goRight) { selectedNext = true; - } else if (prev) { - if (!altKey) this.state.selection = {}; + } else if (!!prev) { + if (!altKey) this.state.selection.clear(); this.select(prev); found = true; break; } } else if (selectedNext) { - if (!altKey) this.state.selection = {}; + if (!altKey) this.state.selection.clear(); this.select(_t.id); found = true; break; @@ -1097,21 +1201,21 @@ class TabManager extends React.Component { last = _t.id; if (!first) first = _t.id; if (!selectedTab) { - if (!altKey) this.state.selection = {}; + if (!altKey) this.state.selection.clear(); this.select(_t.id); found = true; break; } else if (selectedTab === _t.id) { if (goRight) { selectedNext = true; - } else if (prev) { - if (!altKey) this.state.selection = {}; + } else if (!!prev) { + if (!altKey) this.state.selection.clear(); this.select(prev); found = true; break; } } else if (selectedNext) { - if (!altKey) this.state.selection = {}; + if (!altKey) this.state.selection.clear(); this.select(_t.id); found = true; break; @@ -1121,28 +1225,28 @@ class TabManager extends React.Component { } } } - if (!found && goRight && first) { - if (!altKey) this.state.selection = {}; + if (!found && goRight && !!first) { + if (!altKey) this.state.selection.clear(); this.select(first); found = true; } - if (!found && goLeft && last) { - if (!altKey) this.state.selection = {}; + if (!found && goLeft && !!last) { + if (!altKey) this.state.selection.clear(); this.select(last); found = true; } } } if (goUp || goDown) { - let selectedTabs = Object.keys(this.state.selection); + let selectedTabs = [...this.state.selection.keys()]; if (selectedTabs.length > 1) { } else { let found = false; let selectedNext = false; let selectedTab = -1; - let first = false; - let prev = false; - let last = false; + let first = 0; + let prev = 0; + let last = 0; let tabPosition = -1; let i = -1; if (selectedTabs.length === 1) { @@ -1168,7 +1272,7 @@ class TabManager extends React.Component { // console.log("select next window ", selectedNext, tabPosition); selectedNext = true; break; - } else if (prev) { + } else if (!!prev) { // console.log("select prev window ", prev, tabPosition); this.selectWindowTab(prev, tabPosition); found = true; @@ -1205,7 +1309,7 @@ class TabManager extends React.Component { // console.log("select next window ", selectedNext, tabPosition); selectedNext = true; break; - } else if (prev) { + } else if (!!prev) { // console.log("select prev window ", prev, tabPosition); this.selectWindowTab(prev, tabPosition); found = true; @@ -1223,16 +1327,16 @@ class TabManager extends React.Component { } } // console.log(found, goDown, first); - if (!found && goDown && first) { + if (!found && goDown && !!first) { // console.log("go first", first); - this.state.selection = {}; + this.state.selection.clear(); this.selectWindowTab(first, tabPosition); found = true; } // console.log(found, goUp, last); - if (!found && goUp && last) { + if (!found && goUp && !!last) { // console.log("go last", last); - this.state.selection = {}; + this.state.selection.clear(); this.selectWindowTab(last, tabPosition); found = true; } @@ -1242,8 +1346,8 @@ class TabManager extends React.Component { } // page up / page down if (e.keyCode === 33 || e.keyCode === 34) { - if (document.activeElement !== this.refs.windowcontainer) { - this.refs.windowcontainer.focus(); + if (document.activeElement != this.refs.windowcontainer) { + (this.refs.windowcontainer as HTMLElement).focus(); } } } @@ -1255,13 +1359,13 @@ class TabManager extends React.Component { for (let _t of _w.tabs) { i++; if ((_w.tabs.length >= tabPosition && tabPosition === i) || (_w.tabs.length < tabPosition && _w.tabs.length === i)) { - this.state.selection = {}; + this.state.selection.clear(); this.select(_t.id); } } } } - scrollTo(what, id) { + scrollTo(what : string, id : number) { var els = document.getElementById(what + "-" + id); if (!!els) { if (!this.elVisible(els)) { @@ -1274,17 +1378,8 @@ class TabManager extends React.Component { if (layout && typeof (layout) === "string") { newLayout = layout; } else { - if (this.state.layout === "blocks") { - newLayout = this.state.layout = "blocks-big"; - } else if (this.state.layout === "blocks-big") { - newLayout = this.state.layout = "horizontal"; - } else if (this.state.layout === "horizontal") { - newLayout = this.state.layout = "vertical"; - } else { - newLayout = this.state.layout = "blocks"; - } + newLayout = this.nextlayout(); } - this.state.layout = newLayout; await setLocalStorage("layout", newLayout); this.setState({ @@ -1296,46 +1391,49 @@ class TabManager extends React.Component { this.forceUpdate(); } nextlayout() { - if (this.state.layout === "blocks") { - return "blocks-big"; - } else if (this.state.layout === "blocks-big") { - return "horizontal"; - } else if (this.state.layout === "horizontal") { - return "vertical"; - } else { - return "blocks"; + switch (this.state.layout) { + case "blocks": + return "blocks-big"; + case "blocks-big": + return "horizontal"; + case "horizontal": + return "vertical"; + default: + return "blocks"; } } readablelayout(layout) { - if (layout === "blocks") { - return "Block"; - } else if (layout === "blocks-big") { - return "Big Block"; - } else if (layout === "horizontal") { - return "Horizontal"; - } else { - return "Vertical"; + switch (layout) { + case "blocks": + return "Block"; + case "blocks-big": + return "Big Block"; + case "horizontal": + return "Horizontal"; + default: + return "Vertical"; } } - select(id) { - if (this.state.selection[id]) { - delete this.state.selection[id]; + select(id : number) { + if (this.state.selection.has(id)) { + this.state.selection.delete(id); this.setState({ lastSelect: id }); } else { - this.state.selection[id] = true; + this.state.selection.add(id); this.setState({ lastSelect: id }); } this.scrollTo('tab', id); - var tab = this.state.tabsbyid[id]; - if(this.refs['window' + tab.windowId] && this.refs['window' + tab.windowId].refs['tab' + id]) { - this.refs['window' + tab.windowId].refs['tab' + id].resolveFavIconUrl(); + var tab = this.state.tabsbyid.get(id); + if(!!this.refs['window' + tab.windowId] && !!(this.refs['window' + tab.windowId] as Window).refs['tab' + id]) { + ((this.refs['window' + tab.windowId] as Window).refs['tab' + id] as Tab).resolveFavIconUrl(); } - var selected = Object.keys(this.state.selection).length; + console.log(this.state.selection); + var selected = this.state.selection.size; if (selected === 0) { this.setState({ topText: "No tabs selected", @@ -1353,27 +1451,27 @@ class TabManager extends React.Component { }); } } - selectTo(id, tabs) { - var activate = false; - var lastSelect = this.state.lastSelect; + selectTo(id : number, tabs : browser.Tabs.Tab[]) { + let activate = false; + const lastSelect = this.state.lastSelect; if (id === lastSelect) { this.select(id); return; } if (!!lastSelect) { - if (this.state.selection[lastSelect]) { + if (this.state.selection.has(lastSelect)) { activate = true; } } else { - if (this.state.selection[id]) { + if (this.state.selection.has(id)) { activate = false; } else { activate = true; } } - var rangeIndex1; - var rangeIndex2; + let rangeIndex1 : number; + let rangeIndex2 : number; for (let i = 0; i < tabs.length; i++) { if (tabs[i].id === id) { rangeIndex1 = i; @@ -1387,11 +1485,11 @@ class TabManager extends React.Component { return; } if (!rangeIndex2) { - var neighbours = []; + const neighbours = []; for (let i = 0; i < tabs.length; i++) { - var tabId = tabs[i].id; + const tabId = tabs[i].id; if (tabId !== id) { - if (this.state.selection[tabId]) { + if (this.state.selection.has(tabId)) { neighbours.push(tabId); } } @@ -1446,26 +1544,26 @@ class TabManager extends React.Component { lastSelect: tabs[rangeIndex2].id }); if (rangeIndex2 < rangeIndex1) { - var r1 = rangeIndex2; - var r2 = rangeIndex1; + let r1 = rangeIndex2; + let r2 = rangeIndex1; rangeIndex1 = r1; rangeIndex2 = r2; } for (let i = 0; i < tabs.length; i++) { if (i >= rangeIndex1 && i <= rangeIndex2) { - var _tab_id = tabs[i].id; + const _tab_id = tabs[i].id; if (activate) { - this.state.selection[_tab_id] = true; + this.state.selection.add(_tab_id); } else { - delete this.state.selection[_tab_id]; + this.state.selection.delete(_tab_id); } } } this.scrollTo('tab', this.state.lastSelect); - var selected = Object.keys(this.state.selection).length; + const selected = this.state.selection.size; if (selected === 0) { this.setState({ topText: "No tabs selected", @@ -1484,47 +1582,47 @@ class TabManager extends React.Component { } this.forceUpdate(); } - drag(e, id) { - if (!this.state.selection[id]) { - this.state.selection = {}; - this.state.selection[id] = true; - this.state.lastSelect = id; + drag(e : React.DragEvent, id : number) { + if (!this.state.selection.has(id)) { + this.state.selection.add(id); + this.setState({ + lastSelect: id + }); } this.forceUpdate(); } - async drop(id, before) { - var _this5 = this; - var tab = this.state.tabsbyid[id]; - var tabs = Object.keys(this.state.selection).map(function(id) { - return _this5.state.tabsbyid[id]; + async drop(id : number, before : boolean) { + var _this = this; + var tab : browser.Tabs.Tab = this.state.tabsbyid.get(id); + var tabs : browser.Tabs.Tab[] = [...this.state.selection.keys()].map(function(id) { + return _this.state.tabsbyid.get(id); }); var index = tab.index + (before ? 0 : 1); for (let i = 0; i < tabs.length; i++) { - var t = tabs[i]; + const t : browser.Tabs.Tab = tabs[i]; await browser.tabs.move(t.id, { windowId: tab.windowId, index: index }); await browser.tabs.update(t.id, { pinned: t.pinned }); } - this.setState({ - selection: {} - }); + this.state.selection.clear(); this.update(); } - async dropWindow(windowId) { - var _this6 = this; - var tabs = Object.keys(this.state.selection).map(function(id) { - return _this6.state.tabsbyid[id]; + async dropWindow(windowId : number) { + var _this = this; + var tabs : browser.Tabs.Tab[] = [...this.state.selection.keys()].map(function(id) { + return _this.state.tabsbyid.get(id); }); - browser.runtime.sendMessage({command: "move_tabs_to_window", window_id: windowId, tabs: tabs}); + browser.runtime.sendMessage({command: S.move_tabs_to_window, window_id: windowId, tabs: tabs}); + this.state.selection.clear(); + } + async changeTabLimit(e : React.ChangeEvent) { + var _tab_limit = parseInt(e.target.value); this.setState({ - selection: {} + tabLimit: _tab_limit }); - } - async changeTabLimit(e) { - this.state.tabLimit = e.target.value; - await setLocalStorage("tabLimit", this.state.tabLimit); + await setLocalStorage("tabLimit", _tab_limit); this.tabLimitText(); this.forceUpdate(); } @@ -1533,10 +1631,13 @@ class TabManager extends React.Component { bottomText: "Limit the number of tabs per window. Will move new tabs into a new window instead. 0 to turn off" }); } - async changeTabWidth(e) { - this.state.tabWidth = e.target.value; - await setLocalStorage("tabWidth", this.state.tabWidth); - document.body.style.width = this.state.tabWidth + "px"; + async changeTabWidth(e : React.ChangeEvent) { + var _tab_width = parseInt(e.target.value); + this.setState({ + tabWidth: _tab_width + }); + await setLocalStorage("tabWidth", _tab_width); + document.body.style.width = _tab_width + "px"; this.tabWidthText(); this.forceUpdate(); } @@ -1545,10 +1646,13 @@ class TabManager extends React.Component { bottomText: "Change the width of this window. 800 by default." }); } - async changeTabHeight(e) { - this.state.tabHeight = e.target.value; - await setLocalStorage("tabHeight", this.state.tabHeight); - document.body.style.height = this.state.tabHeight + "px"; + async changeTabHeight(e : React.ChangeEvent) { + var _tab_height = parseInt(e.target.value); + this.setState({ + tabHeight: _tab_height + }); + await setLocalStorage("tabHeight", _tab_height); + document.body.style.height = _tab_height + "px"; this.tabHeightText(); this.forceUpdate(); } @@ -1558,8 +1662,9 @@ class TabManager extends React.Component { }); } async toggleAnimations() { - this.state.animations = !this.state.animations; - await setLocalStorage("animations", this.state.animations); + var _animations = !this.state.animations; + this.setState({ animations: _animations }); + await setLocalStorage("animations", _animations); this.animationsText(); this.forceUpdate(); } @@ -1569,8 +1674,9 @@ class TabManager extends React.Component { }); } async toggleWindowTitles() { - this.state.windowTitles = !this.state.windowTitles; - await setLocalStorage("windowTitles", this.state.windowTitles); + var _window_titles = !this.state.windowTitles; + this.setState({windowTitles: _window_titles}); + await setLocalStorage("windowTitles", _window_titles); this.windowTitlesText(); this.forceUpdate(); } @@ -1580,8 +1686,9 @@ class TabManager extends React.Component { }); } async toggleCompact() { - this.state.compact = !this.state.compact; - await setLocalStorage("compact", this.state.compact); + var _compact = !this.state.compact; + this.setState({compact: _compact}); + await setLocalStorage("compact", _compact); this.compactText(); this.forceUpdate(); } @@ -1591,10 +1698,12 @@ class TabManager extends React.Component { }); } async toggleDark() { - this.state.dark = !this.state.dark; - await setLocalStorage("dark", this.state.dark); + var _dark = !this.state.dark; + this.setState({dark: _dark}); + await setLocalStorage("dark", _dark); + this.darkText(); - if (this.state.dark) { + if (_dark) { document.body.className = "dark"; document.documentElement.className = "dark"; } else { @@ -1609,8 +1718,9 @@ class TabManager extends React.Component { }); } async toggleTabActions() { - this.state.tabactions = !this.state.tabactions; - await setLocalStorage("tabactions", this.state.tabactions); + var _tabactions = !this.state.tabactions; + this.setState({tabactions: _tabactions}); + await setLocalStorage("tabactions", _tabactions); this.tabActionsText(); this.forceUpdate(); } @@ -1620,10 +1730,11 @@ class TabManager extends React.Component { }); } async toggleBadge() { - this.state.badge = !this.state.badge; - await setLocalStorage("badge", this.state.badge); + var _badge = !this.state.badge; + this.setState({badge: _badge}); + await setLocalStorage("badge", _badge); this.badgeText(); - browser.runtime.sendMessage({command: "update_tab_count"}); + browser.runtime.sendMessage({command: S.update_tab_count}); this.forceUpdate(); } badgeText() { @@ -1632,10 +1743,11 @@ class TabManager extends React.Component { }); } async toggleOpenInOwnTab() { - this.state.openInOwnTab = !this.state.openInOwnTab; - await setLocalStorage("openInOwnTab", this.state.openInOwnTab); + var _openInOwnTab = !this.state.openInOwnTab; + this.setState({openInOwnTab: _openInOwnTab}); + await setLocalStorage("openInOwnTab", _openInOwnTab); this.openInOwnTabText(); - browser.runtime.sendMessage({ command: "reload_popup_controls" }); + browser.runtime.sendMessage({ command: S.reload_popup_controls }); this.forceUpdate(); } openInOwnTabText() { @@ -1644,8 +1756,9 @@ class TabManager extends React.Component { }); } async toggleSessions() { - this.state.sessionsFeature = !this.state.sessionsFeature; - await setLocalStorage("sessionsFeature", this.state.sessionsFeature); + var _sessionsFeature = !this.state.sessionsFeature; + this.setState({sessionsFeature: _sessionsFeature}); + await setLocalStorage("sessionsFeature", _sessionsFeature); this.sessionsText(); this.forceUpdate(); } @@ -1684,7 +1797,7 @@ class TabManager extends React.Component { bottomText: "Allows you to export your saved windows to an external backup" }); } - importSessions(evt) { + importSessions(evt : React.ChangeEvent) { if (navigator.userAgent.search("Firefox") > -1) { if(window.inPopup) { window.alert("Due to a Firefox bug session import does not work in the popup. Please use the options screen or open Tab Manager Plus in its' own tab"); @@ -1706,7 +1819,7 @@ class TabManager extends React.Component { //console.log('FILE CONTENT', event.target.result); var backupFile; try { - backupFile = JSON.parse(event.target.result); + backupFile = JSON.parse(event.target.result.toString()); } catch (err) { console.error(err); window.alert(err); @@ -1751,18 +1864,22 @@ class TabManager extends React.Component { } async toggleHide() { + var _hide_windows = this.state.hideWindows; if (navigator.userAgent.search("Firefox") > -1) { - this.state.hideWindows = false; + _hide_windows = false; } else { - var granted = await browser.permissions.request({ permissions: ["system.display"] }); + var granted = await chrome.permissions.request({ permissions: ["system.display"] }); if (granted) { - this.state.hideWindows = !this.state.hideWindows; + _hide_windows = !_hide_windows; } else { - this.state.hideWindows = false; + _hide_windows = false; } } - await setLocalStorage("hideWindows", this.state.hideWindows); + await setLocalStorage("hideWindows", _hide_windows); + this.setState({ + hideWindows: _hide_windows + }); this.hideText(); this.forceUpdate(); } @@ -1772,8 +1889,11 @@ class TabManager extends React.Component { }); } async toggleFilterMismatchedTabs() { - this.state.filterTabs = !this.state.filterTabs; - await setLocalStorage("filter-tabs", this.state.filterTabs); + var _filter_tabs = !this.state.filterTabs; + this.setState({ + filterTabs: _filter_tabs + }); + await setLocalStorage("filter-tabs", _filter_tabs); this.forceUpdate(); } getTip() { @@ -1790,16 +1910,13 @@ class TabManager extends React.Component { return "Tip: " + tips[Math.floor(Math.random() * tips.length)]; } - isInViewport(element, ofElement) { - var rect = element.getBoundingClientRect(); - return rect.top >= 0 && rect.left >= 0 && rect.bottom <= ofElement.height && rect.right <= ofElement.width; - } - elVisible(elem) { + elVisible(elem : HTMLElement) { if (!(elem instanceof Element)) throw Error("DomUtil: elem is not an element."); var style = getComputedStyle(elem); if (style.display === "none") return false; if (style.visibility !== "visible") return false; - if (style.opacity < 0.1) return false; + let _opacity : number = parseFloat(style.opacity); + if (_opacity < 0.1) return false; if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height + elem.getBoundingClientRect().width === 0) { return false; } @@ -1812,29 +1929,10 @@ class TabManager extends React.Component { if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false; if (elemCenter.y < 0) return false; if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false; - var pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y); + var pointContainer : ParentNode = document.elementFromPoint(elemCenter.x, elemCenter.y); do { if (pointContainer === elem) return true; - } while ((pointContainer = pointContainer.parentNode)); + } while ((pointContainer = pointContainer.parentNode)) return false; } -} - -function debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, - args = arguments; - var later = function later() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} - -const maybePluralize = (count, noun, suffix = 's') => - `${count} ${noun}${count !== 1 ? suffix : ''}`; \ No newline at end of file +} \ No newline at end of file From e41b0edf160c7f47c291a606eabc54e56d6271ab Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 11:58:38 +0700 Subject: [PATCH 17/46] Move Window from jsx to tsx Fix pending urls not being read when calculating window title Fix window title not updating after tabs loaded Fix IPs being shown weird in window title/name Fix title being based on whole url, instead of hostname while tab was loading Try to find nicer window title Group by top-Ips --- lib/popup/views/{Window.jsx => Window.tsx} | 452 ++++++++++++--------- 1 file changed, 267 insertions(+), 185 deletions(-) rename lib/popup/views/{Window.jsx => Window.tsx} (67%) diff --git a/lib/popup/views/Window.jsx b/lib/popup/views/Window.tsx similarity index 67% rename from lib/popup/views/Window.jsx rename to lib/popup/views/Window.tsx index 09ebbe78..433d3dd8 100644 --- a/lib/popup/views/Window.jsx +++ b/lib/popup/views/Window.tsx @@ -1,7 +1,14 @@ "use strict"; -export class Window extends React.Component { - constructor(props) { +import {getLocalStorage, setLocalStorage, getLocalStorageMap} from "@helpers/storage"; +import {Tab} from "@views"; +import * as S from "@strings"; +import * as React from "react"; +import * as browser from 'webextension-polyfill'; +import {ICommand, IWindow, IWindowState, ISavedSession} from '@types'; + +export class Window extends React.Component { + constructor(props : IWindow) { super(props); this.state = { @@ -9,8 +16,10 @@ export class Window extends React.Component { windowTitles: [], color: "default", name: "", + auto_name: "", tabs: 0, - hover: false + hover: false, + dirty: false }; this.addTab = this.addTab.bind(this); @@ -36,43 +45,157 @@ export class Window extends React.Component { async componentDidMount() { await this.checkSettings(); + await this.update(); } + async componentDidUpdate(prevProps, prevState) { + if (this.state.dirty) { + await this.update(); + this.setState({ dirty: false }); + } + } + + async checkSettings() { - var colors = await getLocalStorage("windowColors", {}); - var color = colors[this.props.window.id] || "default"; + let colors = await getLocalStorageMap(S.windowColors); + let color = colors.get(this.props.window.id) || "default"; + + this.setState({ + color: color + }); + } - var name; - if (!!this.props.window.titlePreface) { - name = this.props.window.titlePreface; + async update() { + let name : string; + if (!!this.props.window.title) { + name = this.props.window.title; } else { - var names = await getLocalStorage("windowNames", {}); - if (typeof names !== 'object') { - await setLocalStorage("windowNames", {}); - names = {}; + let names : Map = await getLocalStorageMap(S.windowNames); + name = names.get(this.props.window.id) || ""; + } + + if (!!name) { + if (name !== this.state.name) { + this.setState({ + name: name + }); } - name = names[this.props.window.id] || ""; + return; } - this.setState({ - color: color, - name: name - }); + let _window_titles = this.state.windowTitles; + let _tabs = this.state.tabs; + let tabs = await browser.tabs.query({ windowId: this.props.window.id }); + if (tabs.length == 0) return; + + if (_window_titles.length === 0 || this.state.tabs !== tabs.length + this.props.window.id * 99) { + _window_titles.length = 0; + _tabs = tabs.length + this.props.window.id * 99; + + for (let i = 0; i < tabs.length; i++) { + const _tab = tabs[i]; + if (!!_tab && (!!_tab.url || !!_tab.pendingUrl)) { + let url : URL; + if (!!_tab.pendingUrl) { + url = new URL(_tab.pendingUrl); + } else if (!!_tab.url) { + url = new URL(_tab.url); + } + + // force refresh once we've loaded tabs + if (_tab.status == "loading") _tabs--; + + let protocol = url.protocol || ""; + let hostname = url.hostname || ""; + if (protocol.indexOf("view-source") > -1 && !!url.pathname) { + url = new URL(url.pathname); + hostname = url.hostname || "source"; + } else if (protocol.indexOf("chrome-extension") > -1) { + hostname = _tab.title || "extension"; + } else if (protocol.indexOf("about") > -1) { + hostname = _tab.title || "about"; + } else if (hostname.indexOf("mail.google") > -1) { + hostname = "gmail"; + } else { + if (!hostname) hostname = ""; + hostname = hostname.replace("www.", ""); + if (!isIpAddress(hostname)) { + let regex_var = new RegExp(/(\.[^\.]{0,2})(\.[^\.]{0,2})(\.*$)|(\.[^\.]*)(\.*$)/); + hostname = hostname + .replace(regex_var, "") + .split(".") + .pop(); + } else { + if (!!_tab.title) { + hostname = _tab.title; + } else { + let ip = hostname.split("."); + hostname = ip[0] + "." + ip[1] + ".*.*"; + } + } + } + + if (!hostname || hostname.length > 7) { + let title = _tab.title || ""; + + const separators = /\s[—|•-]\s/; // Define separators here + + do { + let titles = title.split(separators); + let first = titles[0]; + let last = titles[titles.length - 1]; + if (slugify(first) == slugify(hostname) || slugify_no_space(first) == slugify_no_space(hostname) || slugify_no_space(first).startsWith(slugify_no_space(hostname).substring(0, 3)) || slugify_no_space(hostname).startsWith(slugify_no_space(first).substring(0, 3))) { + title = first; + } else if (slugify(last) == slugify(hostname) || slugify_no_space(last) == slugify_no_space(hostname) || slugify_no_space(last).startsWith(slugify_no_space(hostname).substring(0, 3)) || slugify_no_space(hostname).startsWith(slugify_no_space(last).substring(0, 3))) { + title = last; + } else { + titles.sort((a : string, b : string) => a.length - b.length); + titles.pop(); + title = titles.join("-"); + } + } while (title.length > hostname.length && separators.test(title)) + + if (!hostname || (!!title && title.length < 23)) { + hostname = title; + } + + // while (hostname.length > 21 && hostname.indexOf(" ") > -1) { + // let hostnames = hostname.split(" "); + // hostnames.pop(); + // hostname = hostnames.join(" "); + // } + + } + + _window_titles.push(hostname); + } + } + + this.setState({ + tabs: _tabs + }) + } + + if (_window_titles.length > 0) { + name = this.topEntries(this.state.windowTitles).join(""); + this.setState({ + auto_name: name + }); + } } render() { - var _this = this; - - var color = this.state.color || "default"; - var name = this.state.name; - - var hideWindow = true; - var titleAdded = false; - var tabsperrow = this.props.layout.indexOf("blocks") > -1 ? Math.ceil(Math.sqrt(this.props.tabs.length + 2)) : this.props.layout === "vertical" ? 1 : 15; - var tabs = this.props.tabs.map(function(tab) { - var isHidden = !!_this.props.hiddenTabs[tab.id] && _this.props.filterTabs; - var isSelected = !!_this.props.selection[tab.id]; - hideWindow &= isHidden; + let _this = this; + + let color = this.state.color || "default"; + + let hideWindow = true; + let titleAdded = false; + let tabsperrow = this.props.layout.indexOf("blocks") > -1 ? Math.ceil(Math.sqrt(this.props.tabs.length + 2)) : this.props.layout === "vertical" ? 1 : 15; + let tabs = this.props.tabs.map(function(tab) { + let isHidden : boolean = _this.props.hiddenTabs.has(tab.id) && _this.props.filterTabs; + let isSelected : boolean = _this.props.selection.has(tab.id); + if (!isHidden) hideWindow = false; return ( @@ -331,112 +454,41 @@ export class Window extends React.Component { } if (this.props.windowTitles) { - if (!!name) { - tabs.unshift( -

- {name} -

- ); - titleAdded = true; - } else { - if (this.state.windowTitles.length === 0 || this.state.tabs !== tabs.length + this.props.window.id * 99) { - this.state.windowTitles = []; - this.state.tabs = tabs.length + this.props.window.id * 99; - for (var i = 0; i < tabs.length; i++) { - - if (!!tabs[i].props && !!tabs[i].props.tab && !!tabs[i].props.tab.url) { - var url = new URL(tabs[i].props.tab.url); - var protocol = url.protocol || ""; - var hostname = url.hostname || ""; - if (protocol.indexOf("view-source") > -1 && !!url.pathname) { - url = new URL(url.pathname); - hostname = url.hostname || "source"; - } else if (protocol.indexOf("chrome-extension") > -1) { - hostname = tabs[i].props.tab.title || "extension"; - } else if (protocol.indexOf("about") > -1) { - hostname = tabs[i].props.tab.title || "about"; - } else if (hostname.indexOf("mail.google") > -1) { - hostname = "gmail"; - } else { - if (!hostname) hostname = ""; - hostname = hostname.replace("www.", ""); - var regex_var = new RegExp(/(\.[^\.]{0,2})(\.[^\.]{0,2})(\.*$)|(\.[^\.]*)(\.*$)/); - hostname = hostname - .replace(regex_var, "") - .split(".") - .pop(); - } - - if (!!hostname && hostname.length > 18) { - hostname = tabs[i].props.tab.title || ""; - - while (hostname.length > 18 && hostname.indexOf("—") > -1) { - hostname = hostname.split("—"); - hostname.pop(); - hostname = hostname.join("—"); - } - - while (hostname.length > 18 && hostname.indexOf("-") > -1) { - hostname = hostname.split("-"); - hostname.pop(); - hostname = hostname.join("-"); - } - - while (hostname.length > 18 && hostname.indexOf(" ") > -1) { - hostname = hostname.split(" "); - hostname.pop(); - hostname = hostname.join(" "); - } - - } - this.state.windowTitles.push(hostname); - } - } - } - - if (this.state.windowTitles.length > 0) { - tabs.unshift( -

- {this.topEntries(this.state.windowTitles).join("")} -

- ); - titleAdded = true; - } - } + titleAdded = true; + tabs.unshift( +

+ {this.props.window.incognito ? "🕵" : ""} + {!!this.state.name ? this.state.name : this.state.auto_name} +

+ ); } if (tabsperrow < 5) { tabsperrow = 5; } - var children = []; + let children = []; if (!!titleAdded) { children.push(tabs.shift()); } - var z = -1; - for (var j = 0; j < tabs.length; j++) { - var tab = tabs[j].props.tab; - var isHidden = !!tab && !!tab.id && !!this.props.hiddenTabs[tab.id] && this.props.filterTabs; - if(!isHidden) { - z++; - children.push(tabs[j]); - } + let z = -1; + for (let j = 0; j < tabs.length; j++) { + let tab = tabs[j].props.tab; + let isHidden = !!tab && !!tab.id && this.props.hiddenTabs.has(tab.id) && this.props.filterTabs; + if(isHidden) continue; + z++; + children.push(tabs[j]); + if ((z + 1) % tabsperrow === 0 && z && this.props.layout.indexOf("blocks") > -1) { children.push(
); } } - var focused = false; + let focused = false; if (this.props.window.focused || this.props.lastOpenWindow === this.props.window.id) { focused = true; } @@ -486,27 +538,27 @@ export class Window extends React.Component { browser.tabs.create({ windowId: this.props.window.id }); } dragOver(e) { - this.state.hover = true; + this.setState({hover: true}); this.stopProp(e); } dragLeave(e) { - this.state.hover = false; + this.setState({hover: false}); e.nativeEvent.preventDefault(); } drop(e) { - var distance = 1000000; - var closestTab = null; - var closestRef = null; - - for (var i = 0; i < this.props.tabs.length; i++) { - var tab = this.props.tabs[i]; - var tabRef = this.refs["tab" + tab.id].tabRef.current; - var tabRect = tabRef.getBoundingClientRect(); - var x = e.nativeEvent.clientX; - var y = e.nativeEvent.clientY; - var dx = tabRect.x - x; - var dy = tabRect.y - y; - var d = Math.sqrt(dx * dx + dy * dy); + let distance = 1000000; + let closestTab = null; + let closestRef = null; + + for (let i = 0; i < this.props.tabs.length; i++) { + let tab = this.props.tabs[i]; + let tabRef = (this.refs["tab" + tab.id] as Tab).state.tabRef.current; + let tabRect = tabRef.getBoundingClientRect(); + let x = e.nativeEvent.clientX; + let y = e.nativeEvent.clientY; + let dx = tabRect.x - x; + let dy = tabRect.y - y; + let d = Math.sqrt(dx * dx + dy * dy); if (d < distance) { distance = d; closestTab = tab.id; @@ -517,8 +569,8 @@ export class Window extends React.Component { this.stopProp(e); if (closestTab != null) { - var before; - var boundingRect = closestRef.getBoundingClientRect(); + let before : boolean; + let boundingRect = closestRef.getBoundingClientRect(); if (this.props.layout === "vertical") { before = e.nativeEvent.clientY < boundingRect.top; } else { @@ -530,37 +582,37 @@ export class Window extends React.Component { } } hoverWindow(tabs, _) { - this.state.hover = true; + this.setState({ hover: true }); this.props.hoverIcon("Focus this window\nWill select this window with " + tabs.length + " tabs"); // this.props.hoverIcon(e); } hoverWindowOut(_) { - this.state.hover = false; + this.setState({ hover: false }); } - checkKey(e) { + async checkKey(e) { // close popup when enter or escape have been pressed if (e.keyCode === 13 || e.keyCode === 27) { this.stopProp(e); - this.closePopup(); + await this.closePopup(); } } async windowClick(e) { this.stopProp(e); - var windowId = this.props.window.id; + let windowId = this.props.window.id; if (navigator.userAgent.search("Firefox") > -1) { - browser.runtime.sendMessage({command: "focus_on_window_delayed", window_id: windowId}); + browser.runtime.sendMessage({command: S.focus_on_window_delayed, window_id: windowId}); } else { - browser.runtime.sendMessage({command: "focus_on_window", window_id: windowId}); + browser.runtime.sendMessage({command: S.focus_on_window, window_id: windowId}); } this.props.parentUpdate(); if (!!window.inPopup) window.close(); return false; } - selectToFromTab(tabId) { - if(tabId) this.props.selectTo(tabId, this.props.tabs); + selectToFromTab(tabId : number) { + if (!!tabId) this.props.selectTo(tabId, this.props.tabs); } async close(e) { this.stopProp(e); @@ -568,7 +620,7 @@ export class Window extends React.Component { } uuidv4() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { - var r = (Math.random() * 16) | 0, + let r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); @@ -577,35 +629,35 @@ export class Window extends React.Component { this.stopProp(e); console.log("session name", this.state.name); - var sessionName = this.state.name || this.topEntries(this.state.windowTitles).join(""); - var sessionColor = this.state.color || "default"; + let sessionName = this.state.name || this.topEntries(this.state.windowTitles).join(""); + let sessionColor = this.state.color || "default"; console.log("session name", sessionName); - var session = { + let session : ISavedSession = { tabs: [], - windowsInfo: {}, + windowsInfo: null, name: sessionName, + customName: !!this.state.name, color: sessionColor, date: Date.now(), sessionStartTime: Date.now(), + incognito: this.props.window.incognito, id: this.uuidv4() }; - if (this.state.name) { - session.customName = true; - } - - var queryInfo = {}; + let queryInfo : browser.Tabs.QueryQueryInfoType = { + windowId: this.props.window.id + }; //queryInfo.currentWindow = true; - queryInfo.windowId = this.props.window.id; + console.log(queryInfo); - var tabs = await browser.tabs.query(queryInfo); + let tabs : browser.Tabs.Tab[] = await browser.tabs.query(queryInfo); console.log(tabs); - for (var tabkey in tabs) { + for (let tabkey in tabs) { if (navigator.userAgent.search("Firefox") > -1) { - var newTab = tabs[tabkey]; + let newTab = tabs[tabkey]; if (!!newTab.url && newTab.url.search("about:") > -1) { continue; } @@ -617,10 +669,10 @@ export class Window extends React.Component { console.log(session); - var sessions = await getLocalStorage('sessions', {}); + let sessions = await getLocalStorage('sessions', {}); sessions[session.id] = session; - var value = await setLocalStorage('sessions', sessions).catch(function(err) { + let value = await setLocalStorage('sessions', sessions).catch(function(err) { console.log(err); console.error(err.message); }); @@ -661,39 +713,36 @@ export class Window extends React.Component { this.setState(a); this.props.toggleColors(!this.state.colorActive, this.props.window.id); - var color = a.color || "default"; + let color = a.color || "default"; - browser.runtime.sendMessage({ - command: "set_window_color", + browser.runtime.sendMessage({ + command: S.set_window_color, window_id: this.props.window.id, color: color }); - this.state.color = color; - this.setState({ - color: color - }); - this.closePopup(); + this.setState({ color: color }); + await this.closePopup(); } - closePopup() { + async closePopup() { this.props.toggleColors(!this.state.colorActive, this.props.window.id); this.setState({ colorActive: !this.state.colorActive }); + await this.update(); this.props.parentUpdate(); } async changeName(e) { // this.setState(a); - var name = ""; + let name = ""; if(e && e.target && e.target.value) name = e.target.value; - browser.runtime.sendMessage({ - command: "set_window_name", + browser.runtime.sendMessage({ + command: S.set_window_name, window_id: this.props.window.id, name: name }); - this.state.name = name; this.setState({ name: name }); @@ -709,16 +758,16 @@ export class Window extends React.Component { } } } - topEntries(arr) { - var cnts = arr.reduce(function(obj, val) { + topEntries(arr : string[]) : string[] { + let cnts = arr.reduce(function(obj, val) { obj[val] = (obj[val] || 0) + 1; return obj; }, {}); - var sorted = Object.keys(cnts).sort(function(a, b) { + let sorted = Object.keys(cnts).sort(function(a, b) { return cnts[b] - cnts[a]; }); - var more = 0; + let more = 0; if (sorted.length === 3) { } else { while (sorted.length > 2) { @@ -726,7 +775,7 @@ export class Window extends React.Component { more++; } } - for (var i = 0; i < sorted.length; i++) { + for (let i = 0; i < sorted.length; i++) { if (i > 0) { sorted[i] = ", " + sorted[i]; } @@ -747,3 +796,36 @@ export class Window extends React.Component { } } } + +function slugify(text) { + return text + .toString() + .toLowerCase() + .replace(/\s+/g, "-") // Replace spaces with - + .replace(/[^\w-]+/g, "") // Remove all non-word chars + .replace(/--+/g, "-") // Replace multiple - with single - + .replace(/^-+/, "") // Trim - from start of text + .replace(/-+$/, ""); // Trim - from end of text +} + +function slugify_no_space(text) { + return text + .toString() + .toLowerCase() + .replace(/\s+/g, "") // Replace spaces with - + .replace(/[^\w-]+/g, "") // Remove all non-word chars + .replace(/--+/g, "-") // Replace multiple - with single - + .replace(/^-+/, "") // Trim - from start of text + .replace(/-+$/, ""); // Trim - from end of text +} + +// Function to check if the string is an IP address (IPv4 or IPv6) +function isIpAddress(input) { + // Regex for IPv4: 0-255.0-255.0-255.0-255 + const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + + // Regex for IPv6: Matches standard IPv6 formatting (8 groups of 4 hex digits) + const ipv6Regex = /^([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/; + + return ipv4Regex.test(input) || ipv6Regex.test(input); +} \ No newline at end of file From 8bb24d44dfe3b874fdd18a2e0ed8ea86ac817316 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:01:33 +0700 Subject: [PATCH 18/46] Move TabOptions from jsx to tsx --- lib/popup/views/TabOptions.jsx | 417 -------------------------------- lib/popup/views/TabOptions.tsx | 422 +++++++++++++++++++++++++++++++++ lib/types/ITabOptions.ts | 46 ++++ lib/types/ITabOptionsState.ts | 3 + lib/types/index.ts | 4 +- 5 files changed, 474 insertions(+), 418 deletions(-) delete mode 100644 lib/popup/views/TabOptions.jsx create mode 100644 lib/popup/views/TabOptions.tsx create mode 100644 lib/types/ITabOptions.ts create mode 100644 lib/types/ITabOptionsState.ts diff --git a/lib/popup/views/TabOptions.jsx b/lib/popup/views/TabOptions.jsx deleted file mode 100644 index 06ced850..00000000 --- a/lib/popup/views/TabOptions.jsx +++ /dev/null @@ -1,417 +0,0 @@ -"use strict"; - -class TabOptions extends React.Component { - constructor(props) { - super(props); - this.state = {}; - } - logo() { - var logo = [,

Tab Manager Plus {__VERSION__}

]; - - return ( -
-
{logo}
-
- ); - } - optionsSection() { - var opts = [ -
-

Tab options

-
- -
-
, -
-

Popup size

-
- You can resize the popup here up to a maximum size of 800x600. This limitation is a browser limitation, and we cannot display a bigger popup due to - this. If you want to have a better overview, instead you can right click on the Tab Manager Plus icon, and `open in own tab`. This will open the Tab - Manager in a new tab. -
-
- - -
-
- - -
-
, -
-

Window style

-
-
- -
- -
- Dark mode, for working at night time.
- By default: disabled -
-
-
-
- -
- -
- Saves a little bit of space around the icons. Makes it less beautiful, but more space efficient.
- By default: disabled -
-
-
-
- -
- -
- Disables/enables animations and transitions in the popup.
- By default: enabled -
-
-
-
- -
- -
- Disables/enables window titles.
- By default: enabled -
-
-
, -
-

Session Management

-
-
- -
- -
- Allows you to save windows as sessions ( saved windows ). You can restore these saved windows later on. The restored windows won't have the history - restored. This feature is currently in beta. -
- By default: disabled ( experimental feature ) -
-
- {this.props.sessionsFeature &&
-
- - -
-
Allows you to backup your saved windows to an external file.
-
} - {this.props.sessionsFeature &&
-
- - -
-
- Allows you to restore your backup from an external file. The restored windows will be added to your current saved windows. -
-
} -
, -
-

Popup icon

-
-
- -
- -
- Shows you the number of open tabs over the Tab Manager icon in the top right of your browser. -
- By default: enabled -
-
-
-
- -
- -
- Opens the Tab Manager in own tab by default, instead of the popup. -
- By default: disabled -
-
-
, -
-

Window settings

-
-
- -
- -
- With this option enabled, you will only have 1 open window per monitor at all times. When you switch to another window, the other windows will be - minimized to the tray automatically. -
- By default: disabled -
-
-
-
- -
- -
- Displays buttons in every window for : opening a new tab, minimizing the window, assigning a color to the window and closing the window. -
- By default: enabled -
-
-
, -
-

Advanced settings

-
- -
- If you also want to see your incognito tabs in the Tab Manager overview, then enable incognito access for this extension. -
-
-
- - Change shortcut key - -
If you want to disable or change the shortcut key with which to open Tab Manager Plus, you can do so here.
-
-
, -
-
-

Right mouse button

-
With the right mouse button you can select tabs
-

Shift+Right mouse button

-
- While holding shift, and pressing the right mouse button you can select all tabs between the last selected tab and the current one -
-

Middle mouse button

-
With the middle mouse button you can close a tab
-

[Enter / Return] button

-
- With the return button you can switch to the currently selected tab, or move multiple selected tabs to a new window -
-
-
- ]; - - return
{opts}
; - } - openIncognitoOptions() { - browser.tabs.create({ - url: "chrome://extensions/?id=cnkdjjdmfiffagllbiiilooaoofcoeff" - }); - } - openShortcuts() { - browser.tabs.create({ url: "chrome://extensions/shortcuts" }); - } - licenses() { - var licenses = []; - licenses.push( -
- Tab Manager Plus is based on{" "} - - dsc/Tab-Manager - - ,{" "} - - joshperry/Tab-Manager - {" "} - and{" "} - - JonasNo/Tab-Manager - - .
- Licensed by{" "} - - MPLv2 - - . Icons made by{" "} - - Freepik - {" "} - from{" "} - - www.flaticon.com - - . Licensed by{" "} - - CC 3.0 BY - - . -
- ); - - return
{licenses}
; - } - render() { - var children = []; - - children.push(this.logo()); - children.push(this.optionsSection()); - children.push(
); - //children.push(React.createElement('h4', {}, this.props.getTip())); - children.push(this.licenses()); - - return ( -
-
{children}
-
- ); - } -} \ No newline at end of file diff --git a/lib/popup/views/TabOptions.tsx b/lib/popup/views/TabOptions.tsx new file mode 100644 index 00000000..cc3c5d16 --- /dev/null +++ b/lib/popup/views/TabOptions.tsx @@ -0,0 +1,422 @@ +"use strict"; + +import * as React from "react"; +import * as browser from 'webextension-polyfill'; +import { ITabOptions, ITabOptionsState } from "@types"; + +export class TabOptions extends React.Component { + constructor(props) { + super(props); + this.state = {}; + } + logo() { + return ( +
+
+ Tab Manager Plus +

Tab Manager Plus {"__VERSION__"}

+
+
+ ); + } + + optionsSection() { + return ( +
+
+

Tab options

+
+ +
+
+
+

Popup size

+
+ You can resize the popup here up to a maximum size of 800x600. This limitation is a browser limitation, and we cannot display a bigger popup due to + this. If you want to have a better overview, instead you can right click on the Tab Manager Plus icon, and `open in own tab`. This will open the Tab + Manager in a new tab. +
+
+ + +
+
+ + +
+
+
+

Window style

+
+
+ +
+ +
+ Dark mode, for working at night time.
+ By default: disabled +
+
+
+
+ +
+ +
+ Saves a little bit of space around the icons. Makes it less beautiful, but more space efficient.
+ By default: disabled +
+
+
+
+ +
+ +
+ Disables/enables animations and transitions in the popup.
+ By default: enabled +
+
+
+
+ +
+ +
+ Disables/enables window titles.
+ By default: enabled +
+
+
+
+

Session Management

+
+
+ +
+ +
+ Allows you to save windows as sessions ( saved windows ). You can restore these saved windows later on. The restored windows won't have the history + restored. This feature is currently in beta. +
+ By default: disabled ( experimental feature ) +
+
+ {this.props.sessionsFeature &&
+
+ + +
+
Allows you to backup your saved windows to an external file.
+
} + {this.props.sessionsFeature &&
+
+ + +
+
+ Allows you to restore your backup from an external file. The restored windows will be added to your current saved windows. +
+
} +
+
+

Popup icon

+
+
+ +
+ +
+ Shows you the number of open tabs over the Tab Manager icon in the top right of your browser. +
+ By default: enabled +
+
+
+
+ +
+ +
+ Opens the Tab Manager in own tab by default, instead of the popup. +
+ By default: disabled +
+
+
+
+

Window settings

+
+
+ +
+ +
+ With this option enabled, you will only have 1 open window per monitor at all times. When you switch to another window, the other windows will be + minimized to the tray automatically. +
+ By default: disabled +
+
+
+
+ +
+ +
+ Displays buttons in every window for : opening a new tab, minimizing the window, assigning a color to the window and closing the window. +
+ By default: enabled +
+
+
+
+

Advanced settings

+
+ +
+ If you also want to see your incognito tabs in the Tab Manager overview, then enable incognito access for this extension. +
+
+
+ + Change shortcut key + +
If you want to disable or change the shortcut key with which to open Tab Manager Plus, you can do so here.
+
+
+
+
+

Right mouse button

+
With the right mouse button you can select tabs
+

Shift+Right mouse button

+
+ While holding shift, and pressing the right mouse button you can select all tabs between the last selected tab and the current one +
+

Middle mouse button

+
With the middle mouse button you can close a tab
+

[Enter / Return] button

+
+ With the return button you can switch to the currently selected tab, or move multiple selected tabs to a new window +
+
+
+
+ ); + } + openIncognitoOptions() { + browser.tabs.create({ + url: "chrome://extensions/?id=cnkdjjdmfiffagllbiiilooaoofcoeff" + }); + } + openShortcuts() { + browser.tabs.create({ url: "chrome://extensions/shortcuts" }); + } + licenses() { + return ( +
+
+ Tab Manager Plus is based on{" "} + + dsc/Tab-Manager + + ,{" "} + + joshperry/Tab-Manager + {" "} + and{" "} + + JonasNo/Tab-Manager + + .
+ Licensed by{" "} + + MPLv2 + + . Icons made by{" "} + + Freepik + {" "} + from{" "} + + www.flaticon.com + + . Licensed by{" "} + + CC 3.0 BY + + . +
+
+ ); + } + render() { + var children = []; + + children.push(this.logo()); + children.push(this.optionsSection()); + children.push(
); + //children.push(React.createElement('h4', {}, this.props.getTip())); + children.push(this.licenses()); + + return ( +
+
{children}
+
+ ); + } +} \ No newline at end of file diff --git a/lib/types/ITabOptions.ts b/lib/types/ITabOptions.ts new file mode 100644 index 00000000..37e79b8f --- /dev/null +++ b/lib/types/ITabOptions.ts @@ -0,0 +1,46 @@ +import * as React from "react"; + +export interface ITabOptions { + animations: boolean, + badge: boolean, + compact: boolean, + dark: boolean, + hideWindows: boolean, + openInOwnTab: boolean, + sessionsFeature: boolean, + tabHeight: number, + tabLimit: number, + tabWidth: number, + tabactions: boolean, + windowTitles: boolean, + + tabLimitText: () => void, + changeTabLimit: (e: React.ChangeEvent) => void, + tabWidthText: () => void, + changeTabWidth: (e: React.ChangeEvent) => void, + tabHeightText: () => void, + changeTabHeight: (e: React.ChangeEvent) => void, + darkText: () => void, + toggleDark: () => void, + compactText: () => void, + toggleCompact: () => void, + animationsText: () => void, + toggleAnimations: () => void, + windowTitlesText: () => void, + toggleWindowTitles: () => void, + sessionsText: () => void, + toggleSessions: () => void, + exportSessionsText: () => void, + exportSessions: () => void, + importSessionsText: () => void, + importSessions: (e: React.ChangeEvent) => void, + badgeText: () => void, + toggleBadge: () => void, + openInOwnTabText: () => void, + toggleOpenInOwnTab: () => void, + hideText: () => void, + toggleHide: () => void, + tabActionsText: () => void, + toggleTabActions: () => void + getTip: () => string +} \ No newline at end of file diff --git a/lib/types/ITabOptionsState.ts b/lib/types/ITabOptionsState.ts new file mode 100644 index 00000000..bbe57112 --- /dev/null +++ b/lib/types/ITabOptionsState.ts @@ -0,0 +1,3 @@ +export interface ITabOptionsState { + +} \ No newline at end of file diff --git a/lib/types/index.ts b/lib/types/index.ts index 3a195ebd..548d63c5 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -7,4 +7,6 @@ export * from './IWindow'; export * from './IWindowState'; export * from './ISession'; export * from './ISessionState'; -export * from './ISavedSession'; \ No newline at end of file +export * from './ISavedSession'; +export * from './ITabOptions'; +export * from './ITabOptionsState'; \ No newline at end of file From 577103ff64051a754bd7a14b1603e1c4fbc17382 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:02:01 +0700 Subject: [PATCH 19/46] @views and other helpers --- lib/popup/views/index.ts | 5 +++++ tsconfig.json | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/popup/views/index.ts create mode 100644 tsconfig.json diff --git a/lib/popup/views/index.ts b/lib/popup/views/index.ts new file mode 100644 index 00000000..9f5ec9e5 --- /dev/null +++ b/lib/popup/views/index.ts @@ -0,0 +1,5 @@ +export * from './Session' +export * from './Tab' +export * from './TabManager' +export * from './TabOptions' +export * from './Window' \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..561eac94 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "jsx": "react", + "baseUrl": "./lib", + "paths": { + "@views": [ + "popup/views/index" + ], + "@background/*": [ + "service_worker/background/*" + ], + "@helpers/*": [ + "helpers/*" + ], + "@types": [ + "types/index" + ], + "@ui/*": [ + "service_worker/ui/*" + ], + "@strings": [ + "strings/strings" + ], + "@context": [ + "service_worker/context" + ] + }, + "target": "es6" + } +} \ No newline at end of file From 0a105d7dff9b94226b76f2dacb3e8f94c48b8c76 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:04:00 +0700 Subject: [PATCH 20/46] Move Tab from jsx to tsx Fix : Also use favicon url based on pending url if tab is loading Fix : Don't attempt to show empty favicon --- lib/popup/views/{Tab.jsx => Tab.tsx} | 140 +++++++++++++++------------ 1 file changed, 77 insertions(+), 63 deletions(-) rename lib/popup/views/{Tab.jsx => Tab.tsx} (62%) diff --git a/lib/popup/views/Tab.jsx b/lib/popup/views/Tab.tsx similarity index 62% rename from lib/popup/views/Tab.jsx rename to lib/popup/views/Tab.tsx index 7900191d..d2bdaf49 100644 --- a/lib/popup/views/Tab.jsx +++ b/lib/popup/views/Tab.tsx @@ -1,12 +1,19 @@ "use strict"; -class Tab extends React.Component { +import * as S from "@strings"; +import * as React from "react"; +import * as browser from 'webextension-polyfill'; +import {ICommand, ITab, ITabState} from '@types'; + +export class Tab extends React.Component { constructor(props) { super(props); this.state = { favIcon: "", dragFavIcon: "", - hovered: false + draggingOver: "", + hovered: false, + tabRef: React.createRef() }; this.onHover = this.onHover.bind(this); @@ -19,8 +26,6 @@ class Tab extends React.Component { this.drop = this.drop.bind(this); this.resolveFavIconUrl = this.resolveFavIconUrl.bind(this); this.checkSettings = this.checkSettings.bind(this); - - this.tabRef = React.createRef(); } async componentDidMount() { @@ -32,8 +37,8 @@ class Tab extends React.Component { } render() { - var children = []; - if (this.props.layout == "vertical") { + const children = []; + if (this.props.layout === "vertical") { children.push(
Pinned @@ -54,7 +59,7 @@ class Tab extends React.Component { key={"tab-icon-" + this.props.tab.id} className="iconoverlay " style={{ - backgroundImage: "url(" + this.state.favIcon + ")" + backgroundImage: !!this.state.favIcon ? "url(" + this.state.favIcon + ")" : "" }} /> ); @@ -75,18 +80,18 @@ class Tab extends React.Component { ((this.props.tab.mutedInfo && this.props.tab.mutedInfo.muted) ? "muted " : "") + (this.props.tab.audible ? "audible " : "") + (this.props.tab.discarded ? "discarded " : "") + - (this.props.layout == "vertical" ? "full " : "") + + (this.props.layout === "vertical" ? "full " : "") + (this.props.tab.incognito ? "incognito " : "") + - (this.state.draggingOver || "") + + (this.state.draggingOver) + (this.props.searchActive ? "search-active " : "") + " tab-" + this.props.tab.id + " " + - (this.props.layout == "vertical" ? "vertical " : "blocks "), + (this.props.layout === "vertical" ? "vertical " : "blocks "), style: - (this.props.layout == "vertical" + (this.props.layout === "vertical" ? { } - : { backgroundImage: "url(" + this.state.favIcon + ")" } + : { backgroundImage: !!this.state.favIcon ? "url(" + this.state.favIcon + ")" : "" } ) , id: this.props.id, @@ -95,7 +100,7 @@ class Tab extends React.Component { onMouseDown: this.onMouseDown, onMouseEnter: this.onHover, onMouseOut: this.onHoverOut, - ref: this.tabRef + ref: this.state.tabRef }; if (!!this.props.draggable) { @@ -114,21 +119,21 @@ class Tab extends React.Component { ); } onHover(e) { - this.setState({hover: true}); + this.setState({hovered: true}); this.props.hoverHandler(this.props.tab); } onHoverOut(e) { - this.setState({hover: false}); + this.setState({hovered: false}); } - onMouseDown(e) { + async onMouseDown(e : React.MouseEvent) { if (e.button === 0) return; if (!this.props.draggable) return; - this.click(e); + await this.click(e); } - async click(e) { + async click(e : React.MouseEvent) { this.stopProp(e); - var tabId = this.props.tab.id; + var tabId : number = this.props.tab.id; var windowId = this.props.window.id; if (e.button === 1) { @@ -145,14 +150,14 @@ class Tab extends React.Component { this.props.click(e, this.props.tab.id); } else { if (navigator.userAgent.search("Firefox") > -1) { - browser.runtime.sendMessage({ - command: "focus_on_tab_and_window_delayed", - tab: {id: tabId, windowId: windowId} + browser.runtime.sendMessage({ + command: S.focus_on_tab_and_window_delayed, + saved_tab: {tabId: tabId, windowId: windowId} }); } else { - browser.runtime.sendMessage({ - command: "focus_on_tab_and_window", - tab: {id: tabId, windowId: windowId} + browser.runtime.sendMessage({ + command: S.focus_on_tab_and_window, + saved_tab: {tabId: tabId, windowId: windowId} }); } } @@ -161,29 +166,38 @@ class Tab extends React.Component { } return false; } - dragStart(e) { + dragStart(e : React.DragEvent) { if (!this.props.draggable) return false; if (!this.props.drag) return false; - this.state.dragFavIcon = ""; + this.setState({ + dragFavIcon: "" + }); this.props.dragFavicon(this.state.favIcon); - e.dataTransfer.setData("Text", this.props.tab.id); + e.dataTransfer.setData("Text", this.props.tab.id.toString()); e.dataTransfer.setData("text/uri-list", this.props.tab.url || ""); this.props.drag(e, this.props.tab.id); } - dragOver(e) { + dragOver(e : React.DragEvent) { if (!this.props.draggable) return false; if (!this.props.drag) return false; - this.state.dragFavIcon = this.props.dragFavicon(); + let favicon = this.props.dragFavicon(); + let draggingover; var before = this.state.draggingOver; - if (this.props.layout == "vertical") { - this.state.draggingOver = e.nativeEvent.offsetY > ReactDOM.findDOMNode(this).clientHeight / 2 ? "bottom" : "top"; + if (this.props.layout === "vertical") { + draggingover = e.nativeEvent.offsetY > this.state.tabRef.current.clientHeight / 2 ? "bottom" : "top"; } else { - this.state.draggingOver = e.nativeEvent.offsetX > ReactDOM.findDOMNode(this).clientWidth / 2 ? "right" : "left"; + draggingover = e.nativeEvent.offsetX > this.state.tabRef.current.clientWidth / 2 ? "right" : "left"; } - if (before != this.state.draggingOver) { + + this.setState({ + draggingOver: draggingover, + dragFavIcon: favicon + }); + + if (before !== this.state.draggingOver) { this.forceUpdate(); this.props.parentUpdate(); } @@ -191,55 +205,55 @@ class Tab extends React.Component { dragOut() { if (!this.props.draggable) return false; if (!this.props.drag) return; - this.state.dragFavIcon = ""; - delete this.state.draggingOver; + + this.setState({ + dragFavIcon: "", + draggingOver: "" + }); this.forceUpdate(); this.props.parentUpdate(); } - drop(e) { + drop(e : React.DragEvent) { if (!this.props.draggable) return false; if (!this.props.drag) return false; if (!this.props.drop) return; - this.state.dragFavIcon = ""; this.stopProp(e); - var before = this.state.draggingOver == "top" || this.state.draggingOver == "left"; - delete this.state.draggingOver; + + var before = this.state.draggingOver === "top" || this.state.draggingOver === "left"; + + this.setState({ + draggingOver: "", + dragFavIcon: "" + }); this.props.drop(this.props.tab.id, before); this.forceUpdate(); this.props.parentUpdate(); } async resolveFavIconUrl() { - var image; + let image : string; // firefox screenshots; needs // if(!!browser.tabs.captureTab) { // console.log("tabs captureTab"); // image = await browser.tabs.captureTab(this.props.tab.id); // image = "url(" + image + ")"; // }else - if (!!this.props.tab.url && navigator.userAgent.search("Firefox") === -1) { - image = "chrome-extension://" + chrome.runtime.id + "/_favicon/?pageUrl=" + encodeURIComponent(this.props.tab.url) + "&size=64&" + Date.now(); - } else if (!!this.props.tab.url && this.props.tab.url.indexOf("chrome://") !== 0 && this.props.tab.url.indexOf("about:") !== 0) { - // chrome screenshots / only for active tabs; needs - // if(!!browser.tabs.captureVisibleTab && this.props.tab.highlighted) { - // console.log("tabsCapture"); - // try { - // image = await browser.tabs.captureVisibleTab( this.props.window.id, {} ); - // //console.log(image); - // } catch ( e ) { - // console.log(e.message); - // } - // image = "url(" + image + ")"; - // }else{ - image = this.props.tab.favIconUrl ? "" + this.props.tab.favIconUrl + "" : ""; - //} - } else { - var favIcons = ["bookmarks", "chrome", "crashes", "downloads", "extensions", "flags", "history", "settings"]; - var iconUrl = this.props.tab.url || ""; - var iconName = ""; - if (iconUrl.length > 9) iconName = iconUrl.slice(9).match(/^\w+/g); - image = !iconName || favIcons.indexOf(iconName[0]) < 0 ? "" : "../images/chrome/" + iconName[0] + ".png"; + + var _url : string = this.props.tab.url || this.props.tab.pendingUrl || ""; + + if (!!_url && navigator.userAgent.search("Firefox") === -1) { + image = "chrome-extension://" + chrome.runtime.id + "/_favicon/?pageUrl=" + encodeURIComponent(_url) + "&size=64"; // &" + Date.now(); + } else if (!!_url && _url.indexOf("chrome://") !== 0 && _url.indexOf("about:") !== 0) { + image = this.props.tab.favIconUrl ? "" + this.props.tab.favIconUrl + "" : ""; + } else { + const favIcons = ["bookmarks", "chrome", "crashes", "downloads", "extensions", "flags", "history", "settings"]; + let iconUrl = _url; + if (iconUrl.length > 9) { + let iconName = iconUrl.slice(9).match(/^\w+/g); + console.log(iconName); + image = !iconName || favIcons.indexOf(iconName[0]) < 0 ? "" : "../images/chrome/" + iconName[0] + ".png"; + } } this.setState({ favIcon: image From cb25d8ed98922708f4633539eb6de92c888d28c2 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:04:50 +0700 Subject: [PATCH 21/46] Move Session from jsx to tsx --- lib/popup/views/{Session.jsx => Session.tsx} | 94 +++++++++++--------- 1 file changed, 50 insertions(+), 44 deletions(-) rename lib/popup/views/{Session.jsx => Session.tsx} (60%) diff --git a/lib/popup/views/Session.jsx b/lib/popup/views/Session.tsx similarity index 60% rename from lib/popup/views/Session.jsx rename to lib/popup/views/Session.tsx index 7b1425c1..04e39dbb 100644 --- a/lib/popup/views/Session.jsx +++ b/lib/popup/views/Session.tsx @@ -1,17 +1,23 @@ -"use strict"; +"use strict" -class Session extends React.Component { - constructor(props) { +import {getLocalStorage, setLocalStorage} from "@helpers/storage"; +import {Tab} from "@views"; +import * as React from "react"; +import * as browser from 'webextension-polyfill'; +import {ICommand, ISession, ISessionState} from '@types'; +import * as S from "@strings"; + +export class Session extends React.Component { + constructor(props : ISession) { super(props); - //console.log(this.props.window); - //console.log(this.props.window.name); - var name = this.props.window.name; - var color = this.props.window.color || "default"; + //console.log(this.props.session); + //console.log(this.props.session.name); + let name = this.props.session.name; + let color = this.props.session.color || "default"; + this.state = { - windowTitles: [], name: name, - color: color, - tabs: 0 + color: color }; this.stop = this.stop.bind(this); @@ -23,22 +29,22 @@ class Session extends React.Component { } render() { - var _this = this; - var name = this.state.name; - var color = this.state.color; - var hideWindow = true; - var titleAdded = false; - var tabsperrow = this.props.layout.indexOf("blocks") > -1 ? Math.ceil(Math.sqrt(this.props.tabs.length + 2)) : this.props.layout === "vertical" ? 1 : 15; - var tabs = this.props.tabs.map(function(tab) { - var tabId = tab.id * tab.id * tab.id * 100; - var isHidden = !!_this.props.hiddenTabs[tabId] && _this.props.filterTabs; - var isSelected = !!_this.props.selection[tabId]; + let _this = this; + let name = this.state.name; + let color = this.state.color || "default"; + let hideWindow = true; + let titleAdded = false; + let tabsperrow = this.props.layout.indexOf("blocks") > -1 ? Math.ceil(Math.sqrt(this.props.tabs.length + 2)) : this.props.layout === "vertical" ? 1 : 15; + let tabs = this.props.tabs.map(function(tab) { + let tabId = tab.id * tab.id * tab.id * 100; + let isHidden = _this.props.hiddenTabs.has(tabId) && _this.props.filterTabs; + let isSelected = _this.props.selection.has(tabId); tab.id = tab.index; - hideWindow &= isHidden; + if (!isHidden) hideWindow = false; return ( , -
+
, +
-1 ? "" : "windowaction")} title={"Restore this saved window\nWill restore " + tabs.length + " tabs. Please note : The tabs will be restored without their history."} @@ -78,7 +84,7 @@ class Session extends React.Component { if (this.props.windowTitles) { if (name) { tabs.unshift( -

+

{name}

); @@ -95,20 +101,20 @@ class Session extends React.Component { for (var j = 0; j < tabs.length; j++) { children.push(tabs[j]); if ((j + 1) % tabsperrow === 0 && j && this.props.layout.indexOf("blocks") > -1) { - children.push(
); + children.push(
); } } var focused = false; - if (this.props.window.windowsInfo.focused || this.props.lastOpenWindow === this.props.window.windowsInfo.id) { + if (this.props.session.windowsInfo.focused || this.props.lastOpenWindow === this.props.session.windowsInfo.id) { focused = true; } return (
) { e.stopPropagation(); } - async windowClick(e) { + async windowClick(e : React.MouseEvent) { this.restoreSession(e, null); } - async openTab(e, index) { + async openTab(e : React.MouseEvent, index : number) { console.log(index); this.restoreSession(e, index); } - async restoreSession(e, tabId) { + async restoreSession(e : React.MouseEvent, tabId : number) { e.stopPropagation(); - console.log("source window", this.props.window); + console.log("source window", this.props.session); // chrome.runtime.getBackgroundPage(function callback(tabs, backgroundPage) { // backgroundPage.createWindowWithTabs(tabs); - // }.bind(null, this.props.window.tabs)); + // }.bind(null, this.props.session.tabs)); - browser.runtime.sendMessage({command: "create_window_with_session_tabs", window: this.props.window, tab_id: tabId}); + browser.runtime.sendMessage({command: S.create_window_with_session_tabs, session: this.props.session, tab_id: tabId}); @@ -177,8 +183,8 @@ class Session extends React.Component { // }); // } // browser.windows.update(w.id, { focused: true }); - // }.bind(null, this.props.window.tabs)); - // browser.windows.update(this.props.window.windowsInfo.id, { + // }.bind(null, this.props.session.tabs)); + // browser.windows.update(this.props.session.windowsInfo.id, { // "focused": true }, // function (a) {this.props.parentUpdate();}.bind(this)); } @@ -186,7 +192,7 @@ class Session extends React.Component { e.stopPropagation(); var sessions = await getLocalStorage('sessions', {}); - delete sessions[this.props.window.id]; + delete sessions[this.props.session.id]; var value = await setLocalStorage('sessions', sessions).catch(function (err) { console.log(err); @@ -195,11 +201,11 @@ class Session extends React.Component { console.log(value); this.props.parentUpdate(); - // browser.windows.remove(this.props.window.windowsInfo.id); + // browser.windows.remove(this.props.session.windowsInfo.id); } maximize(e) { e.stopPropagation(); - // browser.windows.update(this.props.window.windowsInfo.id, { + // browser.windows.update(this.props.session.windowsInfo.id, { // "state": "normal" }, // function (a) {this.props.parentUpdate();}.bind(this)); } From e4077b05292cf7653127c7de727364c0e255cf08 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:04:57 +0700 Subject: [PATCH 22/46] Update TabOptionsFirefox.jsx --- lib/popup/views/TabOptionsFirefox.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/popup/views/TabOptionsFirefox.jsx b/lib/popup/views/TabOptionsFirefox.jsx index 81dff048..e408a059 100644 --- a/lib/popup/views/TabOptionsFirefox.jsx +++ b/lib/popup/views/TabOptionsFirefox.jsx @@ -1,6 +1,8 @@ "use strict"; -class TabOptions extends React.Component { +var browser = browser || chrome; + +export class TabOptions extends React.Component { constructor(props) { super(props); this.state = {}; From e40da3886f15f43cf797d3770a7dafcc5e3dbe21 Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:08:36 +0700 Subject: [PATCH 23/46] Entry point through popup.js now --- options.html | 11 ----------- popup.html | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/options.html b/options.html index 80eb30be..5191cea8 100644 --- a/options.html +++ b/options.html @@ -10,17 +10,6 @@ - - - - - - - - - - - diff --git a/popup.html b/popup.html index 3741d6d8..8d95a8b3 100644 --- a/popup.html +++ b/popup.html @@ -10,17 +10,6 @@ - - - - - - - - - - - From 1f833fd741f2318f085905bf39ee6f5cfd18fddf Mon Sep 17 00:00:00 2001 From: mastef Date: Tue, 1 Oct 2024 12:10:24 +0700 Subject: [PATCH 24/46] Update built files --- outlib/helpers/migrate.js | 136 - outlib/helpers/migrate.js.map | 1 - outlib/helpers/storage.js | 82 - outlib/helpers/storage.js.map | 1 - outlib/helpers/utils.js | 39 - outlib/helpers/utils.js.map | 1 - outlib/popup/changelog.js | 4 - outlib/popup/changelog.js.map | 1 - outlib/popup/options.js | 8 +- outlib/popup/options.js.map | 8 +- outlib/popup/popup.js | 26591 +++++++++++++++- outlib/popup/popup.js.map | 8 +- outlib/popup/views/Session.js | 317 - outlib/popup/views/Session.js.map | 1 - outlib/popup/views/Tab.js | 343 - outlib/popup/views/Tab.js.map | 1 - outlib/popup/views/TabManager.js | 2897 -- outlib/popup/views/TabManager.js.map | 1 - outlib/popup/views/TabOptions.js | 734 - outlib/popup/views/TabOptions.js.map | 1 - outlib/popup/views/TabOptionsFirefox.js | 699 - outlib/popup/views/TabOptionsFirefox.js.map | 1 - outlib/popup/views/Window.js | 1063 - outlib/popup/views/Window.js.map | 1 - outlib/service_worker/background/actions.js | 178 - .../service_worker/background/actions.js.map | 1 - outlib/service_worker/background/tabs.js | 503 - outlib/service_worker/background/tabs.js.map | 1 - outlib/service_worker/background/tracking.js | 293 - .../service_worker/background/tracking.js.map | 1 - outlib/service_worker/background/windows.js | 786 - .../service_worker/background/windows.js.map | 1 - outlib/service_worker/service_worker.js | 2093 +- outlib/service_worker/service_worker.js.map | 8 +- outlib/service_worker/ui/context_menus.js | 161 - outlib/service_worker/ui/context_menus.js.map | 1 - outlib/service_worker/ui/open.js | 177 - outlib/service_worker/ui/open.js.map | 1 - 38 files changed, 28421 insertions(+), 8723 deletions(-) delete mode 100644 outlib/helpers/migrate.js delete mode 100644 outlib/helpers/migrate.js.map delete mode 100644 outlib/helpers/storage.js delete mode 100644 outlib/helpers/storage.js.map delete mode 100644 outlib/helpers/utils.js delete mode 100644 outlib/helpers/utils.js.map delete mode 100644 outlib/popup/changelog.js delete mode 100644 outlib/popup/changelog.js.map delete mode 100644 outlib/popup/views/Session.js delete mode 100644 outlib/popup/views/Session.js.map delete mode 100644 outlib/popup/views/Tab.js delete mode 100644 outlib/popup/views/Tab.js.map delete mode 100644 outlib/popup/views/TabManager.js delete mode 100644 outlib/popup/views/TabManager.js.map delete mode 100644 outlib/popup/views/TabOptions.js delete mode 100644 outlib/popup/views/TabOptions.js.map delete mode 100644 outlib/popup/views/TabOptionsFirefox.js delete mode 100644 outlib/popup/views/TabOptionsFirefox.js.map delete mode 100644 outlib/popup/views/Window.js delete mode 100644 outlib/popup/views/Window.js.map delete mode 100644 outlib/service_worker/background/actions.js delete mode 100644 outlib/service_worker/background/actions.js.map delete mode 100644 outlib/service_worker/background/tabs.js delete mode 100644 outlib/service_worker/background/tabs.js.map delete mode 100644 outlib/service_worker/background/tracking.js delete mode 100644 outlib/service_worker/background/tracking.js.map delete mode 100644 outlib/service_worker/background/windows.js delete mode 100644 outlib/service_worker/background/windows.js.map delete mode 100644 outlib/service_worker/ui/context_menus.js delete mode 100644 outlib/service_worker/ui/context_menus.js.map delete mode 100644 outlib/service_worker/ui/open.js delete mode 100644 outlib/service_worker/ui/open.js.map diff --git a/outlib/helpers/migrate.js b/outlib/helpers/migrate.js deleted file mode 100644 index 76f22f0f..00000000 --- a/outlib/helpers/migrate.js +++ /dev/null @@ -1,136 +0,0 @@ -"use strict"; - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - -var browser = browser || chrome; - -var stringkeys = ["layout"]; - -var jsonkeys = ["tabLimit", "tabWidth", "tabHeight", "windowAge", "windowNames", "windowColors"]; - -var boolkeys = ["openInOwnTab", "animations", "windowTitles", "compact", "dark", "tabactions", "badge", "sessionsFeature", "hideWindows", "filter-tabs"]; - -_asyncToGenerator(regeneratorRuntime.mark(function _callee() { - var hasLocalStorage, key, keyValue, values; - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - hasLocalStorage = false; - - - for (key in stringkeys) { - if (!!localStorage[key]) hasLocalStorage = true; - } - - for (key in boolkeys) { - if (!!localStorage[key]) hasLocalStorage = true; - } - - for (key in jsonkeys) { - if (!!localStorage[key]) hasLocalStorage = true; - } - - if (!hasLocalStorage) { - _context.next = 34; - break; - } - - keyValue = {}; - _context.next = 8; - return browser.storage.local.get(null); - - case 8: - values = _context.sent; - - values = values || {}; - _context.t0 = regeneratorRuntime.keys(values); - - case 11: - if ((_context.t1 = _context.t0()).done) { - _context.next = 21; - break; - } - - key = _context.t1.value; - - if (!values[key].tabs) { - _context.next = 18; - break; - } - - _context.next = 16; - return browser.storage.local.remove(key); - - case 16: - _context.next = 19; - break; - - case 18: - delete values[key]; - - case 19: - _context.next = 11; - break; - - case 21: - keyValue = {}; - - keyValue["sessions"] = values; - _context.next = 25; - return browser.storage.local.set(keyValue); - - case 25: - keyValue = {}; - - for (key in stringkeys) { - if (!!localStorage[key]) keyValue[key] = localStorage[key]; - } - - for (key in boolkeys) { - if (!!localStorage[key]) keyValue[key] = toBoolean(localStorage[key]); - } - - for (key in jsonkeys) { - if (!!localStorage[key]) keyValue[key] = JSON.parse(localStorage[key]); - } - - _context.next = 31; - return browser.storage.local.set(keyValue); - - case 31: - for (key in stringkeys) { - localStorage.removeItem(key); - }for (key in boolkeys) { - localStorage.removeItem(key); - }for (key in jsonkeys) { - localStorage.removeItem(key); - } - case 34: - case "end": - return _context.stop(); - } - } - }, _callee, this); -}))(); - -function toBoolean(str) { - if (typeof str === "undefined" || str === null) { - return false; - } else if (typeof str === "string") { - switch (str.toLowerCase()) { - case "false": - case "no": - case "0": - case "": - return false; - default: - return true; - } - } else if (typeof str === "number") { - return str !== 0; - } else { - return true; - } -} -//# sourceMappingURL=migrate.js.map \ No newline at end of file diff --git a/outlib/helpers/migrate.js.map b/outlib/helpers/migrate.js.map deleted file mode 100644 index 294b3b31..00000000 --- a/outlib/helpers/migrate.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../lib/helpers/migrate.js"],"names":["browser","chrome","stringkeys","jsonkeys","boolkeys","hasLocalStorage","key","localStorage","keyValue","storage","local","get","values","tabs","remove","set","toBoolean","JSON","parse","removeItem","str","toLowerCase"],"mappings":"AAAC;;;;AAED,IAAIA,UAAUA,WAAWC,MAAzB;;AAEA,IAAIC,aAAa,CAChB,QADgB,CAAjB;;AAIA,IAAIC,WAAW,CACd,UADc,EAEd,UAFc,EAGd,WAHc,EAId,WAJc,EAKd,aALc,EAMd,cANc,CAAf;;AASA,IAAIC,WAAW,CACd,cADc,EAEd,YAFc,EAGd,cAHc,EAId,SAJc,EAKd,MALc,EAMd,YANc,EAOd,OAPc,EAQd,iBARc,EASd,aATc,EAUd,aAVc,CAAf;;AAaA,0CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAEIC,oBAFJ,GAEsB,KAFtB;;;AAIA,UAASC,GAAT,IAAgBJ,UAAhB,EAA4B;AAC3B,UAAI,CAAC,CAACK,aAAaD,GAAb,CAAN,EAAyBD,kBAAkB,IAAlB;AACzB;;AAED,UAASC,GAAT,IAAgBF,QAAhB,EAA0B;AACzB,UAAI,CAAC,CAACG,aAAaD,GAAb,CAAN,EAAyBD,kBAAkB,IAAlB;AACzB;;AAED,UAASC,GAAT,IAAgBH,QAAhB,EAA0B;AACzB,UAAI,CAAC,CAACI,aAAaD,GAAb,CAAN,EAAyBD,kBAAkB,IAAlB;AACzB;;AAdD,UAgBIA,eAhBJ;AAAA;AAAA;AAAA;;AAiBKG,aAjBL,GAiBgB,EAjBhB;AAAA;AAAA,YAmBoBR,QAAQS,OAAR,CAAgBC,KAAhB,CAAsBC,GAAtB,CAA0B,IAA1B,CAnBpB;;AAAA;AAmBKC,WAnBL;;AAoBCA,cAASA,UAAU,EAAnB;AApBD,2CAsBiBA,MAtBjB;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAsBUN,QAtBV;;AAAA,SAuBO,CAACM,OAAON,GAAP,EAAYO,IAvBpB;AAAA;AAAA;AAAA;;AAAA;AAAA,YAwBSb,QAAQS,OAAR,CAAgBC,KAAhB,CAAsBI,MAAtB,CAA6BR,GAA7B,CAxBT;;AAAA;AAAA;AAAA;;AAAA;AA0BG,YAAOM,OAAON,GAAP,CAAP;;AA1BH;AAAA;AAAA;;AAAA;AA6BKE,aA7BL,GA6BgB,EA7BhB;;AA8BCA,cAAS,UAAT,IAAuBI,MAAvB;AA9BD;AAAA,YA+BOZ,QAAQS,OAAR,CAAgBC,KAAhB,CAAsBK,GAAtB,CAA0BP,QAA1B,CA/BP;;AAAA;AAiCKA,aAjCL,GAiCgB,EAjChB;;AAkCC,UAASF,GAAT,IAAgBJ,UAAhB,EAA4B;AAC3B,UAAI,CAAC,CAACK,aAAaD,GAAb,CAAN,EAAyBE,SAASF,GAAT,IAAgBC,aAAaD,GAAb,CAAhB;AACzB;;AAED,UAASA,GAAT,IAAgBF,QAAhB,EAA0B;AACzB,UAAI,CAAC,CAACG,aAAaD,GAAb,CAAN,EAAyBE,SAASF,GAAT,IAAgBU,UAAUT,aAAaD,GAAb,CAAV,CAAhB;AACzB;;AAED,UAASA,GAAT,IAAgBH,QAAhB,EAA0B;AACzB,UAAI,CAAC,CAACI,aAAaD,GAAb,CAAN,EAAyBE,SAASF,GAAT,IAAgBW,KAAKC,KAAL,CAAWX,aAAaD,GAAb,CAAX,CAAhB;AACzB;;AA5CF;AAAA,YA8CON,QAAQS,OAAR,CAAgBC,KAAhB,CAAsBK,GAAtB,CAA0BP,QAA1B,CA9CP;;AAAA;AAiDC,UAASF,GAAT,IAAgBJ,UAAhB;AAA4BK,mBAAaY,UAAb,CAAwBb,GAAxB;AAA5B,MACA,KAASA,GAAT,IAAgBF,QAAhB;AAA0BG,mBAAaY,UAAb,CAAwBb,GAAxB;AAA1B,MACA,KAASA,GAAT,IAAgBH,QAAhB;AAA0BI,mBAAaY,UAAb,CAAwBb,GAAxB;AAA1B;AAnDD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAD;;AAwDA,SAASU,SAAT,CAAmBI,GAAnB,EACA;AACC,KAAI,OAAOA,GAAP,KAAe,WAAf,IAA8BA,QAAQ,IAA1C,EAAgD;AAC/C,SAAO,KAAP;AACA,EAFD,MAEO,IAAI,OAAOA,GAAP,KAAe,QAAnB,EAA6B;AACnC,UAAQA,IAAIC,WAAJ,EAAR;AACC,QAAK,OAAL;AACA,QAAK,IAAL;AACA,QAAK,GAAL;AACA,QAAK,EAAL;AACC,WAAO,KAAP;AACD;AACC,WAAO,IAAP;AAPF;AASA,EAVM,MAUA,IAAI,OAAOD,GAAP,KAAe,QAAnB,EAA6B;AACnC,SAAOA,QAAQ,CAAf;AACA,EAFM,MAEA;AACN,SAAO,IAAP;AACA;AACD","file":"migrate.js","sourcesContent":["\"use strict\";\r\n\r\nvar browser = browser || chrome;\r\n\r\nvar stringkeys = [\r\n\t\"layout\"\r\n];\r\n\r\nvar jsonkeys = [\r\n\t\"tabLimit\",\r\n\t\"tabWidth\",\r\n\t\"tabHeight\",\r\n\t\"windowAge\",\r\n\t\"windowNames\",\r\n\t\"windowColors\"\r\n];\r\n\r\nvar boolkeys = [\r\n\t\"openInOwnTab\",\r\n\t\"animations\",\r\n\t\"windowTitles\",\r\n\t\"compact\",\r\n\t\"dark\",\r\n\t\"tabactions\",\r\n\t\"badge\",\r\n\t\"sessionsFeature\",\r\n\t\"hideWindows\",\r\n\t\"filter-tabs\"\r\n];\r\n\r\n(async function () {\r\n\r\n\tvar hasLocalStorage = false;\r\n\r\n\tfor (var key in stringkeys) {\r\n\t\tif (!!localStorage[key]) hasLocalStorage = true;\r\n\t}\r\n\r\n\tfor (var key in boolkeys) {\r\n\t\tif (!!localStorage[key]) hasLocalStorage = true;\r\n\t}\r\n\r\n\tfor (var key in jsonkeys) {\r\n\t\tif (!!localStorage[key]) hasLocalStorage = true;\r\n\t}\r\n\r\n\tif (hasLocalStorage) {\r\n\t\tvar keyValue = {};\r\n\r\n\t\tvar values = await browser.storage.local.get(null);\r\n\t\tvalues = values || {};\r\n\t\t// delete all values that don't have a tabs array\r\n\t\tfor (var key in values) {\r\n\t\t\tif (!!values[key].tabs) {\r\n\t\t\t\tawait browser.storage.local.remove(key);\r\n\t\t\t} else {\r\n\t\t\t\tdelete values[key];\r\n\t\t\t}\r\n\t\t}\r\n\t\tvar keyValue = {};\r\n\t\tkeyValue[\"sessions\"] = values;\r\n\t\tawait browser.storage.local.set(keyValue);\r\n\r\n\t\tvar keyValue = {};\r\n\t\tfor (var key in stringkeys) {\r\n\t\t\tif (!!localStorage[key]) keyValue[key] = localStorage[key];\r\n\t\t}\r\n\r\n\t\tfor (var key in boolkeys) {\r\n\t\t\tif (!!localStorage[key]) keyValue[key] = toBoolean(localStorage[key]);\r\n\t\t}\r\n\r\n\t\tfor (var key in jsonkeys) {\r\n\t\t\tif (!!localStorage[key]) keyValue[key] = JSON.parse(localStorage[key]);\r\n\t\t}\r\n\r\n\t\tawait browser.storage.local.set(keyValue);\r\n\r\n\t\t// clear old localstorage\r\n\t\tfor (var key in stringkeys) localStorage.removeItem(key);\r\n\t\tfor (var key in boolkeys) localStorage.removeItem(key);\r\n\t\tfor (var key in jsonkeys) localStorage.removeItem(key);\r\n\r\n\t}\r\n})();\r\n\r\nfunction toBoolean(str)\r\n{\r\n\tif (typeof str === \"undefined\" || str === null) {\r\n\t\treturn false;\r\n\t} else if (typeof str === \"string\") {\r\n\t\tswitch (str.toLowerCase()) {\r\n\t\t\tcase \"false\":\r\n\t\t\tcase \"no\":\r\n\t\t\tcase \"0\":\r\n\t\t\tcase \"\":\r\n\t\t\t\treturn false;\r\n\t\t\tdefault:\r\n\t\t\t\treturn true;\r\n\t\t}\r\n\t} else if (typeof str === \"number\") {\r\n\t\treturn str !== 0;\r\n\t} else {\r\n\t\treturn true;\r\n\t}\r\n}"]} \ No newline at end of file diff --git a/outlib/helpers/storage.js b/outlib/helpers/storage.js deleted file mode 100644 index c16a6e9a..00000000 --- a/outlib/helpers/storage.js +++ /dev/null @@ -1,82 +0,0 @@ -"use strict"; - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - -var browser = browser || chrome; - -var getLocalStorage = function () { - var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(key) { - var default_value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - return _context.abrupt("return", new Promise(function (resolve, reject) { - browser.storage.local.get([key]).then(function (result) { - if (result[key] === undefined) { - resolve(default_value); - } else { - resolve(result[key]); - } - }); - })); - - case 1: - case "end": - return _context.stop(); - } - } - }, _callee, undefined); - })); - - return function getLocalStorage(_x) { - return _ref.apply(this, arguments); - }; -}(); - -var setLocalStorage = function () { - var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(key, value) { - var obj; - return regeneratorRuntime.wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - obj = {}; - - obj[key] = value; - return _context2.abrupt("return", browser.storage.local.set(obj)); - - case 3: - case "end": - return _context2.stop(); - } - } - }, _callee2, undefined); - })); - - return function setLocalStorage(_x3, _x4) { - return _ref2.apply(this, arguments); - }; -}(); - -var removeLocalStorage = function () { - var _ref3 = _asyncToGenerator(regeneratorRuntime.mark(function _callee3(key) { - return regeneratorRuntime.wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - return _context3.abrupt("return", browser.storage.local.remove(key)); - - case 1: - case "end": - return _context3.stop(); - } - } - }, _callee3, undefined); - })); - - return function removeLocalStorage(_x5) { - return _ref3.apply(this, arguments); - }; -}(); -//# sourceMappingURL=storage.js.map \ No newline at end of file diff --git a/outlib/helpers/storage.js.map b/outlib/helpers/storage.js.map deleted file mode 100644 index 101b3527..00000000 --- a/outlib/helpers/storage.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../lib/helpers/storage.js"],"names":["browser","chrome","getLocalStorage","key","default_value","Promise","resolve","reject","storage","local","get","then","result","undefined","setLocalStorage","value","obj","set","removeLocalStorage","remove"],"mappings":";;;;AAAC,IAAIA,UAAUA,WAAWC,MAAzB;;AAED,IAAMC;AAAA,sDAAkB,iBAAOC,GAAP;AAAA,MAAYC,aAAZ,uEAA4B,IAA5B;AAAA;AAAA;AAAA;AAAA;AAAA,uCAChB,IAAIC,OAAJ,CAAY,UAACC,OAAD,EAAUC,MAAV,EAAqB;AACvCP,eAAQQ,OAAR,CAAgBC,KAAhB,CAAsBC,GAAtB,CAA0B,CAACP,GAAD,CAA1B,EAAiCQ,IAAjC,CAAsC,kBAAU;AAC/C,YAAIC,OAAOT,GAAP,MAAgBU,SAApB,EAA+B;AAC9BP,iBAAQF,aAAR;AACA,SAFD,MAEO;AACNE,iBAAQM,OAAOT,GAAP,CAAR;AACA;AACD,QAND;AAOA,OARM,CADgB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAlB;;AAAA;AAAA;AAAA;AAAA,GAAN;;AAYA,IAAMW;AAAA,uDAAkB,kBAAOX,GAAP,EAAYY,KAAZ;AAAA;AAAA;AAAA;AAAA;AAAA;AACnBC,SADmB,GACb,EADa;;AAEvBA,UAAIb,GAAJ,IAAWY,KAAX;AAFuB,wCAGhBf,QAAQQ,OAAR,CAAgBC,KAAhB,CAAsBQ,GAAtB,CAA0BD,GAA1B,CAHgB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAlB;;AAAA;AAAA;AAAA;AAAA,GAAN;;AAMA,IAAME;AAAA,uDAAqB,kBAAOf,GAAP;AAAA;AAAA;AAAA;AAAA;AAAA,wCACnBH,QAAQQ,OAAR,CAAgBC,KAAhB,CAAsBU,MAAtB,CAA6BhB,GAA7B,CADmB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAArB;;AAAA;AAAA;AAAA;AAAA,GAAN","file":"storage.js","sourcesContent":["var browser = browser || chrome;\n\nconst getLocalStorage = async (key, default_value = null) => {\n\treturn new Promise((resolve, reject) => {\n\t\tbrowser.storage.local.get([key]).then(result => {\n\t\t\tif (result[key] === undefined) {\n\t\t\t\tresolve(default_value);\n\t\t\t} else {\n\t\t\t\tresolve(result[key]);\n\t\t\t}\n\t\t});\n\t});\n};\n\nconst setLocalStorage = async (key, value) => {\n\tvar obj = {};\n\tobj[key] = value;\n\treturn browser.storage.local.set(obj);\n};\n\nconst removeLocalStorage = async (key) => {\n\treturn browser.storage.local.remove(key);\n};"]} \ No newline at end of file diff --git a/outlib/helpers/utils.js b/outlib/helpers/utils.js deleted file mode 100644 index 57decf5c..00000000 --- a/outlib/helpers/utils.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; - -function debounce(func, wait, immediate) { - var timeout; - return function () { - var context = this, - args = arguments; - var later = function later() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} - -function is_in_bounds(object, bounds) { - var C = object, - B = bounds; - if (C.left >= B.left && C.left <= B.left + B.width) { - if (C.top >= B.top && C.top <= B.top + B.height) { - return true; - } - } - return false; -} - -function stringHashcode(string) { - var hash = 0; - for (var i = 0; i < string.length; i++) { - var code = string.charCodeAt(i); - hash = (hash << 5) - hash + code; - hash = hash & hash; - } - return hash; -} -//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/outlib/helpers/utils.js.map b/outlib/helpers/utils.js.map deleted file mode 100644 index 3a96e405..00000000 --- a/outlib/helpers/utils.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../lib/helpers/utils.js"],"names":["debounce","func","wait","immediate","timeout","context","args","arguments","later","apply","callNow","clearTimeout","setTimeout","is_in_bounds","object","bounds","C","B","left","width","top","height","stringHashcode","string","hash","i","length","code","charCodeAt"],"mappings":";;AAIA,SAASA,QAAT,CAAkBC,IAAlB,EAAwBC,IAAxB,EAA8BC,SAA9B,EAAyC;AACxC,KAAIC,OAAJ;AACA,QAAO,YAAY;AAClB,MAAIC,UAAU,IAAd;AAAA,MAAoBC,OAAOC,SAA3B;AACA,MAAIC,QAAQ,SAASA,KAAT,GAAiB;AAC5BJ,aAAU,IAAV;AACA,OAAI,CAACD,SAAL,EAAgBF,KAAKQ,KAAL,CAAWJ,OAAX,EAAoBC,IAApB;AAChB,GAHD;AAIA,MAAII,UAAUP,aAAa,CAACC,OAA5B;AACAO,eAAaP,OAAb;AACAA,YAAUQ,WAAWJ,KAAX,EAAkBN,IAAlB,CAAV;AACA,MAAIQ,OAAJ,EAAaT,KAAKQ,KAAL,CAAWJ,OAAX,EAAoBC,IAApB;AACb,EAVD;AAWA;;AAED,SAASO,YAAT,CAAsBC,MAAtB,EAA8BC,MAA9B,EAAsC;AACrC,KAAIC,IAAIF,MAAR;AAAA,KAAgBG,IAAIF,MAApB;AACA,KAAIC,EAAEE,IAAF,IAAUD,EAAEC,IAAZ,IAAoBF,EAAEE,IAAF,IAAUD,EAAEC,IAAF,GAASD,EAAEE,KAA7C,EAAoD;AACnD,MAAIH,EAAEI,GAAF,IAASH,EAAEG,GAAX,IAAkBJ,EAAEI,GAAF,IAASH,EAAEG,GAAF,GAAQH,EAAEI,MAAzC,EAAiD;AAChD,UAAO,IAAP;AACA;AACD;AACD,QAAO,KAAP;AACA;;AAED,SAASC,cAAT,CAAwBC,MAAxB,EAAgC;AAC/B,KAAIC,OAAO,CAAX;AACA,MAAK,IAAIC,IAAI,CAAb,EAAgBA,IAAIF,OAAOG,MAA3B,EAAmCD,GAAnC,EAAwC;AACvC,MAAIE,OAAOJ,OAAOK,UAAP,CAAkBH,CAAlB,CAAX;AACAD,SAAQ,CAACA,QAAQ,CAAT,IAAcA,IAAf,GAAuBG,IAA9B;AACAH,SAAOA,OAAOA,IAAd;AACA;AACD,QAAOA,IAAP;AACA","file":"utils.js","sourcesContent":["// Returns a function, that, as long as it continues to be invoked, will not\r\n// be triggered. The function will be called after it stops being called for\r\n// N milliseconds. If `immediate` is passed, trigger the function on the\r\n// leading edge, instead of the trailing.\r\nfunction debounce(func, wait, immediate) {\r\n\tvar timeout;\r\n\treturn function () {\r\n\t\tvar context = this, args = arguments;\r\n\t\tvar later = function later() {\r\n\t\t\ttimeout = null;\r\n\t\t\tif (!immediate) func.apply(context, args);\r\n\t\t};\r\n\t\tvar callNow = immediate && !timeout;\r\n\t\tclearTimeout(timeout);\r\n\t\ttimeout = setTimeout(later, wait);\r\n\t\tif (callNow) func.apply(context, args);\r\n\t}\r\n}\r\n\r\nfunction is_in_bounds(object, bounds) {\r\n\tvar C = object, B = bounds;\r\n\tif (C.left >= B.left && C.left <= B.left + B.width) {\r\n\t\tif (C.top >= B.top && C.top <= B.top + B.height) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nfunction stringHashcode(string) {\r\n\tvar hash = 0;\r\n\tfor (var i = 0; i < string.length; i++) {\r\n\t\tvar code = string.charCodeAt(i);\r\n\t\thash = ((hash << 5) - hash) + code;\r\n\t\thash = hash & hash; // Convert to 32bit integer\r\n\t}\r\n\treturn hash;\r\n}"]} \ No newline at end of file diff --git a/outlib/popup/changelog.js b/outlib/popup/changelog.js deleted file mode 100644 index 837e48bb..00000000 --- a/outlib/popup/changelog.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict"; - -window.changelogPage = true; -//# sourceMappingURL=changelog.js.map \ No newline at end of file diff --git a/outlib/popup/changelog.js.map b/outlib/popup/changelog.js.map deleted file mode 100644 index 4d9ae789..00000000 --- a/outlib/popup/changelog.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../lib/popup/changelog.js"],"names":["window","changelogPage"],"mappings":"AAAA;;AACAA,OAAOC,aAAP,GAAuB,IAAvB","file":"changelog.js","sourcesContent":["\"use strict\";\nwindow.changelogPage = true;"]} \ No newline at end of file diff --git a/outlib/popup/options.js b/outlib/popup/options.js index 1f343405..8749a8a9 100644 --- a/outlib/popup/options.js +++ b/outlib/popup/options.js @@ -1,4 +1,6 @@ "use strict"; - -window.optionPage = true; -//# sourceMappingURL=options.js.map \ No newline at end of file +(() => { + // lib/popup/options.js + window.optionPage = true; +})(); +//# sourceMappingURL=options.js.map diff --git a/outlib/popup/options.js.map b/outlib/popup/options.js.map index bb2b744a..845b9a42 100644 --- a/outlib/popup/options.js.map +++ b/outlib/popup/options.js.map @@ -1 +1,7 @@ -{"version":3,"sources":["../../lib/popup/options.js"],"names":["window","optionPage"],"mappings":"AAAA;;AACAA,OAAOC,UAAP,GAAoB,IAApB","file":"options.js","sourcesContent":["\"use strict\";\nwindow.optionPage = true;"]} \ No newline at end of file +{ + "version": 3, + "sources": ["../../lib/popup/options.js"], + "sourcesContent": ["\"use strict\";\nwindow.optionPage = true;"], + "mappings": ";;;AACA,SAAO,aAAa;", + "names": [] +} diff --git a/outlib/popup/popup.js b/outlib/popup/popup.js index 2225b704..d00415a9 100644 --- a/outlib/popup/popup.js +++ b/outlib/popup/popup.js @@ -1,123 +1,26472 @@ "use strict"; +(() => { + var __create = Object.create; + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getProtoOf = Object.getPrototypeOf; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; + }; + var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod + )); -var loadApp = function () { - var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() { - var height, width, root; - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - if (!window.loaded) { - _context.next = 2; - break; - } - - return _context.abrupt("return"); - - case 2: - _context.next = 4; - return getLocalStorage("tabHeight", 0); - - case 4: - height = _context.sent; - _context.next = 7; - return getLocalStorage("tabWidth", 0); - - case 7: - width = _context.sent; - - console.log(height, width); - if (window.inPopup) { - - if (height > 0 && width > 0) { - document.body.style.width = width + "px"; - document.body.style.height = height + "px"; - } - - root = document.getElementById("root"); - - if (root != null) { - height = document.body.style.height.split("px")[0]; - - height = parseInt(height) || 0; - if (height < 300) { - height = 400; - document.body.style.minHeight = height + "px"; - } else { - height++; - if (height > 600) height = 600; - document.body.style.minHeight = height + "px"; - } - } - } else { - if (window.inPanel) { - document.documentElement.style.maxHeight = "auto"; - document.documentElement.style.maxWidth = "auto"; - document.body.style.maxHeight = "auto"; - document.body.style.maxWidth = "auto"; - } - document.documentElement.style.maxHeight = "100%"; - document.documentElement.style.maxWidth = "100%"; - document.documentElement.style.height = "100%"; - document.documentElement.style.width = "100%"; - document.body.style.maxHeight = "100%"; - document.body.style.maxWidth = "100%"; - document.body.style.height = "100%"; - document.body.style.width = "100%"; - } - - if (!window.loaded) { - _context.next = 12; - break; - } - - return _context.abrupt("return"); - - case 12: - window.loaded = true; - ReactDOM.render(React.createElement(TabManager, { optionsActive: !!window.optionPage }), document.getElementById("TMP")); - - case 14: - case "end": - return _context.stop(); - } - } - }, _callee, this); - })); - - return function loadApp() { - return _ref.apply(this, arguments); - }; -}(); - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - -window.loaded = false; -if (window.location.search.indexOf("?popup") > -1) { - window.inPopup = true; -} else { - window.inPopup = false; -} -if (window.location.search.indexOf("?panel") > -1) { - window.inPanel = true; -} else { - window.inPanel = false; -} -window.onload = function () { - window.requestAnimationFrame(loadApp); -}; -setTimeout(loadApp, 75); -setTimeout(loadApp, 125); -setTimeout(loadApp, 250); -setTimeout(loadApp, 375); -setTimeout(loadApp, 700); -setTimeout(loadApp, 1000); -setTimeout(loadApp, 2000); -setTimeout(loadApp, 3000); -setTimeout(loadApp, 5000); -setTimeout(loadApp, 15000); - -window.addEventListener("contextmenu", function (e) { - e.preventDefault(); -}); -//# sourceMappingURL=popup.js.map \ No newline at end of file + // node_modules/webextension-polyfill/dist/browser-polyfill.js + var require_browser_polyfill = __commonJS({ + "node_modules/webextension-polyfill/dist/browser-polyfill.js"(exports, module) { + (function(global, factory) { + if (typeof define === "function" && define.amd) { + define("webextension-polyfill", ["module"], factory); + } else if (typeof exports !== "undefined") { + factory(module); + } else { + var mod = { + exports: {} + }; + factory(mod); + global.browser = mod.exports; + } + })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : exports, function(module2) { + "use strict"; + if (!(globalThis.chrome && globalThis.chrome.runtime && globalThis.chrome.runtime.id)) { + throw new Error("This script should only be loaded in a browser extension."); + } + if (!(globalThis.browser && globalThis.browser.runtime && globalThis.browser.runtime.id)) { + const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; + const wrapAPIs = (extensionAPIs) => { + const apiMetadata = { + "alarms": { + "clear": { + "minArgs": 0, + "maxArgs": 1 + }, + "clearAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "bookmarks": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getChildren": { + "minArgs": 1, + "maxArgs": 1 + }, + "getRecent": { + "minArgs": 1, + "maxArgs": 1 + }, + "getSubTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTree": { + "minArgs": 0, + "maxArgs": 0 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "browserAction": { + "disable": { + "minArgs": 0, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "enable": { + "minArgs": 0, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "getBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1 + }, + "getBadgeText": { + "minArgs": 1, + "maxArgs": 1 + }, + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "openPopup": { + "minArgs": 0, + "maxArgs": 0 + }, + "setBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "setBadgeText": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "setPopup": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "setTitle": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + } + }, + "browsingData": { + "remove": { + "minArgs": 2, + "maxArgs": 2 + }, + "removeCache": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCookies": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeDownloads": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFormData": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeHistory": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeLocalStorage": { + "minArgs": 1, + "maxArgs": 1 + }, + "removePasswords": { + "minArgs": 1, + "maxArgs": 1 + }, + "removePluginData": { + "minArgs": 1, + "maxArgs": 1 + }, + "settings": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "commands": { + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "contextMenus": { + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "cookies": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAllCookieStores": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "devtools": { + "inspectedWindow": { + "eval": { + "minArgs": 1, + "maxArgs": 2, + "singleCallbackArg": false + } + }, + "panels": { + "create": { + "minArgs": 3, + "maxArgs": 3, + "singleCallbackArg": true + }, + "elements": { + "createSidebarPane": { + "minArgs": 1, + "maxArgs": 1 + } + } + } + }, + "downloads": { + "cancel": { + "minArgs": 1, + "maxArgs": 1 + }, + "download": { + "minArgs": 1, + "maxArgs": 1 + }, + "erase": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFileIcon": { + "minArgs": 1, + "maxArgs": 2 + }, + "open": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "pause": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFile": { + "minArgs": 1, + "maxArgs": 1 + }, + "resume": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + } + }, + "extension": { + "isAllowedFileSchemeAccess": { + "minArgs": 0, + "maxArgs": 0 + }, + "isAllowedIncognitoAccess": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "history": { + "addUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "deleteRange": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "getVisits": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "i18n": { + "detectLanguage": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAcceptLanguages": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "identity": { + "launchWebAuthFlow": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "idle": { + "queryState": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "management": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSelf": { + "minArgs": 0, + "maxArgs": 0 + }, + "setEnabled": { + "minArgs": 2, + "maxArgs": 2 + }, + "uninstallSelf": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "notifications": { + "clear": { + "minArgs": 1, + "maxArgs": 1 + }, + "create": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPermissionLevel": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "pageAction": { + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "hide": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "setPopup": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "setTitle": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + }, + "show": { + "minArgs": 1, + "maxArgs": 1, + "fallbackToNoCallback": true + } + }, + "permissions": { + "contains": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "request": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "runtime": { + "getBackgroundPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPlatformInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "openOptionsPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "requestUpdateCheck": { + "minArgs": 0, + "maxArgs": 0 + }, + "sendMessage": { + "minArgs": 1, + "maxArgs": 3 + }, + "sendNativeMessage": { + "minArgs": 2, + "maxArgs": 2 + }, + "setUninstallURL": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "sessions": { + "getDevices": { + "minArgs": 0, + "maxArgs": 1 + }, + "getRecentlyClosed": { + "minArgs": 0, + "maxArgs": 1 + }, + "restore": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "storage": { + "local": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "managed": { + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "sync": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + } + }, + "tabs": { + "captureVisibleTab": { + "minArgs": 0, + "maxArgs": 2 + }, + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "detectLanguage": { + "minArgs": 0, + "maxArgs": 1 + }, + "discard": { + "minArgs": 0, + "maxArgs": 1 + }, + "duplicate": { + "minArgs": 1, + "maxArgs": 1 + }, + "executeScript": { + "minArgs": 1, + "maxArgs": 2 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 0 + }, + "getZoom": { + "minArgs": 0, + "maxArgs": 1 + }, + "getZoomSettings": { + "minArgs": 0, + "maxArgs": 1 + }, + "goBack": { + "minArgs": 0, + "maxArgs": 1 + }, + "goForward": { + "minArgs": 0, + "maxArgs": 1 + }, + "highlight": { + "minArgs": 1, + "maxArgs": 1 + }, + "insertCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "query": { + "minArgs": 1, + "maxArgs": 1 + }, + "reload": { + "minArgs": 0, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "sendMessage": { + "minArgs": 2, + "maxArgs": 3 + }, + "setZoom": { + "minArgs": 1, + "maxArgs": 2 + }, + "setZoomSettings": { + "minArgs": 1, + "maxArgs": 2 + }, + "update": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "topSites": { + "get": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "webNavigation": { + "getAllFrames": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFrame": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "webRequest": { + "handlerBehaviorChanged": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "windows": { + "create": { + "minArgs": 0, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 1 + }, + "getLastFocused": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + } + }; + if (Object.keys(apiMetadata).length === 0) { + throw new Error("api-metadata.json has not been included in browser-polyfill"); + } + class DefaultWeakMap extends WeakMap { + constructor(createItem, items = void 0) { + super(items); + this.createItem = createItem; + } + get(key) { + if (!this.has(key)) { + this.set(key, this.createItem(key)); + } + return super.get(key); + } + } + const isThenable = (value) => { + return value && typeof value === "object" && typeof value.then === "function"; + }; + const makeCallback = (promise, metadata) => { + return (...callbackArgs) => { + if (extensionAPIs.runtime.lastError) { + promise.reject(new Error(extensionAPIs.runtime.lastError.message)); + } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) { + promise.resolve(callbackArgs[0]); + } else { + promise.resolve(callbackArgs); + } + }; + }; + const pluralizeArguments = (numArgs) => numArgs == 1 ? "argument" : "arguments"; + const wrapAsyncFunction = (name, metadata) => { + return function asyncFunctionWrapper(target, ...args) { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + return new Promise((resolve, reject) => { + if (metadata.fallbackToNoCallback) { + try { + target[name](...args, makeCallback({ + resolve, + reject + }, metadata)); + } catch (cbError) { + console.warn(`${name} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `, cbError); + target[name](...args); + metadata.fallbackToNoCallback = false; + metadata.noCallback = true; + resolve(); + } + } else if (metadata.noCallback) { + target[name](...args); + resolve(); + } else { + target[name](...args, makeCallback({ + resolve, + reject + }, metadata)); + } + }); + }; + }; + const wrapMethod = (target, method, wrapper) => { + return new Proxy(method, { + apply(targetMethod, thisObj, args) { + return wrapper.call(thisObj, target, ...args); + } + }); + }; + let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + const wrapObject = (target, wrappers = {}, metadata = {}) => { + let cache = /* @__PURE__ */ Object.create(null); + let handlers = { + has(proxyTarget2, prop) { + return prop in target || prop in cache; + }, + get(proxyTarget2, prop, receiver) { + if (prop in cache) { + return cache[prop]; + } + if (!(prop in target)) { + return void 0; + } + let value = target[prop]; + if (typeof value === "function") { + if (typeof wrappers[prop] === "function") { + value = wrapMethod(target, target[prop], wrappers[prop]); + } else if (hasOwnProperty(metadata, prop)) { + let wrapper = wrapAsyncFunction(prop, metadata[prop]); + value = wrapMethod(target, target[prop], wrapper); + } else { + value = value.bind(target); + } + } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) { + value = wrapObject(value, wrappers[prop], metadata[prop]); + } else if (hasOwnProperty(metadata, "*")) { + value = wrapObject(value, wrappers[prop], metadata["*"]); + } else { + Object.defineProperty(cache, prop, { + configurable: true, + enumerable: true, + get() { + return target[prop]; + }, + set(value2) { + target[prop] = value2; + } + }); + return value; + } + cache[prop] = value; + return value; + }, + set(proxyTarget2, prop, value, receiver) { + if (prop in cache) { + cache[prop] = value; + } else { + target[prop] = value; + } + return true; + }, + defineProperty(proxyTarget2, prop, desc) { + return Reflect.defineProperty(cache, prop, desc); + }, + deleteProperty(proxyTarget2, prop) { + return Reflect.deleteProperty(cache, prop); + } + }; + let proxyTarget = Object.create(target); + return new Proxy(proxyTarget, handlers); + }; + const wrapEvent = (wrapperMap) => ({ + addListener(target, listener, ...args) { + target.addListener(wrapperMap.get(listener), ...args); + }, + hasListener(target, listener) { + return target.hasListener(wrapperMap.get(listener)); + }, + removeListener(target, listener) { + target.removeListener(wrapperMap.get(listener)); + } + }); + const onRequestFinishedWrappers = new DefaultWeakMap((listener) => { + if (typeof listener !== "function") { + return listener; + } + return function onRequestFinished(req) { + const wrappedReq = wrapObject(req, {}, { + getContent: { + minArgs: 0, + maxArgs: 0 + } + }); + listener(wrappedReq); + }; + }); + const onMessageWrappers = new DefaultWeakMap((listener) => { + if (typeof listener !== "function") { + return listener; + } + return function onMessage(message, sender, sendResponse) { + let didCallSendResponse = false; + let wrappedSendResponse; + let sendResponsePromise = new Promise((resolve) => { + wrappedSendResponse = function(response) { + didCallSendResponse = true; + resolve(response); + }; + }); + let result; + try { + result = listener(message, sender, wrappedSendResponse); + } catch (err) { + result = Promise.reject(err); + } + const isResultThenable = result !== true && isThenable(result); + if (result !== true && !isResultThenable && !didCallSendResponse) { + return false; + } + const sendPromisedResult = (promise) => { + promise.then((msg) => { + sendResponse(msg); + }, (error) => { + let message2; + if (error && (error instanceof Error || typeof error.message === "string")) { + message2 = error.message; + } else { + message2 = "An unexpected error occurred"; + } + sendResponse({ + __mozWebExtensionPolyfillReject__: true, + message: message2 + }); + }).catch((err) => { + console.error("Failed to send onMessage rejected reply", err); + }); + }; + if (isResultThenable) { + sendPromisedResult(result); + } else { + sendPromisedResult(sendResponsePromise); + } + return true; + }; + }); + const wrappedSendMessageCallback = ({ + reject, + resolve + }, reply) => { + if (extensionAPIs.runtime.lastError) { + if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) { + resolve(); + } else { + reject(new Error(extensionAPIs.runtime.lastError.message)); + } + } else if (reply && reply.__mozWebExtensionPolyfillReject__) { + reject(new Error(reply.message)); + } else { + resolve(reply); + } + }; + const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + return new Promise((resolve, reject) => { + const wrappedCb = wrappedSendMessageCallback.bind(null, { + resolve, + reject + }); + args.push(wrappedCb); + apiNamespaceObj.sendMessage(...args); + }); + }; + const staticWrappers = { + devtools: { + network: { + onRequestFinished: wrapEvent(onRequestFinishedWrappers) + } + }, + runtime: { + onMessage: wrapEvent(onMessageWrappers), + onMessageExternal: wrapEvent(onMessageWrappers), + sendMessage: wrappedSendMessage.bind(null, "sendMessage", { + minArgs: 1, + maxArgs: 3 + }) + }, + tabs: { + sendMessage: wrappedSendMessage.bind(null, "sendMessage", { + minArgs: 2, + maxArgs: 3 + }) + } + }; + const settingMetadata = { + clear: { + minArgs: 1, + maxArgs: 1 + }, + get: { + minArgs: 1, + maxArgs: 1 + }, + set: { + minArgs: 1, + maxArgs: 1 + } + }; + apiMetadata.privacy = { + network: { + "*": settingMetadata + }, + services: { + "*": settingMetadata + }, + websites: { + "*": settingMetadata + } + }; + return wrapObject(extensionAPIs, staticWrappers, apiMetadata); + }; + module2.exports = wrapAPIs(chrome); + } else { + module2.exports = globalThis.browser; + } + }); + } + }); + + // node_modules/object-assign/index.js + var require_object_assign = __commonJS({ + "node_modules/object-assign/index.js"(exports, module) { + "use strict"; + var getOwnPropertySymbols = Object.getOwnPropertySymbols; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var propIsEnumerable = Object.prototype.propertyIsEnumerable; + function toObject(val) { + if (val === null || val === void 0) { + throw new TypeError("Object.assign cannot be called with null or undefined"); + } + return Object(val); + } + function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } + var test1 = new String("abc"); + test1[5] = "de"; + if (Object.getOwnPropertyNames(test1)[0] === "5") { + return false; + } + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2["_" + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function(n) { + return test2[n]; + }); + if (order2.join("") !== "0123456789") { + return false; + } + var test3 = {}; + "abcdefghijklmnopqrst".split("").forEach(function(letter) { + test3[letter] = letter; + }); + if (Object.keys(Object.assign({}, test3)).join("") !== "abcdefghijklmnopqrst") { + return false; + } + return true; + } catch (err) { + return false; + } + } + module.exports = shouldUseNative() ? Object.assign : function(target, source) { + var from; + var to = toObject(target); + var symbols; + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + return to; + }; + } + }); + + // node_modules/prop-types/lib/ReactPropTypesSecret.js + var require_ReactPropTypesSecret = __commonJS({ + "node_modules/prop-types/lib/ReactPropTypesSecret.js"(exports, module) { + "use strict"; + var ReactPropTypesSecret = "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"; + module.exports = ReactPropTypesSecret; + } + }); + + // node_modules/prop-types/lib/has.js + var require_has = __commonJS({ + "node_modules/prop-types/lib/has.js"(exports, module) { + module.exports = Function.call.bind(Object.prototype.hasOwnProperty); + } + }); + + // node_modules/prop-types/checkPropTypes.js + var require_checkPropTypes = __commonJS({ + "node_modules/prop-types/checkPropTypes.js"(exports, module) { + "use strict"; + var printWarning = function() { + }; + if (true) { + ReactPropTypesSecret = require_ReactPropTypesSecret(); + loggedTypeFailures = {}; + has = require_has(); + printWarning = function(text) { + var message = "Warning: " + text; + if (typeof console !== "undefined") { + console.error(message); + } + try { + throw new Error(message); + } catch (x) { + } + }; + } + var ReactPropTypesSecret; + var loggedTypeFailures; + var has; + function checkPropTypes(typeSpecs, values, location, componentName, getStack) { + if (true) { + for (var typeSpecName in typeSpecs) { + if (has(typeSpecs, typeSpecName)) { + var error; + try { + if (typeof typeSpecs[typeSpecName] !== "function") { + var err = Error( + (componentName || "React class") + ": " + location + " type `" + typeSpecName + "` is invalid; it must be a function, usually from the `prop-types` package, but received `" + typeof typeSpecs[typeSpecName] + "`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`." + ); + err.name = "Invariant Violation"; + throw err; + } + error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); + } catch (ex) { + error = ex; + } + if (error && !(error instanceof Error)) { + printWarning( + (componentName || "React class") + ": type specification of " + location + " `" + typeSpecName + "` is invalid; the type checker function must return `null` or an `Error` but returned a " + typeof error + ". You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument)." + ); + } + if (error instanceof Error && !(error.message in loggedTypeFailures)) { + loggedTypeFailures[error.message] = true; + var stack = getStack ? getStack() : ""; + printWarning( + "Failed " + location + " type: " + error.message + (stack != null ? stack : "") + ); + } + } + } + } + } + checkPropTypes.resetWarningCache = function() { + if (true) { + loggedTypeFailures = {}; + } + }; + module.exports = checkPropTypes; + } + }); + + // node_modules/react/cjs/react.development.js + var require_react_development = __commonJS({ + "node_modules/react/cjs/react.development.js"(exports, module) { + "use strict"; + if (true) { + (function() { + "use strict"; + var _assign = require_object_assign(); + var checkPropTypes = require_checkPropTypes(); + var ReactVersion = "16.11.0"; + var hasSymbol = typeof Symbol === "function" && Symbol.for; + var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for("react.element") : 60103; + var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for("react.portal") : 60106; + var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for("react.fragment") : 60107; + var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for("react.strict_mode") : 60108; + var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for("react.profiler") : 60114; + var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for("react.provider") : 60109; + var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for("react.context") : 60110; + var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for("react.concurrent_mode") : 60111; + var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for("react.forward_ref") : 60112; + var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for("react.suspense") : 60113; + var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for("react.suspense_list") : 60120; + var REACT_MEMO_TYPE = hasSymbol ? Symbol.for("react.memo") : 60115; + var REACT_LAZY_TYPE = hasSymbol ? Symbol.for("react.lazy") : 60116; + var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for("react.fundamental") : 60117; + var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for("react.responder") : 60118; + var REACT_SCOPE_TYPE = hasSymbol ? Symbol.for("react.scope") : 60119; + var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; + var FAUX_ITERATOR_SYMBOL = "@@iterator"; + function getIteratorFn(maybeIterable) { + if (maybeIterable === null || typeof maybeIterable !== "object") { + return null; + } + var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; + if (typeof maybeIterator === "function") { + return maybeIterator; + } + return null; + } + var lowPriorityWarningWithoutStack = function() { + }; + { + var printWarning = function(format) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + var argIndex = 0; + var message = "Warning: " + format.replace(/%s/g, function() { + return args[argIndex++]; + }); + if (typeof console !== "undefined") { + console.warn(message); + } + try { + throw new Error(message); + } catch (x) { + } + }; + lowPriorityWarningWithoutStack = function(condition, format) { + if (format === void 0) { + throw new Error("`lowPriorityWarningWithoutStack(condition, format, ...args)` requires a warning message argument"); + } + if (!condition) { + for (var _len2 = arguments.length, args = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { + args[_key2 - 2] = arguments[_key2]; + } + printWarning.apply(void 0, [format].concat(args)); + } + }; + } + var lowPriorityWarningWithoutStack$1 = lowPriorityWarningWithoutStack; + var warningWithoutStack = function() { + }; + { + warningWithoutStack = function(condition, format) { + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + if (format === void 0) { + throw new Error("`warningWithoutStack(condition, format, ...args)` requires a warning message argument"); + } + if (args.length > 8) { + throw new Error("warningWithoutStack() currently supports at most 8 arguments."); + } + if (condition) { + return; + } + if (typeof console !== "undefined") { + var argsWithFormat = args.map(function(item) { + return "" + item; + }); + argsWithFormat.unshift("Warning: " + format); + Function.prototype.apply.call(console.error, console, argsWithFormat); + } + try { + var argIndex = 0; + var message = "Warning: " + format.replace(/%s/g, function() { + return args[argIndex++]; + }); + throw new Error(message); + } catch (x) { + } + }; + } + var warningWithoutStack$1 = warningWithoutStack; + var didWarnStateUpdateForUnmountedComponent = {}; + function warnNoop(publicInstance, callerName) { + { + var _constructor = publicInstance.constructor; + var componentName = _constructor && (_constructor.displayName || _constructor.name) || "ReactClass"; + var warningKey = componentName + "." + callerName; + if (didWarnStateUpdateForUnmountedComponent[warningKey]) { + return; + } + warningWithoutStack$1(false, "Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.", callerName, componentName); + didWarnStateUpdateForUnmountedComponent[warningKey] = true; + } + } + var ReactNoopUpdateQueue = { + /** + * Checks whether or not this composite component is mounted. + * @param {ReactClass} publicInstance The instance we want to test. + * @return {boolean} True if mounted, false otherwise. + * @protected + * @final + */ + isMounted: function(publicInstance) { + return false; + }, + /** + * Forces an update. This should only be invoked when it is known with + * certainty that we are **not** in a DOM transaction. + * + * You may want to call this when you know that some deeper aspect of the + * component's state has changed but `setState` was not called. + * + * This will not invoke `shouldComponentUpdate`, but it will invoke + * `componentWillUpdate` and `componentDidUpdate`. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {?function} callback Called after component is updated. + * @param {?string} callerName name of the calling function in the public API. + * @internal + */ + enqueueForceUpdate: function(publicInstance, callback, callerName) { + warnNoop(publicInstance, "forceUpdate"); + }, + /** + * Replaces all of the state. Always use this or `setState` to mutate state. + * You should treat `this.state` as immutable. + * + * There is no guarantee that `this.state` will be immediately updated, so + * accessing `this.state` after calling this method may return the old value. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} completeState Next state. + * @param {?function} callback Called after component is updated. + * @param {?string} callerName name of the calling function in the public API. + * @internal + */ + enqueueReplaceState: function(publicInstance, completeState, callback, callerName) { + warnNoop(publicInstance, "replaceState"); + }, + /** + * Sets a subset of the state. This only exists because _pendingState is + * internal. This provides a merging strategy that is not available to deep + * properties which is confusing. TODO: Expose pendingState or don't use it + * during the merge. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} partialState Next partial state to be merged with state. + * @param {?function} callback Called after component is updated. + * @param {?string} Name of the calling function in the public API. + * @internal + */ + enqueueSetState: function(publicInstance, partialState, callback, callerName) { + warnNoop(publicInstance, "setState"); + } + }; + var emptyObject = {}; + { + Object.freeze(emptyObject); + } + function Component6(props, context, updater) { + this.props = props; + this.context = context; + this.refs = emptyObject; + this.updater = updater || ReactNoopUpdateQueue; + } + Component6.prototype.isReactComponent = {}; + Component6.prototype.setState = function(partialState, callback) { + if (!(typeof partialState === "object" || typeof partialState === "function" || partialState == null)) { + { + throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables."); + } + } + this.updater.enqueueSetState(this, partialState, callback, "setState"); + }; + Component6.prototype.forceUpdate = function(callback) { + this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); + }; + { + var deprecatedAPIs = { + isMounted: ["isMounted", "Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."], + replaceState: ["replaceState", "Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."] + }; + var defineDeprecationWarning = function(methodName, info) { + Object.defineProperty(Component6.prototype, methodName, { + get: function() { + lowPriorityWarningWithoutStack$1(false, "%s(...) is deprecated in plain JavaScript React classes. %s", info[0], info[1]); + return void 0; + } + }); + }; + for (var fnName in deprecatedAPIs) { + if (deprecatedAPIs.hasOwnProperty(fnName)) { + defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); + } + } + } + function ComponentDummy() { + } + ComponentDummy.prototype = Component6.prototype; + function PureComponent(props, context, updater) { + this.props = props; + this.context = context; + this.refs = emptyObject; + this.updater = updater || ReactNoopUpdateQueue; + } + var pureComponentPrototype = PureComponent.prototype = new ComponentDummy(); + pureComponentPrototype.constructor = PureComponent; + _assign(pureComponentPrototype, Component6.prototype); + pureComponentPrototype.isPureReactComponent = true; + function createRef2() { + var refObject = { + current: null + }; + { + Object.seal(refObject); + } + return refObject; + } + var ReactCurrentDispatcher = { + /** + * @internal + * @type {ReactComponent} + */ + current: null + }; + var ReactCurrentBatchConfig = { + suspense: null + }; + var ReactCurrentOwner = { + /** + * @internal + * @type {ReactComponent} + */ + current: null + }; + var BEFORE_SLASH_RE = /^(.*)[\\\/]/; + var describeComponentFrame = function(name, source, ownerName) { + var sourceInfo = ""; + if (source) { + var path = source.fileName; + var fileName = path.replace(BEFORE_SLASH_RE, ""); + { + if (/^index\./.test(fileName)) { + var match = path.match(BEFORE_SLASH_RE); + if (match) { + var pathBeforeSlash = match[1]; + if (pathBeforeSlash) { + var folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, ""); + fileName = folderName + "/" + fileName; + } + } + } + } + sourceInfo = " (at " + fileName + ":" + source.lineNumber + ")"; + } else if (ownerName) { + sourceInfo = " (created by " + ownerName + ")"; + } + return "\n in " + (name || "Unknown") + sourceInfo; + }; + var Resolved = 1; + function refineResolvedLazyComponent(lazyComponent) { + return lazyComponent._status === Resolved ? lazyComponent._result : null; + } + function getWrappedName(outerType, innerType, wrapperName) { + var functionName = innerType.displayName || innerType.name || ""; + return outerType.displayName || (functionName !== "" ? wrapperName + "(" + functionName + ")" : wrapperName); + } + function getComponentName(type) { + if (type == null) { + return null; + } + { + if (typeof type.tag === "number") { + warningWithoutStack$1(false, "Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."); + } + } + if (typeof type === "function") { + return type.displayName || type.name || null; + } + if (typeof type === "string") { + return type; + } + switch (type) { + case REACT_FRAGMENT_TYPE: + return "Fragment"; + case REACT_PORTAL_TYPE: + return "Portal"; + case REACT_PROFILER_TYPE: + return "Profiler"; + case REACT_STRICT_MODE_TYPE: + return "StrictMode"; + case REACT_SUSPENSE_TYPE: + return "Suspense"; + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + } + if (typeof type === "object") { + switch (type.$$typeof) { + case REACT_CONTEXT_TYPE: + return "Context.Consumer"; + case REACT_PROVIDER_TYPE: + return "Context.Provider"; + case REACT_FORWARD_REF_TYPE: + return getWrappedName(type, type.render, "ForwardRef"); + case REACT_MEMO_TYPE: + return getComponentName(type.type); + case REACT_LAZY_TYPE: { + var thenable = type; + var resolvedThenable = refineResolvedLazyComponent(thenable); + if (resolvedThenable) { + return getComponentName(resolvedThenable); + } + break; + } + } + } + return null; + } + var ReactDebugCurrentFrame = {}; + var currentlyValidatingElement = null; + function setCurrentlyValidatingElement(element) { + { + currentlyValidatingElement = element; + } + } + { + ReactDebugCurrentFrame.getCurrentStack = null; + ReactDebugCurrentFrame.getStackAddendum = function() { + var stack = ""; + if (currentlyValidatingElement) { + var name = getComponentName(currentlyValidatingElement.type); + var owner = currentlyValidatingElement._owner; + stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner.type)); + } + var impl = ReactDebugCurrentFrame.getCurrentStack; + if (impl) { + stack += impl() || ""; + } + return stack; + }; + } + var IsSomeRendererActing = { + current: false + }; + var ReactSharedInternals = { + ReactCurrentDispatcher, + ReactCurrentBatchConfig, + ReactCurrentOwner, + IsSomeRendererActing, + // Used by renderers to avoid bundling object-assign twice in UMD bundles: + assign: _assign + }; + { + _assign(ReactSharedInternals, { + // These should not be included in production. + ReactDebugCurrentFrame, + // Shim for React DOM 16.0.0 which still destructured (but not used) this. + // TODO: remove in React 17.0. + ReactComponentTreeHook: {} + }); + } + var warning = warningWithoutStack$1; + { + warning = function(condition, format) { + if (condition) { + return; + } + var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame2.getStackAddendum(); + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + warningWithoutStack$1.apply(void 0, [false, format + "%s"].concat(args, [stack])); + }; + } + var warning$1 = warning; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var RESERVED_PROPS = { + key: true, + ref: true, + __self: true, + __source: true + }; + var specialPropKeyWarningShown; + var specialPropRefWarningShown; + function hasValidRef(config) { + { + if (hasOwnProperty.call(config, "ref")) { + var getter = Object.getOwnPropertyDescriptor(config, "ref").get; + if (getter && getter.isReactWarning) { + return false; + } + } + } + return config.ref !== void 0; + } + function hasValidKey(config) { + { + if (hasOwnProperty.call(config, "key")) { + var getter = Object.getOwnPropertyDescriptor(config, "key").get; + if (getter && getter.isReactWarning) { + return false; + } + } + } + return config.key !== void 0; + } + function defineKeyPropWarningGetter(props, displayName) { + var warnAboutAccessingKey = function() { + if (!specialPropKeyWarningShown) { + specialPropKeyWarningShown = true; + warningWithoutStack$1(false, "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)", displayName); + } + }; + warnAboutAccessingKey.isReactWarning = true; + Object.defineProperty(props, "key", { + get: warnAboutAccessingKey, + configurable: true + }); + } + function defineRefPropWarningGetter(props, displayName) { + var warnAboutAccessingRef = function() { + if (!specialPropRefWarningShown) { + specialPropRefWarningShown = true; + warningWithoutStack$1(false, "%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)", displayName); + } + }; + warnAboutAccessingRef.isReactWarning = true; + Object.defineProperty(props, "ref", { + get: warnAboutAccessingRef, + configurable: true + }); + } + var ReactElement = function(type, key, ref, self2, source, owner, props) { + var element = { + // This tag allows us to uniquely identify this as a React Element + $$typeof: REACT_ELEMENT_TYPE, + // Built-in properties that belong on the element + type, + key, + ref, + props, + // Record the component responsible for creating this element. + _owner: owner + }; + { + element._store = {}; + Object.defineProperty(element._store, "validated", { + configurable: false, + enumerable: false, + writable: true, + value: false + }); + Object.defineProperty(element, "_self", { + configurable: false, + enumerable: false, + writable: false, + value: self2 + }); + Object.defineProperty(element, "_source", { + configurable: false, + enumerable: false, + writable: false, + value: source + }); + if (Object.freeze) { + Object.freeze(element.props); + Object.freeze(element); + } + } + return element; + }; + function jsxDEV(type, config, maybeKey, source, self2) { + var propName; + var props = {}; + var key = null; + var ref = null; + if (maybeKey !== void 0) { + key = "" + maybeKey; + } + if (hasValidKey(config)) { + key = "" + config.key; + } + if (hasValidRef(config)) { + ref = config.ref; + } + for (propName in config) { + if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { + props[propName] = config[propName]; + } + } + if (type && type.defaultProps) { + var defaultProps = type.defaultProps; + for (propName in defaultProps) { + if (props[propName] === void 0) { + props[propName] = defaultProps[propName]; + } + } + } + if (key || ref) { + var displayName = typeof type === "function" ? type.displayName || type.name || "Unknown" : type; + if (key) { + defineKeyPropWarningGetter(props, displayName); + } + if (ref) { + defineRefPropWarningGetter(props, displayName); + } + } + return ReactElement(type, key, ref, self2, source, ReactCurrentOwner.current, props); + } + function createElement7(type, config, children) { + var propName; + var props = {}; + var key = null; + var ref = null; + var self2 = null; + var source = null; + if (config != null) { + if (hasValidRef(config)) { + ref = config.ref; + } + if (hasValidKey(config)) { + key = "" + config.key; + } + self2 = config.__self === void 0 ? null : config.__self; + source = config.__source === void 0 ? null : config.__source; + for (propName in config) { + if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { + props[propName] = config[propName]; + } + } + } + var childrenLength = arguments.length - 2; + if (childrenLength === 1) { + props.children = children; + } else if (childrenLength > 1) { + var childArray = Array(childrenLength); + for (var i = 0; i < childrenLength; i++) { + childArray[i] = arguments[i + 2]; + } + { + if (Object.freeze) { + Object.freeze(childArray); + } + } + props.children = childArray; + } + if (type && type.defaultProps) { + var defaultProps = type.defaultProps; + for (propName in defaultProps) { + if (props[propName] === void 0) { + props[propName] = defaultProps[propName]; + } + } + } + { + if (key || ref) { + var displayName = typeof type === "function" ? type.displayName || type.name || "Unknown" : type; + if (key) { + defineKeyPropWarningGetter(props, displayName); + } + if (ref) { + defineRefPropWarningGetter(props, displayName); + } + } + } + return ReactElement(type, key, ref, self2, source, ReactCurrentOwner.current, props); + } + function cloneAndReplaceKey(oldElement, newKey) { + var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props); + return newElement; + } + function cloneElement(element, config, children) { + if (!!(element === null || element === void 0)) { + { + throw Error("React.cloneElement(...): The argument must be a React element, but you passed " + element + "."); + } + } + var propName; + var props = _assign({}, element.props); + var key = element.key; + var ref = element.ref; + var self2 = element._self; + var source = element._source; + var owner = element._owner; + if (config != null) { + if (hasValidRef(config)) { + ref = config.ref; + owner = ReactCurrentOwner.current; + } + if (hasValidKey(config)) { + key = "" + config.key; + } + var defaultProps; + if (element.type && element.type.defaultProps) { + defaultProps = element.type.defaultProps; + } + for (propName in config) { + if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { + if (config[propName] === void 0 && defaultProps !== void 0) { + props[propName] = defaultProps[propName]; + } else { + props[propName] = config[propName]; + } + } + } + } + var childrenLength = arguments.length - 2; + if (childrenLength === 1) { + props.children = children; + } else if (childrenLength > 1) { + var childArray = Array(childrenLength); + for (var i = 0; i < childrenLength; i++) { + childArray[i] = arguments[i + 2]; + } + props.children = childArray; + } + return ReactElement(element.type, key, ref, self2, source, owner, props); + } + function isValidElement(object) { + return typeof object === "object" && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; + } + var SEPARATOR = "."; + var SUBSEPARATOR = ":"; + function escape(key) { + var escapeRegex = /[=:]/g; + var escaperLookup = { + "=": "=0", + ":": "=2" + }; + var escapedString = ("" + key).replace(escapeRegex, function(match) { + return escaperLookup[match]; + }); + return "$" + escapedString; + } + var didWarnAboutMaps = false; + var userProvidedKeyEscapeRegex = /\/+/g; + function escapeUserProvidedKey(text) { + return ("" + text).replace(userProvidedKeyEscapeRegex, "$&/"); + } + var POOL_SIZE = 10; + var traverseContextPool = []; + function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) { + if (traverseContextPool.length) { + var traverseContext = traverseContextPool.pop(); + traverseContext.result = mapResult; + traverseContext.keyPrefix = keyPrefix; + traverseContext.func = mapFunction; + traverseContext.context = mapContext; + traverseContext.count = 0; + return traverseContext; + } else { + return { + result: mapResult, + keyPrefix, + func: mapFunction, + context: mapContext, + count: 0 + }; + } + } + function releaseTraverseContext(traverseContext) { + traverseContext.result = null; + traverseContext.keyPrefix = null; + traverseContext.func = null; + traverseContext.context = null; + traverseContext.count = 0; + if (traverseContextPool.length < POOL_SIZE) { + traverseContextPool.push(traverseContext); + } + } + function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) { + var type = typeof children; + if (type === "undefined" || type === "boolean") { + children = null; + } + var invokeCallback = false; + if (children === null) { + invokeCallback = true; + } else { + switch (type) { + case "string": + case "number": + invokeCallback = true; + break; + case "object": + switch (children.$$typeof) { + case REACT_ELEMENT_TYPE: + case REACT_PORTAL_TYPE: + invokeCallback = true; + } + } + } + if (invokeCallback) { + callback( + traverseContext, + children, + // If it's the only child, treat the name as if it was wrapped in an array + // so that it's consistent if the number of children grows. + nameSoFar === "" ? SEPARATOR + getComponentKey(children, 0) : nameSoFar + ); + return 1; + } + var child; + var nextName; + var subtreeCount = 0; + var nextNamePrefix = nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR; + if (Array.isArray(children)) { + for (var i = 0; i < children.length; i++) { + child = children[i]; + nextName = nextNamePrefix + getComponentKey(child, i); + subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); + } + } else { + var iteratorFn = getIteratorFn(children); + if (typeof iteratorFn === "function") { + { + if (iteratorFn === children.entries) { + !didWarnAboutMaps ? warning$1(false, "Using Maps as children is unsupported and will likely yield unexpected results. Convert it to a sequence/iterable of keyed ReactElements instead.") : void 0; + didWarnAboutMaps = true; + } + } + var iterator = iteratorFn.call(children); + var step; + var ii = 0; + while (!(step = iterator.next()).done) { + child = step.value; + nextName = nextNamePrefix + getComponentKey(child, ii++); + subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); + } + } else if (type === "object") { + var addendum = ""; + { + addendum = " If you meant to render a collection of children, use an array instead." + ReactDebugCurrentFrame.getStackAddendum(); + } + var childrenString = "" + children; + { + { + throw Error("Objects are not valid as a React child (found: " + (childrenString === "[object Object]" ? "object with keys {" + Object.keys(children).join(", ") + "}" : childrenString) + ")." + addendum); + } + } + } + } + return subtreeCount; + } + function traverseAllChildren(children, callback, traverseContext) { + if (children == null) { + return 0; + } + return traverseAllChildrenImpl(children, "", callback, traverseContext); + } + function getComponentKey(component, index) { + if (typeof component === "object" && component !== null && component.key != null) { + return escape(component.key); + } + return index.toString(36); + } + function forEachSingleChild(bookKeeping, child, name) { + var func = bookKeeping.func, context = bookKeeping.context; + func.call(context, child, bookKeeping.count++); + } + function forEachChildren(children, forEachFunc, forEachContext) { + if (children == null) { + return children; + } + var traverseContext = getPooledTraverseContext(null, null, forEachFunc, forEachContext); + traverseAllChildren(children, forEachSingleChild, traverseContext); + releaseTraverseContext(traverseContext); + } + function mapSingleChildIntoContext(bookKeeping, child, childKey) { + var result = bookKeeping.result, keyPrefix = bookKeeping.keyPrefix, func = bookKeeping.func, context = bookKeeping.context; + var mappedChild = func.call(context, child, bookKeeping.count++); + if (Array.isArray(mappedChild)) { + mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, function(c) { + return c; + }); + } else if (mappedChild != null) { + if (isValidElement(mappedChild)) { + mappedChild = cloneAndReplaceKey( + mappedChild, + // Keep both the (mapped) and old keys if they differ, just as + // traverseAllChildren used to do for objects as children + keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + "/" : "") + childKey + ); + } + result.push(mappedChild); + } + } + function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { + var escapedPrefix = ""; + if (prefix != null) { + escapedPrefix = escapeUserProvidedKey(prefix) + "/"; + } + var traverseContext = getPooledTraverseContext(array, escapedPrefix, func, context); + traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); + releaseTraverseContext(traverseContext); + } + function mapChildren(children, func, context) { + if (children == null) { + return children; + } + var result = []; + mapIntoWithKeyPrefixInternal(children, result, null, func, context); + return result; + } + function countChildren(children) { + return traverseAllChildren(children, function() { + return null; + }, null); + } + function toArray(children) { + var result = []; + mapIntoWithKeyPrefixInternal(children, result, null, function(child) { + return child; + }); + return result; + } + function onlyChild(children) { + if (!isValidElement(children)) { + { + throw Error("React.Children.only expected to receive a single React element child."); + } + } + return children; + } + function createContext(defaultValue, calculateChangedBits) { + if (calculateChangedBits === void 0) { + calculateChangedBits = null; + } else { + { + !(calculateChangedBits === null || typeof calculateChangedBits === "function") ? warningWithoutStack$1(false, "createContext: Expected the optional second argument to be a function. Instead received: %s", calculateChangedBits) : void 0; + } + } + var context = { + $$typeof: REACT_CONTEXT_TYPE, + _calculateChangedBits: calculateChangedBits, + // As a workaround to support multiple concurrent renderers, we categorize + // some renderers as primary and others as secondary. We only expect + // there to be two concurrent renderers at most: React Native (primary) and + // Fabric (secondary); React DOM (primary) and React ART (secondary). + // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, + _currentValue2: defaultValue, + // Used to track how many concurrent renderers this context currently + // supports within in a single renderer. Such as parallel server rendering. + _threadCount: 0, + // These are circular + Provider: null, + Consumer: null + }; + context.Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context + }; + var hasWarnedAboutUsingNestedContextConsumers = false; + var hasWarnedAboutUsingConsumerProvider = false; + { + var Consumer = { + $$typeof: REACT_CONTEXT_TYPE, + _context: context, + _calculateChangedBits: context._calculateChangedBits + }; + Object.defineProperties(Consumer, { + Provider: { + get: function() { + if (!hasWarnedAboutUsingConsumerProvider) { + hasWarnedAboutUsingConsumerProvider = true; + warning$1(false, "Rendering is not supported and will be removed in a future major release. Did you mean to render instead?"); + } + return context.Provider; + }, + set: function(_Provider) { + context.Provider = _Provider; + } + }, + _currentValue: { + get: function() { + return context._currentValue; + }, + set: function(_currentValue) { + context._currentValue = _currentValue; + } + }, + _currentValue2: { + get: function() { + return context._currentValue2; + }, + set: function(_currentValue2) { + context._currentValue2 = _currentValue2; + } + }, + _threadCount: { + get: function() { + return context._threadCount; + }, + set: function(_threadCount) { + context._threadCount = _threadCount; + } + }, + Consumer: { + get: function() { + if (!hasWarnedAboutUsingNestedContextConsumers) { + hasWarnedAboutUsingNestedContextConsumers = true; + warning$1(false, "Rendering is not supported and will be removed in a future major release. Did you mean to render instead?"); + } + return context.Consumer; + } + } + }); + context.Consumer = Consumer; + } + { + context._currentRenderer = null; + context._currentRenderer2 = null; + } + return context; + } + function lazy(ctor) { + var lazyType = { + $$typeof: REACT_LAZY_TYPE, + _ctor: ctor, + // React uses these fields to store the result. + _status: -1, + _result: null + }; + { + var defaultProps; + var propTypes; + Object.defineProperties(lazyType, { + defaultProps: { + configurable: true, + get: function() { + return defaultProps; + }, + set: function(newDefaultProps) { + warning$1(false, "React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."); + defaultProps = newDefaultProps; + Object.defineProperty(lazyType, "defaultProps", { + enumerable: true + }); + } + }, + propTypes: { + configurable: true, + get: function() { + return propTypes; + }, + set: function(newPropTypes) { + warning$1(false, "React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."); + propTypes = newPropTypes; + Object.defineProperty(lazyType, "propTypes", { + enumerable: true + }); + } + } + }); + } + return lazyType; + } + function forwardRef(render2) { + { + if (render2 != null && render2.$$typeof === REACT_MEMO_TYPE) { + warningWithoutStack$1(false, "forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."); + } else if (typeof render2 !== "function") { + warningWithoutStack$1(false, "forwardRef requires a render function but was given %s.", render2 === null ? "null" : typeof render2); + } else { + !// Do not warn for 0 arguments because it could be due to usage of the 'arguments' object + (render2.length === 0 || render2.length === 2) ? warningWithoutStack$1(false, "forwardRef render functions accept exactly two parameters: props and ref. %s", render2.length === 1 ? "Did you forget to use the ref parameter?" : "Any additional parameter will be undefined.") : void 0; + } + if (render2 != null) { + !(render2.defaultProps == null && render2.propTypes == null) ? warningWithoutStack$1(false, "forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?") : void 0; + } + } + return { + $$typeof: REACT_FORWARD_REF_TYPE, + render: render2 + }; + } + function isValidElementType(type) { + return typeof type === "string" || typeof type === "function" || // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill. + type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || typeof type === "object" && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_RESPONDER_TYPE || type.$$typeof === REACT_SCOPE_TYPE); + } + function memo(type, compare) { + { + if (!isValidElementType(type)) { + warningWithoutStack$1(false, "memo: The first argument must be a component. Instead received: %s", type === null ? "null" : typeof type); + } + } + return { + $$typeof: REACT_MEMO_TYPE, + type, + compare: compare === void 0 ? null : compare + }; + } + function resolveDispatcher() { + var dispatcher = ReactCurrentDispatcher.current; + if (!(dispatcher !== null)) { + { + throw Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem."); + } + } + return dispatcher; + } + function useContext(Context, unstable_observedBits) { + var dispatcher = resolveDispatcher(); + { + !(unstable_observedBits === void 0) ? warning$1(false, "useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: %s.%s", unstable_observedBits, typeof unstable_observedBits === "number" && Array.isArray(arguments[2]) ? "\n\nDid you call array.map(useContext)? Calling Hooks inside a loop is not supported. Learn more at https://fb.me/rules-of-hooks" : "") : void 0; + if (Context._context !== void 0) { + var realContext = Context._context; + if (realContext.Consumer === Context) { + warning$1(false, "Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"); + } else if (realContext.Provider === Context) { + warning$1(false, "Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?"); + } + } + } + return dispatcher.useContext(Context, unstable_observedBits); + } + function useState(initialState) { + var dispatcher = resolveDispatcher(); + return dispatcher.useState(initialState); + } + function useReducer(reducer, initialArg, init) { + var dispatcher = resolveDispatcher(); + return dispatcher.useReducer(reducer, initialArg, init); + } + function useRef(initialValue) { + var dispatcher = resolveDispatcher(); + return dispatcher.useRef(initialValue); + } + function useEffect(create, inputs) { + var dispatcher = resolveDispatcher(); + return dispatcher.useEffect(create, inputs); + } + function useLayoutEffect(create, inputs) { + var dispatcher = resolveDispatcher(); + return dispatcher.useLayoutEffect(create, inputs); + } + function useCallback(callback, inputs) { + var dispatcher = resolveDispatcher(); + return dispatcher.useCallback(callback, inputs); + } + function useMemo(create, inputs) { + var dispatcher = resolveDispatcher(); + return dispatcher.useMemo(create, inputs); + } + function useImperativeHandle(ref, create, inputs) { + var dispatcher = resolveDispatcher(); + return dispatcher.useImperativeHandle(ref, create, inputs); + } + function useDebugValue(value, formatterFn) { + { + var dispatcher = resolveDispatcher(); + return dispatcher.useDebugValue(value, formatterFn); + } + } + var emptyObject$1 = {}; + function useResponder(responder, listenerProps) { + var dispatcher = resolveDispatcher(); + { + if (responder == null || responder.$$typeof !== REACT_RESPONDER_TYPE) { + warning$1(false, "useResponder: invalid first argument. Expected an event responder, but instead got %s", responder); + return; + } + } + return dispatcher.useResponder(responder, listenerProps || emptyObject$1); + } + function useTransition(config) { + var dispatcher = resolveDispatcher(); + return dispatcher.useTransition(config); + } + function useDeferredValue(value, config) { + var dispatcher = resolveDispatcher(); + return dispatcher.useDeferredValue(value, config); + } + function withSuspenseConfig(scope, config) { + var previousConfig = ReactCurrentBatchConfig.suspense; + ReactCurrentBatchConfig.suspense = config === void 0 ? null : config; + try { + scope(); + } finally { + ReactCurrentBatchConfig.suspense = previousConfig; + } + } + var propTypesMisspellWarningShown; + { + propTypesMisspellWarningShown = false; + } + var hasOwnProperty$1 = Object.prototype.hasOwnProperty; + function getDeclarationErrorAddendum() { + if (ReactCurrentOwner.current) { + var name = getComponentName(ReactCurrentOwner.current.type); + if (name) { + return "\n\nCheck the render method of `" + name + "`."; + } + } + return ""; + } + function getSourceInfoErrorAddendum(source) { + if (source !== void 0) { + var fileName = source.fileName.replace(/^.*[\\\/]/, ""); + var lineNumber = source.lineNumber; + return "\n\nCheck your code at " + fileName + ":" + lineNumber + "."; + } + return ""; + } + function getSourceInfoErrorAddendumForProps(elementProps) { + if (elementProps !== null && elementProps !== void 0) { + return getSourceInfoErrorAddendum(elementProps.__source); + } + return ""; + } + var ownerHasKeyUseWarning = {}; + function getCurrentComponentErrorInfo(parentType) { + var info = getDeclarationErrorAddendum(); + if (!info) { + var parentName = typeof parentType === "string" ? parentType : parentType.displayName || parentType.name; + if (parentName) { + info = "\n\nCheck the top-level render call using <" + parentName + ">."; + } + } + return info; + } + function validateExplicitKey(element, parentType) { + if (!element._store || element._store.validated || element.key != null) { + return; + } + element._store.validated = true; + var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); + if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { + return; + } + ownerHasKeyUseWarning[currentComponentErrorInfo] = true; + var childOwner = ""; + if (element && element._owner && element._owner !== ReactCurrentOwner.current) { + childOwner = " It was passed a child from " + getComponentName(element._owner.type) + "."; + } + setCurrentlyValidatingElement(element); + { + warning$1(false, 'Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.', currentComponentErrorInfo, childOwner); + } + setCurrentlyValidatingElement(null); + } + function validateChildKeys(node, parentType) { + if (typeof node !== "object") { + return; + } + if (Array.isArray(node)) { + for (var i = 0; i < node.length; i++) { + var child = node[i]; + if (isValidElement(child)) { + validateExplicitKey(child, parentType); + } + } + } else if (isValidElement(node)) { + if (node._store) { + node._store.validated = true; + } + } else if (node) { + var iteratorFn = getIteratorFn(node); + if (typeof iteratorFn === "function") { + if (iteratorFn !== node.entries) { + var iterator = iteratorFn.call(node); + var step; + while (!(step = iterator.next()).done) { + if (isValidElement(step.value)) { + validateExplicitKey(step.value, parentType); + } + } + } + } + } + } + function validatePropTypes(element) { + var type = element.type; + if (type === null || type === void 0 || typeof type === "string") { + return; + } + var name = getComponentName(type); + var propTypes; + if (typeof type === "function") { + propTypes = type.propTypes; + } else if (typeof type === "object" && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here. + // Inner props are checked in the reconciler. + type.$$typeof === REACT_MEMO_TYPE)) { + propTypes = type.propTypes; + } else { + return; + } + if (propTypes) { + setCurrentlyValidatingElement(element); + checkPropTypes(propTypes, element.props, "prop", name, ReactDebugCurrentFrame.getStackAddendum); + setCurrentlyValidatingElement(null); + } else if (type.PropTypes !== void 0 && !propTypesMisspellWarningShown) { + propTypesMisspellWarningShown = true; + warningWithoutStack$1(false, "Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?", name || "Unknown"); + } + if (typeof type.getDefaultProps === "function") { + !type.getDefaultProps.isReactClassApproved ? warningWithoutStack$1(false, "getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.") : void 0; + } + } + function validateFragmentProps(fragment) { + setCurrentlyValidatingElement(fragment); + var keys = Object.keys(fragment.props); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (key !== "children" && key !== "key") { + warning$1(false, "Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.", key); + break; + } + } + if (fragment.ref !== null) { + warning$1(false, "Invalid attribute `ref` supplied to `React.Fragment`."); + } + setCurrentlyValidatingElement(null); + } + function jsxWithValidation(type, props, key, isStaticChildren, source, self2) { + var validType = isValidElementType(type); + if (!validType) { + var info = ""; + if (type === void 0 || typeof type === "object" && type !== null && Object.keys(type).length === 0) { + info += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports."; + } + var sourceInfo = getSourceInfoErrorAddendum(source); + if (sourceInfo) { + info += sourceInfo; + } else { + info += getDeclarationErrorAddendum(); + } + var typeString; + if (type === null) { + typeString = "null"; + } else if (Array.isArray(type)) { + typeString = "array"; + } else if (type !== void 0 && type.$$typeof === REACT_ELEMENT_TYPE) { + typeString = "<" + (getComponentName(type.type) || "Unknown") + " />"; + info = " Did you accidentally export a JSX literal instead of a component?"; + } else { + typeString = typeof type; + } + warning$1(false, "React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s", typeString, info); + } + var element = jsxDEV(type, props, key, source, self2); + if (element == null) { + return element; + } + if (validType) { + var children = props.children; + if (children !== void 0) { + if (isStaticChildren) { + if (Array.isArray(children)) { + for (var i = 0; i < children.length; i++) { + validateChildKeys(children[i], type); + } + if (Object.freeze) { + Object.freeze(children); + } + } else { + warning$1(false, "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."); + } + } else { + validateChildKeys(children, type); + } + } + } + if (hasOwnProperty$1.call(props, "key")) { + warning$1(false, "React.jsx: Spreading a key to JSX is a deprecated pattern. Explicitly pass a key after spreading props in your JSX call. E.g. "); + } + if (type === REACT_FRAGMENT_TYPE) { + validateFragmentProps(element); + } else { + validatePropTypes(element); + } + return element; + } + function jsxWithValidationStatic(type, props, key) { + return jsxWithValidation(type, props, key, true); + } + function jsxWithValidationDynamic(type, props, key) { + return jsxWithValidation(type, props, key, false); + } + function createElementWithValidation(type, props, children) { + var validType = isValidElementType(type); + if (!validType) { + var info = ""; + if (type === void 0 || typeof type === "object" && type !== null && Object.keys(type).length === 0) { + info += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports."; + } + var sourceInfo = getSourceInfoErrorAddendumForProps(props); + if (sourceInfo) { + info += sourceInfo; + } else { + info += getDeclarationErrorAddendum(); + } + var typeString; + if (type === null) { + typeString = "null"; + } else if (Array.isArray(type)) { + typeString = "array"; + } else if (type !== void 0 && type.$$typeof === REACT_ELEMENT_TYPE) { + typeString = "<" + (getComponentName(type.type) || "Unknown") + " />"; + info = " Did you accidentally export a JSX literal instead of a component?"; + } else { + typeString = typeof type; + } + warning$1(false, "React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s", typeString, info); + } + var element = createElement7.apply(this, arguments); + if (element == null) { + return element; + } + if (validType) { + for (var i = 2; i < arguments.length; i++) { + validateChildKeys(arguments[i], type); + } + } + if (type === REACT_FRAGMENT_TYPE) { + validateFragmentProps(element); + } else { + validatePropTypes(element); + } + return element; + } + function createFactoryWithValidation(type) { + var validatedFactory = createElementWithValidation.bind(null, type); + validatedFactory.type = type; + { + Object.defineProperty(validatedFactory, "type", { + enumerable: false, + get: function() { + lowPriorityWarningWithoutStack$1(false, "Factory.type is deprecated. Access the class directly before passing it to createFactory."); + Object.defineProperty(this, "type", { + value: type + }); + return type; + } + }); + } + return validatedFactory; + } + function cloneElementWithValidation(element, props, children) { + var newElement = cloneElement.apply(this, arguments); + for (var i = 2; i < arguments.length; i++) { + validateChildKeys(arguments[i], newElement.type); + } + validatePropTypes(newElement); + return newElement; + } + var hasBadMapPolyfill; + { + hasBadMapPolyfill = false; + try { + var frozenObject = Object.freeze({}); + var testMap = /* @__PURE__ */ new Map([[frozenObject, null]]); + var testSet = /* @__PURE__ */ new Set([frozenObject]); + testMap.set(0, 0); + testSet.add(0); + } catch (e) { + hasBadMapPolyfill = true; + } + } + function createFundamentalComponent(impl) { + if (!hasBadMapPolyfill) { + Object.freeze(impl); + } + var fundamantalComponent = { + $$typeof: REACT_FUNDAMENTAL_TYPE, + impl + }; + { + Object.freeze(fundamantalComponent); + } + return fundamantalComponent; + } + function createEventResponder(displayName, responderConfig) { + var getInitialState = responderConfig.getInitialState, onEvent = responderConfig.onEvent, onMount = responderConfig.onMount, onUnmount = responderConfig.onUnmount, onRootEvent = responderConfig.onRootEvent, rootEventTypes = responderConfig.rootEventTypes, targetEventTypes = responderConfig.targetEventTypes, targetPortalPropagation = responderConfig.targetPortalPropagation; + var eventResponder = { + $$typeof: REACT_RESPONDER_TYPE, + displayName, + getInitialState: getInitialState || null, + onEvent: onEvent || null, + onMount: onMount || null, + onRootEvent: onRootEvent || null, + onUnmount: onUnmount || null, + rootEventTypes: rootEventTypes || null, + targetEventTypes: targetEventTypes || null, + targetPortalPropagation: targetPortalPropagation || false + }; + if (!hasBadMapPolyfill) { + Object.freeze(eventResponder); + } + return eventResponder; + } + function createScope() { + var scopeComponent = { + $$typeof: REACT_SCOPE_TYPE + }; + { + Object.freeze(scopeComponent); + } + return scopeComponent; + } + var exposeConcurrentModeAPIs = false; + var enableFlareAPI = false; + var enableFundamentalAPI = false; + var enableScopeAPI = false; + var enableJSXTransformAPI = false; + var React7 = { + Children: { + map: mapChildren, + forEach: forEachChildren, + count: countChildren, + toArray, + only: onlyChild + }, + createRef: createRef2, + Component: Component6, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment: REACT_FRAGMENT_TYPE, + Profiler: REACT_PROFILER_TYPE, + StrictMode: REACT_STRICT_MODE_TYPE, + Suspense: REACT_SUSPENSE_TYPE, + createElement: createElementWithValidation, + cloneElement: cloneElementWithValidation, + createFactory: createFactoryWithValidation, + isValidElement, + version: ReactVersion, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals + }; + if (exposeConcurrentModeAPIs) { + React7.useTransition = useTransition; + React7.useDeferredValue = useDeferredValue; + React7.SuspenseList = REACT_SUSPENSE_LIST_TYPE; + React7.unstable_withSuspenseConfig = withSuspenseConfig; + } + if (enableFlareAPI) { + React7.unstable_useResponder = useResponder; + React7.unstable_createResponder = createEventResponder; + } + if (enableFundamentalAPI) { + React7.unstable_createFundamental = createFundamentalComponent; + } + if (enableScopeAPI) { + React7.unstable_createScope = createScope; + } + if (enableJSXTransformAPI) { + { + React7.jsxDEV = jsxWithValidation; + React7.jsx = jsxWithValidationDynamic; + React7.jsxs = jsxWithValidationStatic; + } + } + var React$2 = Object.freeze({ + default: React7 + }); + var React$3 = React$2 && React7 || React$2; + var react = React$3.default || React$3; + module.exports = react; + })(); + } + } + }); + + // node_modules/react/index.js + var require_react = __commonJS({ + "node_modules/react/index.js"(exports, module) { + "use strict"; + if (false) { + module.exports = null; + } else { + module.exports = require_react_development(); + } + } + }); + + // node_modules/scheduler/cjs/scheduler.development.js + var require_scheduler_development = __commonJS({ + "node_modules/scheduler/cjs/scheduler.development.js"(exports) { + "use strict"; + if (true) { + (function() { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var enableSchedulerDebugging = false; + var enableIsInputPending = false; + var enableMessageLoopImplementation = true; + var enableProfiling = true; + var requestHostCallback; + var requestHostTimeout; + var cancelHostTimeout; + var shouldYieldToHost; + var requestPaint; + if ( + // If Scheduler runs in a non-DOM environment, it falls back to a naive + // implementation using setTimeout. + typeof window === "undefined" || // Check if MessageChannel is supported, too. + typeof MessageChannel !== "function" + ) { + var _callback = null; + var _timeoutID = null; + var _flushCallback = function() { + if (_callback !== null) { + try { + var currentTime = exports.unstable_now(); + var hasRemainingTime = true; + _callback(hasRemainingTime, currentTime); + _callback = null; + } catch (e) { + setTimeout(_flushCallback, 0); + throw e; + } + } + }; + var initialTime = Date.now(); + exports.unstable_now = function() { + return Date.now() - initialTime; + }; + requestHostCallback = function(cb) { + if (_callback !== null) { + setTimeout(requestHostCallback, 0, cb); + } else { + _callback = cb; + setTimeout(_flushCallback, 0); + } + }; + requestHostTimeout = function(cb, ms) { + _timeoutID = setTimeout(cb, ms); + }; + cancelHostTimeout = function() { + clearTimeout(_timeoutID); + }; + shouldYieldToHost = function() { + return false; + }; + requestPaint = exports.unstable_forceFrameRate = function() { + }; + } else { + var performance2 = window.performance; + var _Date = window.Date; + var _setTimeout = window.setTimeout; + var _clearTimeout = window.clearTimeout; + var requestAnimationFrame = window.requestAnimationFrame; + var cancelAnimationFrame = window.cancelAnimationFrame; + if (typeof console !== "undefined") { + if (typeof requestAnimationFrame !== "function") { + console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"); + } + if (typeof cancelAnimationFrame !== "function") { + console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"); + } + } + if (typeof performance2 === "object" && typeof performance2.now === "function") { + exports.unstable_now = function() { + return performance2.now(); + }; + } else { + var _initialTime = _Date.now(); + exports.unstable_now = function() { + return _Date.now() - _initialTime; + }; + } + var isRAFLoopRunning = false; + var isMessageLoopRunning = false; + var scheduledHostCallback = null; + var rAFTimeoutID = -1; + var taskTimeoutID = -1; + var frameLength = enableMessageLoopImplementation ? ( + // We won't attempt to align with the vsync. Instead we'll yield multiple + // times per frame, often enough to keep it responsive even at really + // high frame rates > 120. + 5 + ) : ( + // Use a heuristic to measure the frame rate and yield at the end of the + // frame. We start out assuming that we run at 30fps but then the + // heuristic tracking will adjust this value to a faster fps if we get + // more frequent animation frames. + 33.33 + ); + var prevRAFTime = -1; + var prevRAFInterval = -1; + var frameDeadline = 0; + var fpsLocked = false; + var maxFrameLength = 300; + var needsPaint = false; + if (enableIsInputPending && navigator !== void 0 && navigator.scheduling !== void 0 && navigator.scheduling.isInputPending !== void 0) { + var scheduling = navigator.scheduling; + shouldYieldToHost = function() { + var currentTime = exports.unstable_now(); + if (currentTime >= frameDeadline) { + if (needsPaint || scheduling.isInputPending()) { + return true; + } + return currentTime >= frameDeadline + maxFrameLength; + } else { + return false; + } + }; + requestPaint = function() { + needsPaint = true; + }; + } else { + shouldYieldToHost = function() { + return exports.unstable_now() >= frameDeadline; + }; + requestPaint = function() { + }; + } + exports.unstable_forceFrameRate = function(fps) { + if (fps < 0 || fps > 125) { + console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"); + return; + } + if (fps > 0) { + frameLength = Math.floor(1e3 / fps); + fpsLocked = true; + } else { + frameLength = 33.33; + fpsLocked = false; + } + }; + var performWorkUntilDeadline = function() { + if (enableMessageLoopImplementation) { + if (scheduledHostCallback !== null) { + var currentTime = exports.unstable_now(); + frameDeadline = currentTime + frameLength; + var hasTimeRemaining = true; + try { + var hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime); + if (!hasMoreWork) { + isMessageLoopRunning = false; + scheduledHostCallback = null; + } else { + port.postMessage(null); + } + } catch (error) { + port.postMessage(null); + throw error; + } + } else { + isMessageLoopRunning = false; + } + needsPaint = false; + } else { + if (scheduledHostCallback !== null) { + var _currentTime = exports.unstable_now(); + var _hasTimeRemaining = frameDeadline - _currentTime > 0; + try { + var _hasMoreWork = scheduledHostCallback(_hasTimeRemaining, _currentTime); + if (!_hasMoreWork) { + scheduledHostCallback = null; + } + } catch (error) { + port.postMessage(null); + throw error; + } + } + needsPaint = false; + } + }; + var channel = new MessageChannel(); + var port = channel.port2; + channel.port1.onmessage = performWorkUntilDeadline; + var onAnimationFrame = function(rAFTime) { + if (scheduledHostCallback === null) { + prevRAFTime = -1; + prevRAFInterval = -1; + isRAFLoopRunning = false; + return; + } + isRAFLoopRunning = true; + requestAnimationFrame(function(nextRAFTime) { + _clearTimeout(rAFTimeoutID); + onAnimationFrame(nextRAFTime); + }); + var onTimeout = function() { + frameDeadline = exports.unstable_now() + frameLength / 2; + performWorkUntilDeadline(); + rAFTimeoutID = _setTimeout(onTimeout, frameLength * 3); + }; + rAFTimeoutID = _setTimeout(onTimeout, frameLength * 3); + if (prevRAFTime !== -1 && // Make sure this rAF time is different from the previous one. This check + // could fail if two rAFs fire in the same frame. + rAFTime - prevRAFTime > 0.1) { + var rAFInterval = rAFTime - prevRAFTime; + if (!fpsLocked && prevRAFInterval !== -1) { + if (rAFInterval < frameLength && prevRAFInterval < frameLength) { + frameLength = rAFInterval < prevRAFInterval ? prevRAFInterval : rAFInterval; + if (frameLength < 8.33) { + frameLength = 8.33; + } + } + } + prevRAFInterval = rAFInterval; + } + prevRAFTime = rAFTime; + frameDeadline = rAFTime + frameLength; + port.postMessage(null); + }; + requestHostCallback = function(callback) { + scheduledHostCallback = callback; + if (enableMessageLoopImplementation) { + if (!isMessageLoopRunning) { + isMessageLoopRunning = true; + port.postMessage(null); + } + } else { + if (!isRAFLoopRunning) { + isRAFLoopRunning = true; + requestAnimationFrame(function(rAFTime) { + onAnimationFrame(rAFTime); + }); + } + } + }; + requestHostTimeout = function(callback, ms) { + taskTimeoutID = _setTimeout(function() { + callback(exports.unstable_now()); + }, ms); + }; + cancelHostTimeout = function() { + _clearTimeout(taskTimeoutID); + taskTimeoutID = -1; + }; + } + function push(heap, node) { + var index = heap.length; + heap.push(node); + siftUp(heap, node, index); + } + function peek(heap) { + var first = heap[0]; + return first === void 0 ? null : first; + } + function pop(heap) { + var first = heap[0]; + if (first !== void 0) { + var last = heap.pop(); + if (last !== first) { + heap[0] = last; + siftDown(heap, last, 0); + } + return first; + } else { + return null; + } + } + function siftUp(heap, node, i) { + var index = i; + while (true) { + var parentIndex = Math.floor((index - 1) / 2); + var parent = heap[parentIndex]; + if (parent !== void 0 && compare(parent, node) > 0) { + heap[parentIndex] = node; + heap[index] = parent; + index = parentIndex; + } else { + return; + } + } + } + function siftDown(heap, node, i) { + var index = i; + var length = heap.length; + while (index < length) { + var leftIndex = (index + 1) * 2 - 1; + var left = heap[leftIndex]; + var rightIndex = leftIndex + 1; + var right = heap[rightIndex]; + if (left !== void 0 && compare(left, node) < 0) { + if (right !== void 0 && compare(right, left) < 0) { + heap[index] = right; + heap[rightIndex] = node; + index = rightIndex; + } else { + heap[index] = left; + heap[leftIndex] = node; + index = leftIndex; + } + } else if (right !== void 0 && compare(right, node) < 0) { + heap[index] = right; + heap[rightIndex] = node; + index = rightIndex; + } else { + return; + } + } + } + function compare(a, b) { + var diff = a.sortIndex - b.sortIndex; + return diff !== 0 ? diff : a.id - b.id; + } + var NoPriority = 0; + var ImmediatePriority = 1; + var UserBlockingPriority = 2; + var NormalPriority = 3; + var LowPriority = 4; + var IdlePriority = 5; + var runIdCounter = 0; + var mainThreadIdCounter = 0; + var profilingStateSize = 4; + var sharedProfilingBuffer = enableProfiling ? ( + // $FlowFixMe Flow doesn't know about SharedArrayBuffer + typeof SharedArrayBuffer === "function" ? new SharedArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) : ( + // $FlowFixMe Flow doesn't know about ArrayBuffer + typeof ArrayBuffer === "function" ? new ArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) : null + ) + ) : null; + var profilingState = enableProfiling && sharedProfilingBuffer !== null ? new Int32Array(sharedProfilingBuffer) : []; + var PRIORITY = 0; + var CURRENT_TASK_ID = 1; + var CURRENT_RUN_ID = 2; + var QUEUE_SIZE = 3; + if (enableProfiling) { + profilingState[PRIORITY] = NoPriority; + profilingState[QUEUE_SIZE] = 0; + profilingState[CURRENT_TASK_ID] = 0; + } + var INITIAL_EVENT_LOG_SIZE = 131072; + var MAX_EVENT_LOG_SIZE = 524288; + var eventLogSize = 0; + var eventLogBuffer = null; + var eventLog = null; + var eventLogIndex = 0; + var TaskStartEvent = 1; + var TaskCompleteEvent = 2; + var TaskErrorEvent = 3; + var TaskCancelEvent = 4; + var TaskRunEvent = 5; + var TaskYieldEvent = 6; + var SchedulerSuspendEvent = 7; + var SchedulerResumeEvent = 8; + function logEvent(entries) { + if (eventLog !== null) { + var offset = eventLogIndex; + eventLogIndex += entries.length; + if (eventLogIndex + 1 > eventLogSize) { + eventLogSize *= 2; + if (eventLogSize > MAX_EVENT_LOG_SIZE) { + console.error("Scheduler Profiling: Event log exceeded maximum size. Don't forget to call `stopLoggingProfilingEvents()`."); + stopLoggingProfilingEvents(); + return; + } + var newEventLog = new Int32Array(eventLogSize * 4); + newEventLog.set(eventLog); + eventLogBuffer = newEventLog.buffer; + eventLog = newEventLog; + } + eventLog.set(entries, offset); + } + } + function startLoggingProfilingEvents() { + eventLogSize = INITIAL_EVENT_LOG_SIZE; + eventLogBuffer = new ArrayBuffer(eventLogSize * 4); + eventLog = new Int32Array(eventLogBuffer); + eventLogIndex = 0; + } + function stopLoggingProfilingEvents() { + var buffer = eventLogBuffer; + eventLogSize = 0; + eventLogBuffer = null; + eventLog = null; + eventLogIndex = 0; + return buffer; + } + function markTaskStart(task, ms) { + if (enableProfiling) { + profilingState[QUEUE_SIZE]++; + if (eventLog !== null) { + logEvent([TaskStartEvent, ms * 1e3, task.id, task.priorityLevel]); + } + } + } + function markTaskCompleted(task, ms) { + if (enableProfiling) { + profilingState[PRIORITY] = NoPriority; + profilingState[CURRENT_TASK_ID] = 0; + profilingState[QUEUE_SIZE]--; + if (eventLog !== null) { + logEvent([TaskCompleteEvent, ms * 1e3, task.id]); + } + } + } + function markTaskCanceled(task, ms) { + if (enableProfiling) { + profilingState[QUEUE_SIZE]--; + if (eventLog !== null) { + logEvent([TaskCancelEvent, ms * 1e3, task.id]); + } + } + } + function markTaskErrored(task, ms) { + if (enableProfiling) { + profilingState[PRIORITY] = NoPriority; + profilingState[CURRENT_TASK_ID] = 0; + profilingState[QUEUE_SIZE]--; + if (eventLog !== null) { + logEvent([TaskErrorEvent, ms * 1e3, task.id]); + } + } + } + function markTaskRun(task, ms) { + if (enableProfiling) { + runIdCounter++; + profilingState[PRIORITY] = task.priorityLevel; + profilingState[CURRENT_TASK_ID] = task.id; + profilingState[CURRENT_RUN_ID] = runIdCounter; + if (eventLog !== null) { + logEvent([TaskRunEvent, ms * 1e3, task.id, runIdCounter]); + } + } + } + function markTaskYield(task, ms) { + if (enableProfiling) { + profilingState[PRIORITY] = NoPriority; + profilingState[CURRENT_TASK_ID] = 0; + profilingState[CURRENT_RUN_ID] = 0; + if (eventLog !== null) { + logEvent([TaskYieldEvent, ms * 1e3, task.id, runIdCounter]); + } + } + } + function markSchedulerSuspended(ms) { + if (enableProfiling) { + mainThreadIdCounter++; + if (eventLog !== null) { + logEvent([SchedulerSuspendEvent, ms * 1e3, mainThreadIdCounter]); + } + } + } + function markSchedulerUnsuspended(ms) { + if (enableProfiling) { + if (eventLog !== null) { + logEvent([SchedulerResumeEvent, ms * 1e3, mainThreadIdCounter]); + } + } + } + var maxSigned31BitInt = 1073741823; + var IMMEDIATE_PRIORITY_TIMEOUT = -1; + var USER_BLOCKING_PRIORITY = 250; + var NORMAL_PRIORITY_TIMEOUT = 5e3; + var LOW_PRIORITY_TIMEOUT = 1e4; + var IDLE_PRIORITY = maxSigned31BitInt; + var taskQueue = []; + var timerQueue = []; + var taskIdCounter = 1; + var isSchedulerPaused = false; + var currentTask = null; + var currentPriorityLevel = NormalPriority; + var isPerformingWork = false; + var isHostCallbackScheduled = false; + var isHostTimeoutScheduled = false; + function advanceTimers(currentTime) { + var timer = peek(timerQueue); + while (timer !== null) { + if (timer.callback === null) { + pop(timerQueue); + } else if (timer.startTime <= currentTime) { + pop(timerQueue); + timer.sortIndex = timer.expirationTime; + push(taskQueue, timer); + if (enableProfiling) { + markTaskStart(timer, currentTime); + timer.isQueued = true; + } + } else { + return; + } + timer = peek(timerQueue); + } + } + function handleTimeout(currentTime) { + isHostTimeoutScheduled = false; + advanceTimers(currentTime); + if (!isHostCallbackScheduled) { + if (peek(taskQueue) !== null) { + isHostCallbackScheduled = true; + requestHostCallback(flushWork); + } else { + var firstTimer = peek(timerQueue); + if (firstTimer !== null) { + requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); + } + } + } + } + function flushWork(hasTimeRemaining, initialTime2) { + if (enableProfiling) { + markSchedulerUnsuspended(initialTime2); + } + isHostCallbackScheduled = false; + if (isHostTimeoutScheduled) { + isHostTimeoutScheduled = false; + cancelHostTimeout(); + } + isPerformingWork = true; + var previousPriorityLevel = currentPriorityLevel; + try { + if (enableProfiling) { + try { + return workLoop(hasTimeRemaining, initialTime2); + } catch (error) { + if (currentTask !== null) { + var currentTime = exports.unstable_now(); + markTaskErrored(currentTask, currentTime); + currentTask.isQueued = false; + } + throw error; + } + } else { + return workLoop(hasTimeRemaining, initialTime2); + } + } finally { + currentTask = null; + currentPriorityLevel = previousPriorityLevel; + isPerformingWork = false; + if (enableProfiling) { + var _currentTime = exports.unstable_now(); + markSchedulerSuspended(_currentTime); + } + } + } + function workLoop(hasTimeRemaining, initialTime2) { + var currentTime = initialTime2; + advanceTimers(currentTime); + currentTask = peek(taskQueue); + while (currentTask !== null && !(enableSchedulerDebugging && isSchedulerPaused)) { + if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) { + break; + } + var callback = currentTask.callback; + if (callback !== null) { + currentTask.callback = null; + currentPriorityLevel = currentTask.priorityLevel; + var didUserCallbackTimeout = currentTask.expirationTime <= currentTime; + markTaskRun(currentTask, currentTime); + var continuationCallback = callback(didUserCallbackTimeout); + currentTime = exports.unstable_now(); + if (typeof continuationCallback === "function") { + currentTask.callback = continuationCallback; + markTaskYield(currentTask, currentTime); + } else { + if (enableProfiling) { + markTaskCompleted(currentTask, currentTime); + currentTask.isQueued = false; + } + if (currentTask === peek(taskQueue)) { + pop(taskQueue); + } + } + advanceTimers(currentTime); + } else { + pop(taskQueue); + } + currentTask = peek(taskQueue); + } + if (currentTask !== null) { + return true; + } else { + var firstTimer = peek(timerQueue); + if (firstTimer !== null) { + requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); + } + return false; + } + } + function unstable_runWithPriority(priorityLevel, eventHandler) { + switch (priorityLevel) { + case ImmediatePriority: + case UserBlockingPriority: + case NormalPriority: + case LowPriority: + case IdlePriority: + break; + default: + priorityLevel = NormalPriority; + } + var previousPriorityLevel = currentPriorityLevel; + currentPriorityLevel = priorityLevel; + try { + return eventHandler(); + } finally { + currentPriorityLevel = previousPriorityLevel; + } + } + function unstable_next(eventHandler) { + var priorityLevel; + switch (currentPriorityLevel) { + case ImmediatePriority: + case UserBlockingPriority: + case NormalPriority: + priorityLevel = NormalPriority; + break; + default: + priorityLevel = currentPriorityLevel; + break; + } + var previousPriorityLevel = currentPriorityLevel; + currentPriorityLevel = priorityLevel; + try { + return eventHandler(); + } finally { + currentPriorityLevel = previousPriorityLevel; + } + } + function unstable_wrapCallback(callback) { + var parentPriorityLevel = currentPriorityLevel; + return function() { + var previousPriorityLevel = currentPriorityLevel; + currentPriorityLevel = parentPriorityLevel; + try { + return callback.apply(this, arguments); + } finally { + currentPriorityLevel = previousPriorityLevel; + } + }; + } + function timeoutForPriorityLevel(priorityLevel) { + switch (priorityLevel) { + case ImmediatePriority: + return IMMEDIATE_PRIORITY_TIMEOUT; + case UserBlockingPriority: + return USER_BLOCKING_PRIORITY; + case IdlePriority: + return IDLE_PRIORITY; + case LowPriority: + return LOW_PRIORITY_TIMEOUT; + case NormalPriority: + default: + return NORMAL_PRIORITY_TIMEOUT; + } + } + function unstable_scheduleCallback(priorityLevel, callback, options) { + var currentTime = exports.unstable_now(); + var startTime; + var timeout; + if (typeof options === "object" && options !== null) { + var delay = options.delay; + if (typeof delay === "number" && delay > 0) { + startTime = currentTime + delay; + } else { + startTime = currentTime; + } + timeout = typeof options.timeout === "number" ? options.timeout : timeoutForPriorityLevel(priorityLevel); + } else { + timeout = timeoutForPriorityLevel(priorityLevel); + startTime = currentTime; + } + var expirationTime = startTime + timeout; + var newTask = { + id: taskIdCounter++, + callback, + priorityLevel, + startTime, + expirationTime, + sortIndex: -1 + }; + if (enableProfiling) { + newTask.isQueued = false; + } + if (startTime > currentTime) { + newTask.sortIndex = startTime; + push(timerQueue, newTask); + if (peek(taskQueue) === null && newTask === peek(timerQueue)) { + if (isHostTimeoutScheduled) { + cancelHostTimeout(); + } else { + isHostTimeoutScheduled = true; + } + requestHostTimeout(handleTimeout, startTime - currentTime); + } + } else { + newTask.sortIndex = expirationTime; + push(taskQueue, newTask); + if (enableProfiling) { + markTaskStart(newTask, currentTime); + newTask.isQueued = true; + } + if (!isHostCallbackScheduled && !isPerformingWork) { + isHostCallbackScheduled = true; + requestHostCallback(flushWork); + } + } + return newTask; + } + function unstable_pauseExecution() { + isSchedulerPaused = true; + } + function unstable_continueExecution() { + isSchedulerPaused = false; + if (!isHostCallbackScheduled && !isPerformingWork) { + isHostCallbackScheduled = true; + requestHostCallback(flushWork); + } + } + function unstable_getFirstCallbackNode() { + return peek(taskQueue); + } + function unstable_cancelCallback(task) { + if (enableProfiling) { + if (task.isQueued) { + var currentTime = exports.unstable_now(); + markTaskCanceled(task, currentTime); + task.isQueued = false; + } + } + task.callback = null; + } + function unstable_getCurrentPriorityLevel() { + return currentPriorityLevel; + } + function unstable_shouldYield() { + var currentTime = exports.unstable_now(); + advanceTimers(currentTime); + var firstTask = peek(taskQueue); + return firstTask !== currentTask && currentTask !== null && firstTask !== null && firstTask.callback !== null && firstTask.startTime <= currentTime && firstTask.expirationTime < currentTask.expirationTime || shouldYieldToHost(); + } + var unstable_requestPaint = requestPaint; + var unstable_Profiling = enableProfiling ? { + startLoggingProfilingEvents, + stopLoggingProfilingEvents, + sharedProfilingBuffer + } : null; + exports.unstable_ImmediatePriority = ImmediatePriority; + exports.unstable_UserBlockingPriority = UserBlockingPriority; + exports.unstable_NormalPriority = NormalPriority; + exports.unstable_IdlePriority = IdlePriority; + exports.unstable_LowPriority = LowPriority; + exports.unstable_runWithPriority = unstable_runWithPriority; + exports.unstable_next = unstable_next; + exports.unstable_scheduleCallback = unstable_scheduleCallback; + exports.unstable_cancelCallback = unstable_cancelCallback; + exports.unstable_wrapCallback = unstable_wrapCallback; + exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel; + exports.unstable_shouldYield = unstable_shouldYield; + exports.unstable_requestPaint = unstable_requestPaint; + exports.unstable_continueExecution = unstable_continueExecution; + exports.unstable_pauseExecution = unstable_pauseExecution; + exports.unstable_getFirstCallbackNode = unstable_getFirstCallbackNode; + exports.unstable_Profiling = unstable_Profiling; + })(); + } + } + }); + + // node_modules/scheduler/index.js + var require_scheduler = __commonJS({ + "node_modules/scheduler/index.js"(exports, module) { + "use strict"; + if (false) { + module.exports = null; + } else { + module.exports = require_scheduler_development(); + } + } + }); + + // node_modules/scheduler/cjs/scheduler-tracing.development.js + var require_scheduler_tracing_development = __commonJS({ + "node_modules/scheduler/cjs/scheduler-tracing.development.js"(exports) { + "use strict"; + if (true) { + (function() { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var enableSchedulerTracing = true; + var DEFAULT_THREAD_ID = 0; + var interactionIDCounter = 0; + var threadIDCounter = 0; + exports.__interactionsRef = null; + exports.__subscriberRef = null; + if (enableSchedulerTracing) { + exports.__interactionsRef = { + current: /* @__PURE__ */ new Set() + }; + exports.__subscriberRef = { + current: null + }; + } + function unstable_clear(callback) { + if (!enableSchedulerTracing) { + return callback(); + } + var prevInteractions = exports.__interactionsRef.current; + exports.__interactionsRef.current = /* @__PURE__ */ new Set(); + try { + return callback(); + } finally { + exports.__interactionsRef.current = prevInteractions; + } + } + function unstable_getCurrent() { + if (!enableSchedulerTracing) { + return null; + } else { + return exports.__interactionsRef.current; + } + } + function unstable_getThreadID() { + return ++threadIDCounter; + } + function unstable_trace(name, timestamp, callback) { + var threadID = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : DEFAULT_THREAD_ID; + if (!enableSchedulerTracing) { + return callback(); + } + var interaction = { + __count: 1, + id: interactionIDCounter++, + name, + timestamp + }; + var prevInteractions = exports.__interactionsRef.current; + var interactions = new Set(prevInteractions); + interactions.add(interaction); + exports.__interactionsRef.current = interactions; + var subscriber = exports.__subscriberRef.current; + var returnValue; + try { + if (subscriber !== null) { + subscriber.onInteractionTraced(interaction); + } + } finally { + try { + if (subscriber !== null) { + subscriber.onWorkStarted(interactions, threadID); + } + } finally { + try { + returnValue = callback(); + } finally { + exports.__interactionsRef.current = prevInteractions; + try { + if (subscriber !== null) { + subscriber.onWorkStopped(interactions, threadID); + } + } finally { + interaction.__count--; + if (subscriber !== null && interaction.__count === 0) { + subscriber.onInteractionScheduledWorkCompleted(interaction); + } + } + } + } + } + return returnValue; + } + function unstable_wrap(callback) { + var threadID = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_THREAD_ID; + if (!enableSchedulerTracing) { + return callback; + } + var wrappedInteractions = exports.__interactionsRef.current; + var subscriber = exports.__subscriberRef.current; + if (subscriber !== null) { + subscriber.onWorkScheduled(wrappedInteractions, threadID); + } + wrappedInteractions.forEach(function(interaction) { + interaction.__count++; + }); + var hasRun = false; + function wrapped() { + var prevInteractions = exports.__interactionsRef.current; + exports.__interactionsRef.current = wrappedInteractions; + subscriber = exports.__subscriberRef.current; + try { + var returnValue; + try { + if (subscriber !== null) { + subscriber.onWorkStarted(wrappedInteractions, threadID); + } + } finally { + try { + returnValue = callback.apply(void 0, arguments); + } finally { + exports.__interactionsRef.current = prevInteractions; + if (subscriber !== null) { + subscriber.onWorkStopped(wrappedInteractions, threadID); + } + } + } + return returnValue; + } finally { + if (!hasRun) { + hasRun = true; + wrappedInteractions.forEach(function(interaction) { + interaction.__count--; + if (subscriber !== null && interaction.__count === 0) { + subscriber.onInteractionScheduledWorkCompleted(interaction); + } + }); + } + } + } + wrapped.cancel = function cancel() { + subscriber = exports.__subscriberRef.current; + try { + if (subscriber !== null) { + subscriber.onWorkCanceled(wrappedInteractions, threadID); + } + } finally { + wrappedInteractions.forEach(function(interaction) { + interaction.__count--; + if (subscriber && interaction.__count === 0) { + subscriber.onInteractionScheduledWorkCompleted(interaction); + } + }); + } + }; + return wrapped; + } + var subscribers = null; + if (enableSchedulerTracing) { + subscribers = /* @__PURE__ */ new Set(); + } + function unstable_subscribe(subscriber) { + if (enableSchedulerTracing) { + subscribers.add(subscriber); + if (subscribers.size === 1) { + exports.__subscriberRef.current = { + onInteractionScheduledWorkCompleted, + onInteractionTraced, + onWorkCanceled, + onWorkScheduled, + onWorkStarted, + onWorkStopped + }; + } + } + } + function unstable_unsubscribe(subscriber) { + if (enableSchedulerTracing) { + subscribers.delete(subscriber); + if (subscribers.size === 0) { + exports.__subscriberRef.current = null; + } + } + } + function onInteractionTraced(interaction) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onInteractionTraced(interaction); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + function onInteractionScheduledWorkCompleted(interaction) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onInteractionScheduledWorkCompleted(interaction); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + function onWorkScheduled(interactions, threadID) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onWorkScheduled(interactions, threadID); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + function onWorkStarted(interactions, threadID) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onWorkStarted(interactions, threadID); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + function onWorkStopped(interactions, threadID) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onWorkStopped(interactions, threadID); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + function onWorkCanceled(interactions, threadID) { + var didCatchError = false; + var caughtError = null; + subscribers.forEach(function(subscriber) { + try { + subscriber.onWorkCanceled(interactions, threadID); + } catch (error) { + if (!didCatchError) { + didCatchError = true; + caughtError = error; + } + } + }); + if (didCatchError) { + throw caughtError; + } + } + exports.unstable_clear = unstable_clear; + exports.unstable_getCurrent = unstable_getCurrent; + exports.unstable_getThreadID = unstable_getThreadID; + exports.unstable_trace = unstable_trace; + exports.unstable_wrap = unstable_wrap; + exports.unstable_subscribe = unstable_subscribe; + exports.unstable_unsubscribe = unstable_unsubscribe; + })(); + } + } + }); + + // node_modules/scheduler/tracing.js + var require_tracing = __commonJS({ + "node_modules/scheduler/tracing.js"(exports, module) { + "use strict"; + if (false) { + module.exports = null; + } else { + module.exports = require_scheduler_tracing_development(); + } + } + }); + + // node_modules/react-dom/cjs/react-dom.development.js + var require_react_dom_development = __commonJS({ + "node_modules/react-dom/cjs/react-dom.development.js"(exports, module) { + "use strict"; + if (true) { + (function() { + "use strict"; + var React7 = require_react(); + var _assign = require_object_assign(); + var Scheduler = require_scheduler(); + var checkPropTypes = require_checkPropTypes(); + var tracing = require_tracing(); + if (!React7) { + { + throw Error("ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM."); + } + } + var eventPluginOrder = null; + var namesToPlugins = {}; + function recomputePluginOrdering() { + if (!eventPluginOrder) { + return; + } + for (var pluginName in namesToPlugins) { + var pluginModule = namesToPlugins[pluginName]; + var pluginIndex = eventPluginOrder.indexOf(pluginName); + if (!(pluginIndex > -1)) { + { + throw Error("EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `" + pluginName + "`."); + } + } + if (plugins[pluginIndex]) { + continue; + } + if (!pluginModule.extractEvents) { + { + throw Error("EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `" + pluginName + "` does not."); + } + } + plugins[pluginIndex] = pluginModule; + var publishedEvents = pluginModule.eventTypes; + for (var eventName in publishedEvents) { + if (!publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName)) { + { + throw Error("EventPluginRegistry: Failed to publish event `" + eventName + "` for plugin `" + pluginName + "`."); + } + } + } + } + } + function publishEventForPlugin(dispatchConfig, pluginModule, eventName) { + if (!!eventNameDispatchConfigs.hasOwnProperty(eventName)) { + { + throw Error("EventPluginHub: More than one plugin attempted to publish the same event name, `" + eventName + "`."); + } + } + eventNameDispatchConfigs[eventName] = dispatchConfig; + var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames; + if (phasedRegistrationNames) { + for (var phaseName in phasedRegistrationNames) { + if (phasedRegistrationNames.hasOwnProperty(phaseName)) { + var phasedRegistrationName = phasedRegistrationNames[phaseName]; + publishRegistrationName(phasedRegistrationName, pluginModule, eventName); + } + } + return true; + } else if (dispatchConfig.registrationName) { + publishRegistrationName(dispatchConfig.registrationName, pluginModule, eventName); + return true; + } + return false; + } + function publishRegistrationName(registrationName, pluginModule, eventName) { + if (!!registrationNameModules[registrationName]) { + { + throw Error("EventPluginHub: More than one plugin attempted to publish the same registration name, `" + registrationName + "`."); + } + } + registrationNameModules[registrationName] = pluginModule; + registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies; + { + var lowerCasedName = registrationName.toLowerCase(); + possibleRegistrationNames[lowerCasedName] = registrationName; + if (registrationName === "onDoubleClick") { + possibleRegistrationNames.ondblclick = registrationName; + } + } + } + var plugins = []; + var eventNameDispatchConfigs = {}; + var registrationNameModules = {}; + var registrationNameDependencies = {}; + var possibleRegistrationNames = {}; + function injectEventPluginOrder(injectedEventPluginOrder) { + if (!!eventPluginOrder) { + { + throw Error("EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React."); + } + } + eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder); + recomputePluginOrdering(); + } + function injectEventPluginsByName(injectedNamesToPlugins) { + var isOrderingDirty = false; + for (var pluginName in injectedNamesToPlugins) { + if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { + continue; + } + var pluginModule = injectedNamesToPlugins[pluginName]; + if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== pluginModule) { + if (!!namesToPlugins[pluginName]) { + { + throw Error("EventPluginRegistry: Cannot inject two different event plugins using the same name, `" + pluginName + "`."); + } + } + namesToPlugins[pluginName] = pluginModule; + isOrderingDirty = true; + } + } + if (isOrderingDirty) { + recomputePluginOrdering(); + } + } + var invokeGuardedCallbackImpl = function(name, func, context, a, b, c, d, e, f) { + var funcArgs = Array.prototype.slice.call(arguments, 3); + try { + func.apply(context, funcArgs); + } catch (error) { + this.onError(error); + } + }; + { + if (typeof window !== "undefined" && typeof window.dispatchEvent === "function" && typeof document !== "undefined" && typeof document.createEvent === "function") { + var fakeNode = document.createElement("react"); + var invokeGuardedCallbackDev = function(name, func, context, a, b, c, d, e, f) { + if (!(typeof document !== "undefined")) { + { + throw Error("The `document` global was defined when React was initialized, but is not defined anymore. This can happen in a test environment if a component schedules an update from an asynchronous callback, but the test has already finished running. To solve this, you can either unmount the component at the end of your test (and ensure that any asynchronous operations get canceled in `componentWillUnmount`), or you can change the test itself to be asynchronous."); + } + } + var evt = document.createEvent("Event"); + var didError = true; + var windowEvent = window.event; + var windowEventDescriptor = Object.getOwnPropertyDescriptor(window, "event"); + var funcArgs = Array.prototype.slice.call(arguments, 3); + function callCallback2() { + fakeNode.removeEventListener(evtType, callCallback2, false); + if (typeof window.event !== "undefined" && window.hasOwnProperty("event")) { + window.event = windowEvent; + } + func.apply(context, funcArgs); + didError = false; + } + var error; + var didSetError = false; + var isCrossOriginError = false; + function handleWindowError(event2) { + error = event2.error; + didSetError = true; + if (error === null && event2.colno === 0 && event2.lineno === 0) { + isCrossOriginError = true; + } + if (event2.defaultPrevented) { + if (error != null && typeof error === "object") { + try { + error._suppressLogging = true; + } catch (inner) { + } + } + } + } + var evtType = "react-" + (name ? name : "invokeguardedcallback"); + window.addEventListener("error", handleWindowError); + fakeNode.addEventListener(evtType, callCallback2, false); + evt.initEvent(evtType, false, false); + fakeNode.dispatchEvent(evt); + if (windowEventDescriptor) { + Object.defineProperty(window, "event", windowEventDescriptor); + } + if (didError) { + if (!didSetError) { + error = new Error(`An error was thrown inside one of your components, but React doesn't know what it was. This is likely due to browser flakiness. React does its best to preserve the "Pause on exceptions" behavior of the DevTools, which requires some DEV-mode only tricks. It's possible that these don't work in your browser. Try triggering the error in production mode, or switching to a modern browser. If you suspect that this is actually an issue with React, please file an issue.`); + } else if (isCrossOriginError) { + error = new Error("A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://fb.me/react-crossorigin-error for more information."); + } + this.onError(error); + } + window.removeEventListener("error", handleWindowError); + }; + invokeGuardedCallbackImpl = invokeGuardedCallbackDev; + } + } + var invokeGuardedCallbackImpl$1 = invokeGuardedCallbackImpl; + var hasError = false; + var caughtError = null; + var hasRethrowError = false; + var rethrowError = null; + var reporter = { + onError: function(error) { + hasError = true; + caughtError = error; + } + }; + function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) { + hasError = false; + caughtError = null; + invokeGuardedCallbackImpl$1.apply(reporter, arguments); + } + function invokeGuardedCallbackAndCatchFirstError(name, func, context, a, b, c, d, e, f) { + invokeGuardedCallback.apply(this, arguments); + if (hasError) { + var error = clearCaughtError(); + if (!hasRethrowError) { + hasRethrowError = true; + rethrowError = error; + } + } + } + function rethrowCaughtError() { + if (hasRethrowError) { + var error = rethrowError; + hasRethrowError = false; + rethrowError = null; + throw error; + } + } + function hasCaughtError() { + return hasError; + } + function clearCaughtError() { + if (hasError) { + var error = caughtError; + hasError = false; + caughtError = null; + return error; + } else { + { + { + throw Error("clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue."); + } + } + } + } + var warningWithoutStack = function() { + }; + { + warningWithoutStack = function(condition, format) { + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + if (format === void 0) { + throw new Error("`warningWithoutStack(condition, format, ...args)` requires a warning message argument"); + } + if (args.length > 8) { + throw new Error("warningWithoutStack() currently supports at most 8 arguments."); + } + if (condition) { + return; + } + if (typeof console !== "undefined") { + var argsWithFormat = args.map(function(item) { + return "" + item; + }); + argsWithFormat.unshift("Warning: " + format); + Function.prototype.apply.call(console.error, console, argsWithFormat); + } + try { + var argIndex = 0; + var message = "Warning: " + format.replace(/%s/g, function() { + return args[argIndex++]; + }); + throw new Error(message); + } catch (x) { + } + }; + } + var warningWithoutStack$1 = warningWithoutStack; + var getFiberCurrentPropsFromNode = null; + var getInstanceFromNode = null; + var getNodeFromInstance = null; + function setComponentTree(getFiberCurrentPropsFromNodeImpl, getInstanceFromNodeImpl, getNodeFromInstanceImpl) { + getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl; + getInstanceFromNode = getInstanceFromNodeImpl; + getNodeFromInstance = getNodeFromInstanceImpl; + { + !(getNodeFromInstance && getInstanceFromNode) ? warningWithoutStack$1(false, "EventPluginUtils.setComponentTree(...): Injected module is missing getNodeFromInstance or getInstanceFromNode.") : void 0; + } + } + var validateEventDispatches; + { + validateEventDispatches = function(event2) { + var dispatchListeners = event2._dispatchListeners; + var dispatchInstances = event2._dispatchInstances; + var listenersIsArr = Array.isArray(dispatchListeners); + var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0; + var instancesIsArr = Array.isArray(dispatchInstances); + var instancesLen = instancesIsArr ? dispatchInstances.length : dispatchInstances ? 1 : 0; + !(instancesIsArr === listenersIsArr && instancesLen === listenersLen) ? warningWithoutStack$1(false, "EventPluginUtils: Invalid `event`.") : void 0; + }; + } + function executeDispatch(event2, listener, inst) { + var type = event2.type || "unknown-event"; + event2.currentTarget = getNodeFromInstance(inst); + invokeGuardedCallbackAndCatchFirstError(type, listener, void 0, event2); + event2.currentTarget = null; + } + function executeDispatchesInOrder(event2) { + var dispatchListeners = event2._dispatchListeners; + var dispatchInstances = event2._dispatchInstances; + { + validateEventDispatches(event2); + } + if (Array.isArray(dispatchListeners)) { + for (var i2 = 0; i2 < dispatchListeners.length; i2++) { + if (event2.isPropagationStopped()) { + break; + } + executeDispatch(event2, dispatchListeners[i2], dispatchInstances[i2]); + } + } else if (dispatchListeners) { + executeDispatch(event2, dispatchListeners, dispatchInstances); + } + event2._dispatchListeners = null; + event2._dispatchInstances = null; + } + function accumulateInto(current2, next) { + if (!(next != null)) { + { + throw Error("accumulateInto(...): Accumulated items must not be null or undefined."); + } + } + if (current2 == null) { + return next; + } + if (Array.isArray(current2)) { + if (Array.isArray(next)) { + current2.push.apply(current2, next); + return current2; + } + current2.push(next); + return current2; + } + if (Array.isArray(next)) { + return [current2].concat(next); + } + return [current2, next]; + } + function forEachAccumulated(arr, cb, scope) { + if (Array.isArray(arr)) { + arr.forEach(cb, scope); + } else if (arr) { + cb.call(scope, arr); + } + } + var eventQueue = null; + var executeDispatchesAndRelease = function(event2) { + if (event2) { + executeDispatchesInOrder(event2); + if (!event2.isPersistent()) { + event2.constructor.release(event2); + } + } + }; + var executeDispatchesAndReleaseTopLevel = function(e) { + return executeDispatchesAndRelease(e); + }; + function runEventsInBatch(events) { + if (events !== null) { + eventQueue = accumulateInto(eventQueue, events); + } + var processingEventQueue = eventQueue; + eventQueue = null; + if (!processingEventQueue) { + return; + } + forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel); + if (!!eventQueue) { + { + throw Error("processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."); + } + } + rethrowCaughtError(); + } + function isInteractive(tag) { + return tag === "button" || tag === "input" || tag === "select" || tag === "textarea"; + } + function shouldPreventMouseEvent(name, type, props) { + switch (name) { + case "onClick": + case "onClickCapture": + case "onDoubleClick": + case "onDoubleClickCapture": + case "onMouseDown": + case "onMouseDownCapture": + case "onMouseMove": + case "onMouseMoveCapture": + case "onMouseUp": + case "onMouseUpCapture": + return !!(props.disabled && isInteractive(type)); + default: + return false; + } + } + var injection = { + /** + * @param {array} InjectedEventPluginOrder + * @public + */ + injectEventPluginOrder, + /** + * @param {object} injectedNamesToPlugins Map from names to plugin modules. + */ + injectEventPluginsByName + }; + function getListener(inst, registrationName) { + var listener; + var stateNode = inst.stateNode; + if (!stateNode) { + return null; + } + var props = getFiberCurrentPropsFromNode(stateNode); + if (!props) { + return null; + } + listener = props[registrationName]; + if (shouldPreventMouseEvent(registrationName, inst.type, props)) { + return null; + } + if (!(!listener || typeof listener === "function")) { + { + throw Error("Expected `" + registrationName + "` listener to be a function, instead got a value of `" + typeof listener + "` type."); + } + } + return listener; + } + function extractPluginEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags) { + var events = null; + for (var i2 = 0; i2 < plugins.length; i2++) { + var possiblePlugin = plugins[i2]; + if (possiblePlugin) { + var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); + if (extractedEvents) { + events = accumulateInto(events, extractedEvents); + } + } + } + return events; + } + function runExtractedPluginEventsInBatch(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags) { + var events = extractPluginEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); + runEventsInBatch(events); + } + var FunctionComponent = 0; + var ClassComponent = 1; + var IndeterminateComponent = 2; + var HostRoot = 3; + var HostPortal = 4; + var HostComponent = 5; + var HostText = 6; + var Fragment = 7; + var Mode = 8; + var ContextConsumer = 9; + var ContextProvider = 10; + var ForwardRef = 11; + var Profiler = 12; + var SuspenseComponent = 13; + var MemoComponent = 14; + var SimpleMemoComponent = 15; + var LazyComponent = 16; + var IncompleteClassComponent = 17; + var DehydratedFragment = 18; + var SuspenseListComponent = 19; + var FundamentalComponent = 20; + var ScopeComponent = 21; + var ReactSharedInternals = React7.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + if (!ReactSharedInternals.hasOwnProperty("ReactCurrentDispatcher")) { + ReactSharedInternals.ReactCurrentDispatcher = { + current: null + }; + } + if (!ReactSharedInternals.hasOwnProperty("ReactCurrentBatchConfig")) { + ReactSharedInternals.ReactCurrentBatchConfig = { + suspense: null + }; + } + var BEFORE_SLASH_RE = /^(.*)[\\\/]/; + var describeComponentFrame = function(name, source, ownerName) { + var sourceInfo = ""; + if (source) { + var path = source.fileName; + var fileName = path.replace(BEFORE_SLASH_RE, ""); + { + if (/^index\./.test(fileName)) { + var match = path.match(BEFORE_SLASH_RE); + if (match) { + var pathBeforeSlash = match[1]; + if (pathBeforeSlash) { + var folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, ""); + fileName = folderName + "/" + fileName; + } + } + } + } + sourceInfo = " (at " + fileName + ":" + source.lineNumber + ")"; + } else if (ownerName) { + sourceInfo = " (created by " + ownerName + ")"; + } + return "\n in " + (name || "Unknown") + sourceInfo; + }; + var hasSymbol = typeof Symbol === "function" && Symbol.for; + var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for("react.element") : 60103; + var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for("react.portal") : 60106; + var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for("react.fragment") : 60107; + var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for("react.strict_mode") : 60108; + var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for("react.profiler") : 60114; + var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for("react.provider") : 60109; + var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for("react.context") : 60110; + var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for("react.concurrent_mode") : 60111; + var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for("react.forward_ref") : 60112; + var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for("react.suspense") : 60113; + var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for("react.suspense_list") : 60120; + var REACT_MEMO_TYPE = hasSymbol ? Symbol.for("react.memo") : 60115; + var REACT_LAZY_TYPE = hasSymbol ? Symbol.for("react.lazy") : 60116; + var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for("react.fundamental") : 60117; + var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for("react.responder") : 60118; + var REACT_SCOPE_TYPE = hasSymbol ? Symbol.for("react.scope") : 60119; + var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; + var FAUX_ITERATOR_SYMBOL = "@@iterator"; + function getIteratorFn(maybeIterable) { + if (maybeIterable === null || typeof maybeIterable !== "object") { + return null; + } + var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; + if (typeof maybeIterator === "function") { + return maybeIterator; + } + return null; + } + var warning = warningWithoutStack$1; + { + warning = function(condition, format) { + if (condition) { + return; + } + var ReactDebugCurrentFrame2 = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame2.getStackAddendum(); + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + warningWithoutStack$1.apply(void 0, [false, format + "%s"].concat(args, [stack])); + }; + } + var warning$1 = warning; + var Uninitialized = -1; + var Pending = 0; + var Resolved = 1; + var Rejected = 2; + function refineResolvedLazyComponent(lazyComponent) { + return lazyComponent._status === Resolved ? lazyComponent._result : null; + } + function initializeLazyComponentType(lazyComponent) { + if (lazyComponent._status === Uninitialized) { + lazyComponent._status = Pending; + var ctor = lazyComponent._ctor; + var thenable = ctor(); + lazyComponent._result = thenable; + thenable.then(function(moduleObject) { + if (lazyComponent._status === Pending) { + var defaultExport = moduleObject.default; + { + if (defaultExport === void 0) { + warning$1(false, "lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))", moduleObject); + } + } + lazyComponent._status = Resolved; + lazyComponent._result = defaultExport; + } + }, function(error) { + if (lazyComponent._status === Pending) { + lazyComponent._status = Rejected; + lazyComponent._result = error; + } + }); + } + } + function getWrappedName(outerType, innerType, wrapperName) { + var functionName = innerType.displayName || innerType.name || ""; + return outerType.displayName || (functionName !== "" ? wrapperName + "(" + functionName + ")" : wrapperName); + } + function getComponentName(type) { + if (type == null) { + return null; + } + { + if (typeof type.tag === "number") { + warningWithoutStack$1(false, "Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."); + } + } + if (typeof type === "function") { + return type.displayName || type.name || null; + } + if (typeof type === "string") { + return type; + } + switch (type) { + case REACT_FRAGMENT_TYPE: + return "Fragment"; + case REACT_PORTAL_TYPE: + return "Portal"; + case REACT_PROFILER_TYPE: + return "Profiler"; + case REACT_STRICT_MODE_TYPE: + return "StrictMode"; + case REACT_SUSPENSE_TYPE: + return "Suspense"; + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + } + if (typeof type === "object") { + switch (type.$$typeof) { + case REACT_CONTEXT_TYPE: + return "Context.Consumer"; + case REACT_PROVIDER_TYPE: + return "Context.Provider"; + case REACT_FORWARD_REF_TYPE: + return getWrappedName(type, type.render, "ForwardRef"); + case REACT_MEMO_TYPE: + return getComponentName(type.type); + case REACT_LAZY_TYPE: { + var thenable = type; + var resolvedThenable = refineResolvedLazyComponent(thenable); + if (resolvedThenable) { + return getComponentName(resolvedThenable); + } + break; + } + } + } + return null; + } + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + function describeFiber(fiber) { + switch (fiber.tag) { + case HostRoot: + case HostPortal: + case HostText: + case Fragment: + case ContextProvider: + case ContextConsumer: + return ""; + default: + var owner = fiber._debugOwner; + var source = fiber._debugSource; + var name = getComponentName(fiber.type); + var ownerName = null; + if (owner) { + ownerName = getComponentName(owner.type); + } + return describeComponentFrame(name, source, ownerName); + } + } + function getStackByFiberInDevAndProd(workInProgress2) { + var info = ""; + var node = workInProgress2; + do { + info += describeFiber(node); + node = node.return; + } while (node); + return info; + } + var current = null; + var phase = null; + function getCurrentFiberOwnerNameInDevOrNull() { + { + if (current === null) { + return null; + } + var owner = current._debugOwner; + if (owner !== null && typeof owner !== "undefined") { + return getComponentName(owner.type); + } + } + return null; + } + function getCurrentFiberStackInDev() { + { + if (current === null) { + return ""; + } + return getStackByFiberInDevAndProd(current); + } + return ""; + } + function resetCurrentFiber() { + { + ReactDebugCurrentFrame.getCurrentStack = null; + current = null; + phase = null; + } + } + function setCurrentFiber(fiber) { + { + ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackInDev; + current = fiber; + phase = null; + } + } + function setCurrentPhase(lifeCyclePhase) { + { + phase = lifeCyclePhase; + } + } + var canUseDOM = !!(typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined"); + function endsWith(subject, search) { + var length = subject.length; + return subject.substring(length - search.length, length) === search; + } + var PLUGIN_EVENT_SYSTEM = 1; + var RESPONDER_EVENT_SYSTEM = 1 << 1; + var IS_PASSIVE = 1 << 2; + var IS_ACTIVE = 1 << 3; + var PASSIVE_NOT_SUPPORTED = 1 << 4; + var IS_REPLAYED = 1 << 5; + var restoreImpl = null; + var restoreTarget = null; + var restoreQueue = null; + function restoreStateOfTarget(target) { + var internalInstance = getInstanceFromNode(target); + if (!internalInstance) { + return; + } + if (!(typeof restoreImpl === "function")) { + { + throw Error("setRestoreImplementation() needs to be called to handle a target for controlled events. This error is likely caused by a bug in React. Please file an issue."); + } + } + var props = getFiberCurrentPropsFromNode(internalInstance.stateNode); + restoreImpl(internalInstance.stateNode, internalInstance.type, props); + } + function setRestoreImplementation(impl) { + restoreImpl = impl; + } + function enqueueStateRestore(target) { + if (restoreTarget) { + if (restoreQueue) { + restoreQueue.push(target); + } else { + restoreQueue = [target]; + } + } else { + restoreTarget = target; + } + } + function needsStateRestore() { + return restoreTarget !== null || restoreQueue !== null; + } + function restoreStateIfNeeded() { + if (!restoreTarget) { + return; + } + var target = restoreTarget; + var queuedTargets = restoreQueue; + restoreTarget = null; + restoreQueue = null; + restoreStateOfTarget(target); + if (queuedTargets) { + for (var i2 = 0; i2 < queuedTargets.length; i2++) { + restoreStateOfTarget(queuedTargets[i2]); + } + } + } + var enableUserTimingAPI = true; + var debugRenderPhaseSideEffects = false; + var debugRenderPhaseSideEffectsForStrictMode = true; + var replayFailedUnitOfWorkWithInvokeGuardedCallback = true; + var warnAboutDeprecatedLifecycles = true; + var enableProfilerTimer = true; + var enableSchedulerTracing = true; + var enableSuspenseServerRenderer = false; + var enableSelectiveHydration = false; + var disableJavaScriptURLs = false; + var disableInputAttributeSyncing = false; + var exposeConcurrentModeAPIs = false; + var warnAboutShorthandPropertyCollision = false; + var enableFlareAPI = false; + var enableFundamentalAPI = false; + var enableScopeAPI = false; + var warnAboutUnmockedScheduler = false; + var flushSuspenseFallbacksInTests = true; + var enableSuspenseCallback = false; + var warnAboutDefaultPropsOnFunctionComponents = false; + var warnAboutStringRefs = false; + var disableLegacyContext = false; + var disableSchedulerTimeoutBasedOnReactExpirationTime = false; + var enableTrustedTypesIntegration = false; + var batchedUpdatesImpl = function(fn, bookkeeping) { + return fn(bookkeeping); + }; + var discreteUpdatesImpl = function(fn, a, b, c) { + return fn(a, b, c); + }; + var flushDiscreteUpdatesImpl = function() { + }; + var batchedEventUpdatesImpl = batchedUpdatesImpl; + var isInsideEventHandler = false; + var isBatchingEventUpdates = false; + function finishEventHandler() { + var controlledComponentsHavePendingUpdates = needsStateRestore(); + if (controlledComponentsHavePendingUpdates) { + flushDiscreteUpdatesImpl(); + restoreStateIfNeeded(); + } + } + function batchedUpdates(fn, bookkeeping) { + if (isInsideEventHandler) { + return fn(bookkeeping); + } + isInsideEventHandler = true; + try { + return batchedUpdatesImpl(fn, bookkeeping); + } finally { + isInsideEventHandler = false; + finishEventHandler(); + } + } + function batchedEventUpdates(fn, a, b) { + if (isBatchingEventUpdates) { + return fn(a, b); + } + isBatchingEventUpdates = true; + try { + return batchedEventUpdatesImpl(fn, a, b); + } finally { + isBatchingEventUpdates = false; + finishEventHandler(); + } + } + function executeUserEventHandler(fn, value) { + var previouslyInEventHandler = isInsideEventHandler; + try { + isInsideEventHandler = true; + var type = typeof value === "object" && value !== null ? value.type : ""; + invokeGuardedCallbackAndCatchFirstError(type, fn, void 0, value); + } finally { + isInsideEventHandler = previouslyInEventHandler; + } + } + function discreteUpdates(fn, a, b, c) { + var prevIsInsideEventHandler = isInsideEventHandler; + isInsideEventHandler = true; + try { + return discreteUpdatesImpl(fn, a, b, c); + } finally { + isInsideEventHandler = prevIsInsideEventHandler; + if (!isInsideEventHandler) { + finishEventHandler(); + } + } + } + var lastFlushedEventTimeStamp = 0; + function flushDiscreteUpdatesIfNeeded(timeStamp) { + if (!isInsideEventHandler && (!enableFlareAPI || timeStamp === 0 || lastFlushedEventTimeStamp !== timeStamp)) { + lastFlushedEventTimeStamp = timeStamp; + flushDiscreteUpdatesImpl(); + } + } + function setBatchingImplementation(_batchedUpdatesImpl, _discreteUpdatesImpl, _flushDiscreteUpdatesImpl, _batchedEventUpdatesImpl) { + batchedUpdatesImpl = _batchedUpdatesImpl; + discreteUpdatesImpl = _discreteUpdatesImpl; + flushDiscreteUpdatesImpl = _flushDiscreteUpdatesImpl; + batchedEventUpdatesImpl = _batchedEventUpdatesImpl; + } + var DiscreteEvent = 0; + var UserBlockingEvent = 1; + var ContinuousEvent = 2; + var UserBlockingPriority = Scheduler.unstable_UserBlockingPriority; + var runWithPriority = Scheduler.unstable_runWithPriority; + var listenToResponderEventTypesImpl; + function setListenToResponderEventTypes(_listenToResponderEventTypesImpl) { + listenToResponderEventTypesImpl = _listenToResponderEventTypesImpl; + } + var rootEventTypesToEventResponderInstances = /* @__PURE__ */ new Map(); + var DoNotPropagateToNextResponder = 0; + var PropagateToNextResponder = 1; + var currentTimeStamp = 0; + var currentInstance = null; + var currentDocument = null; + var currentPropagationBehavior = DoNotPropagateToNextResponder; + var eventResponderContext = { + dispatchEvent: function(eventValue, eventListener, eventPriority2) { + validateResponderContext(); + validateEventValue(eventValue); + switch (eventPriority2) { + case DiscreteEvent: { + flushDiscreteUpdatesIfNeeded(currentTimeStamp); + discreteUpdates(function() { + return executeUserEventHandler(eventListener, eventValue); + }); + break; + } + case UserBlockingEvent: { + runWithPriority(UserBlockingPriority, function() { + return executeUserEventHandler(eventListener, eventValue); + }); + break; + } + case ContinuousEvent: { + executeUserEventHandler(eventListener, eventValue); + break; + } + } + }, + isTargetWithinResponder: function(target) { + validateResponderContext(); + if (target != null) { + var fiber = getClosestInstanceFromNode(target); + var responderFiber = currentInstance.fiber; + while (fiber !== null) { + if (fiber === responderFiber || fiber.alternate === responderFiber) { + return true; + } + fiber = fiber.return; + } + } + return false; + }, + isTargetWithinResponderScope: function(target) { + validateResponderContext(); + var componentInstance = currentInstance; + var responder = componentInstance.responder; + if (target != null) { + var fiber = getClosestInstanceFromNode(target); + var responderFiber = currentInstance.fiber; + while (fiber !== null) { + if (fiber === responderFiber || fiber.alternate === responderFiber) { + return true; + } + if (doesFiberHaveResponder(fiber, responder)) { + return false; + } + fiber = fiber.return; + } + } + return false; + }, + isTargetWithinNode: function(childTarget, parentTarget) { + validateResponderContext(); + var childFiber = getClosestInstanceFromNode(childTarget); + var parentFiber = getClosestInstanceFromNode(parentTarget); + if (childFiber != null && parentFiber != null) { + var parentAlternateFiber = parentFiber.alternate; + var node = childFiber; + while (node !== null) { + if (node === parentFiber || node === parentAlternateFiber) { + return true; + } + node = node.return; + } + return false; + } + return parentTarget.contains(childTarget); + }, + addRootEventTypes: function(rootEventTypes) { + validateResponderContext(); + listenToResponderEventTypesImpl(rootEventTypes, currentDocument); + for (var i2 = 0; i2 < rootEventTypes.length; i2++) { + var rootEventType = rootEventTypes[i2]; + var eventResponderInstance = currentInstance; + registerRootEventType(rootEventType, eventResponderInstance); + } + }, + removeRootEventTypes: function(rootEventTypes) { + validateResponderContext(); + for (var i2 = 0; i2 < rootEventTypes.length; i2++) { + var rootEventType = rootEventTypes[i2]; + var rootEventResponders = rootEventTypesToEventResponderInstances.get(rootEventType); + var rootEventTypesSet = currentInstance.rootEventTypes; + if (rootEventTypesSet !== null) { + rootEventTypesSet.delete(rootEventType); + } + if (rootEventResponders !== void 0) { + rootEventResponders.delete(currentInstance); + } + } + }, + getActiveDocument, + objectAssign: _assign, + getTimeStamp: function() { + validateResponderContext(); + return currentTimeStamp; + }, + isTargetWithinHostComponent: function(target, elementType) { + validateResponderContext(); + var fiber = getClosestInstanceFromNode(target); + while (fiber !== null) { + if (fiber.tag === HostComponent && fiber.type === elementType) { + return true; + } + fiber = fiber.return; + } + return false; + }, + continuePropagation: function() { + currentPropagationBehavior = PropagateToNextResponder; + }, + enqueueStateRestore, + getResponderNode: function() { + validateResponderContext(); + var responderFiber = currentInstance.fiber; + if (responderFiber.tag === ScopeComponent) { + return null; + } + return responderFiber.stateNode; + } + }; + function validateEventValue(eventValue) { + if (typeof eventValue === "object" && eventValue !== null) { + var target = eventValue.target, type = eventValue.type, timeStamp = eventValue.timeStamp; + if (target == null || type == null || timeStamp == null) { + throw new Error('context.dispatchEvent: "target", "timeStamp", and "type" fields on event object are required.'); + } + var showWarning = function(name) { + { + warning$1(false, '%s is not available on event objects created from event responder modules (React Flare). Try wrapping in a conditional, i.e. `if (event.type !== "press") { event.%s }`', name, name); + } + }; + eventValue.isDefaultPrevented = function() { + { + showWarning("isDefaultPrevented()"); + } + }; + eventValue.isPropagationStopped = function() { + { + showWarning("isPropagationStopped()"); + } + }; + Object.defineProperty(eventValue, "nativeEvent", { + get: function() { + { + showWarning("nativeEvent"); + } + } + }); + } + } + function doesFiberHaveResponder(fiber, responder) { + var tag = fiber.tag; + if (tag === HostComponent || tag === ScopeComponent) { + var dependencies = fiber.dependencies; + if (dependencies !== null) { + var respondersMap = dependencies.responders; + if (respondersMap !== null && respondersMap.has(responder)) { + return true; + } + } + } + return false; + } + function getActiveDocument() { + return currentDocument; + } + function createDOMResponderEvent(topLevelType, nativeEvent, nativeEventTarget, passive, passiveSupported) { + var _ref = nativeEvent, buttons = _ref.buttons, pointerType = _ref.pointerType; + var eventPointerType = ""; + if (pointerType !== void 0) { + eventPointerType = pointerType; + } else if (nativeEvent.key !== void 0) { + eventPointerType = "keyboard"; + } else if (buttons !== void 0) { + eventPointerType = "mouse"; + } else if (nativeEvent.changedTouches !== void 0) { + eventPointerType = "touch"; + } + return { + nativeEvent, + passive, + passiveSupported, + pointerType: eventPointerType, + target: nativeEventTarget, + type: topLevelType + }; + } + function responderEventTypesContainType(eventTypes2, type) { + for (var i2 = 0, len = eventTypes2.length; i2 < len; i2++) { + if (eventTypes2[i2] === type) { + return true; + } + } + return false; + } + function validateResponderTargetEventTypes(eventType, responder) { + var targetEventTypes = responder.targetEventTypes; + if (targetEventTypes !== null) { + return responderEventTypesContainType(targetEventTypes, eventType); + } + return false; + } + function traverseAndHandleEventResponderInstances(topLevelType, targetFiber, nativeEvent, nativeEventTarget, eventSystemFlags) { + var isPassiveEvent = (eventSystemFlags & IS_PASSIVE) !== 0; + var isPassiveSupported = (eventSystemFlags & PASSIVE_NOT_SUPPORTED) === 0; + var isPassive = isPassiveEvent || !isPassiveSupported; + var eventType = isPassive ? topLevelType : topLevelType + "_active"; + var visitedResponders = /* @__PURE__ */ new Set(); + var responderEvent = createDOMResponderEvent(topLevelType, nativeEvent, nativeEventTarget, isPassiveEvent, isPassiveSupported); + var node = targetFiber; + var insidePortal = false; + while (node !== null) { + var _node = node, dependencies = _node.dependencies, tag = _node.tag; + if (tag === HostPortal) { + insidePortal = true; + } else if ((tag === HostComponent || tag === ScopeComponent) && dependencies !== null) { + var respondersMap = dependencies.responders; + if (respondersMap !== null) { + var responderInstances = Array.from(respondersMap.values()); + for (var i2 = 0, length = responderInstances.length; i2 < length; i2++) { + var responderInstance = responderInstances[i2]; + var props = responderInstance.props, responder = responderInstance.responder, state = responderInstance.state; + if (!visitedResponders.has(responder) && validateResponderTargetEventTypes(eventType, responder) && (!insidePortal || responder.targetPortalPropagation)) { + visitedResponders.add(responder); + var onEvent2 = responder.onEvent; + if (onEvent2 !== null) { + currentInstance = responderInstance; + onEvent2(responderEvent, eventResponderContext, props, state); + if (currentPropagationBehavior === PropagateToNextResponder) { + visitedResponders.delete(responder); + currentPropagationBehavior = DoNotPropagateToNextResponder; + } + } + } + } + } + } + node = node.return; + } + var rootEventResponderInstances = rootEventTypesToEventResponderInstances.get(eventType); + if (rootEventResponderInstances !== void 0) { + var _responderInstances = Array.from(rootEventResponderInstances); + for (var _i = 0; _i < _responderInstances.length; _i++) { + var _responderInstance = _responderInstances[_i]; + var props = _responderInstance.props, responder = _responderInstance.responder, state = _responderInstance.state; + var onRootEvent = responder.onRootEvent; + if (onRootEvent !== null) { + currentInstance = _responderInstance; + onRootEvent(responderEvent, eventResponderContext, props, state); + } + } + } + } + function mountEventResponder(responder, responderInstance, props, state) { + var onMount = responder.onMount; + if (onMount !== null) { + var previousInstance = currentInstance; + currentInstance = responderInstance; + try { + batchedEventUpdates(function() { + onMount(eventResponderContext, props, state); + }); + } finally { + currentInstance = previousInstance; + } + } + } + function unmountEventResponder(responderInstance) { + var responder = responderInstance.responder; + var onUnmount = responder.onUnmount; + if (onUnmount !== null) { + var props = responderInstance.props, state = responderInstance.state; + var previousInstance = currentInstance; + currentInstance = responderInstance; + try { + batchedEventUpdates(function() { + onUnmount(eventResponderContext, props, state); + }); + } finally { + currentInstance = previousInstance; + } + } + var rootEventTypesSet = responderInstance.rootEventTypes; + if (rootEventTypesSet !== null) { + var rootEventTypes = Array.from(rootEventTypesSet); + for (var i2 = 0; i2 < rootEventTypes.length; i2++) { + var topLevelEventType = rootEventTypes[i2]; + var rootEventResponderInstances = rootEventTypesToEventResponderInstances.get(topLevelEventType); + if (rootEventResponderInstances !== void 0) { + rootEventResponderInstances.delete(responderInstance); + } + } + } + } + function validateResponderContext() { + if (!(currentInstance !== null)) { + { + throw Error("An event responder context was used outside of an event cycle."); + } + } + } + function dispatchEventForResponderEventSystem(topLevelType, targetFiber, nativeEvent, nativeEventTarget, eventSystemFlags) { + if (enableFlareAPI) { + var previousInstance = currentInstance; + var previousTimeStamp = currentTimeStamp; + var previousDocument = currentDocument; + var previousPropagationBehavior = currentPropagationBehavior; + currentPropagationBehavior = DoNotPropagateToNextResponder; + currentDocument = nativeEventTarget.nodeType === 9 ? nativeEventTarget : nativeEventTarget.ownerDocument; + currentTimeStamp = nativeEvent.timeStamp; + try { + batchedEventUpdates(function() { + traverseAndHandleEventResponderInstances(topLevelType, targetFiber, nativeEvent, nativeEventTarget, eventSystemFlags); + }); + } finally { + currentInstance = previousInstance; + currentTimeStamp = previousTimeStamp; + currentDocument = previousDocument; + currentPropagationBehavior = previousPropagationBehavior; + } + } + } + function addRootEventTypesForResponderInstance(responderInstance, rootEventTypes) { + for (var i2 = 0; i2 < rootEventTypes.length; i2++) { + var rootEventType = rootEventTypes[i2]; + registerRootEventType(rootEventType, responderInstance); + } + } + function registerRootEventType(rootEventType, eventResponderInstance) { + var rootEventResponderInstances = rootEventTypesToEventResponderInstances.get(rootEventType); + if (rootEventResponderInstances === void 0) { + rootEventResponderInstances = /* @__PURE__ */ new Set(); + rootEventTypesToEventResponderInstances.set(rootEventType, rootEventResponderInstances); + } + var rootEventTypesSet = eventResponderInstance.rootEventTypes; + if (rootEventTypesSet === null) { + rootEventTypesSet = eventResponderInstance.rootEventTypes = /* @__PURE__ */ new Set(); + } + if (!!rootEventTypesSet.has(rootEventType)) { + { + throw Error('addRootEventTypes() found a duplicate root event type of "' + rootEventType + '". This might be because the event type exists in the event responder "rootEventTypes" array or because of a previous addRootEventTypes() using this root event type.'); + } + } + rootEventTypesSet.add(rootEventType); + rootEventResponderInstances.add(eventResponderInstance); + } + var RESERVED = 0; + var STRING = 1; + var BOOLEANISH_STRING = 2; + var BOOLEAN = 3; + var OVERLOADED_BOOLEAN = 4; + var NUMERIC = 5; + var POSITIVE_NUMERIC = 6; + var ATTRIBUTE_NAME_START_CHAR = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; + var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; + var ROOT_ATTRIBUTE_NAME = "data-reactroot"; + var VALID_ATTRIBUTE_NAME_REGEX = new RegExp("^[" + ATTRIBUTE_NAME_START_CHAR + "][" + ATTRIBUTE_NAME_CHAR + "]*$"); + var hasOwnProperty = Object.prototype.hasOwnProperty; + var illegalAttributeNameCache = {}; + var validatedAttributeNameCache = {}; + function isAttributeNameSafe(attributeName) { + if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) { + return true; + } + if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) { + return false; + } + if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { + validatedAttributeNameCache[attributeName] = true; + return true; + } + illegalAttributeNameCache[attributeName] = true; + { + warning$1(false, "Invalid attribute name: `%s`", attributeName); + } + return false; + } + function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) { + if (propertyInfo !== null) { + return propertyInfo.type === RESERVED; + } + if (isCustomComponentTag) { + return false; + } + if (name.length > 2 && (name[0] === "o" || name[0] === "O") && (name[1] === "n" || name[1] === "N")) { + return true; + } + return false; + } + function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) { + if (propertyInfo !== null && propertyInfo.type === RESERVED) { + return false; + } + switch (typeof value) { + case "function": + // $FlowIssue symbol is perfectly valid here + case "symbol": + return true; + case "boolean": { + if (isCustomComponentTag) { + return false; + } + if (propertyInfo !== null) { + return !propertyInfo.acceptsBooleans; + } else { + var prefix = name.toLowerCase().slice(0, 5); + return prefix !== "data-" && prefix !== "aria-"; + } + } + default: + return false; + } + } + function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) { + if (value === null || typeof value === "undefined") { + return true; + } + if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) { + return true; + } + if (isCustomComponentTag) { + return false; + } + if (propertyInfo !== null) { + switch (propertyInfo.type) { + case BOOLEAN: + return !value; + case OVERLOADED_BOOLEAN: + return value === false; + case NUMERIC: + return isNaN(value); + case POSITIVE_NUMERIC: + return isNaN(value) || value < 1; + } + } + return false; + } + function getPropertyInfo(name) { + return properties.hasOwnProperty(name) ? properties[name] : null; + } + function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace, sanitizeURL2) { + this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN; + this.attributeName = attributeName; + this.attributeNamespace = attributeNamespace; + this.mustUseProperty = mustUseProperty; + this.propertyName = name; + this.type = type; + this.sanitizeURL = sanitizeURL2; + } + var properties = {}; + [ + "children", + "dangerouslySetInnerHTML", + // TODO: This prevents the assignment of defaultValue to regular + // elements (not just inputs). Now that ReactDOMInput assigns to the + // defaultValue property -- do we need this? + "defaultValue", + "defaultChecked", + "innerHTML", + "suppressContentEditableWarning", + "suppressHydrationWarning", + "style" + ].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + RESERVED, + false, + // mustUseProperty + name, + // attributeName + null, + // attributeNamespace + false + ); + }); + [["acceptCharset", "accept-charset"], ["className", "class"], ["htmlFor", "for"], ["httpEquiv", "http-equiv"]].forEach(function(_ref) { + var name = _ref[0], attributeName = _ref[1]; + properties[name] = new PropertyInfoRecord( + name, + STRING, + false, + // mustUseProperty + attributeName, + // attributeName + null, + // attributeNamespace + false + ); + }); + ["contentEditable", "draggable", "spellCheck", "value"].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + BOOLEANISH_STRING, + false, + // mustUseProperty + name.toLowerCase(), + // attributeName + null, + // attributeNamespace + false + ); + }); + ["autoReverse", "externalResourcesRequired", "focusable", "preserveAlpha"].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + BOOLEANISH_STRING, + false, + // mustUseProperty + name, + // attributeName + null, + // attributeNamespace + false + ); + }); + [ + "allowFullScreen", + "async", + // Note: there is a special case that prevents it from being written to the DOM + // on the client side because the browsers are inconsistent. Instead we call focus(). + "autoFocus", + "autoPlay", + "controls", + "default", + "defer", + "disabled", + "disablePictureInPicture", + "formNoValidate", + "hidden", + "loop", + "noModule", + "noValidate", + "open", + "playsInline", + "readOnly", + "required", + "reversed", + "scoped", + "seamless", + // Microdata + "itemScope" + ].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + BOOLEAN, + false, + // mustUseProperty + name.toLowerCase(), + // attributeName + null, + // attributeNamespace + false + ); + }); + [ + "checked", + // Note: `option.selected` is not updated if `select.multiple` is + // disabled with `removeAttribute`. We have special logic for handling this. + "multiple", + "muted", + "selected" + ].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + BOOLEAN, + true, + // mustUseProperty + name, + // attributeName + null, + // attributeNamespace + false + ); + }); + ["capture", "download"].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + OVERLOADED_BOOLEAN, + false, + // mustUseProperty + name, + // attributeName + null, + // attributeNamespace + false + ); + }); + ["cols", "rows", "size", "span"].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + POSITIVE_NUMERIC, + false, + // mustUseProperty + name, + // attributeName + null, + // attributeNamespace + false + ); + }); + ["rowSpan", "start"].forEach(function(name) { + properties[name] = new PropertyInfoRecord( + name, + NUMERIC, + false, + // mustUseProperty + name.toLowerCase(), + // attributeName + null, + // attributeNamespace + false + ); + }); + var CAMELIZE = /[\-\:]([a-z])/g; + var capitalize = function(token) { + return token[1].toUpperCase(); + }; + ["accent-height", "alignment-baseline", "arabic-form", "baseline-shift", "cap-height", "clip-path", "clip-rule", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "dominant-baseline", "enable-background", "fill-opacity", "fill-rule", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "glyph-name", "glyph-orientation-horizontal", "glyph-orientation-vertical", "horiz-adv-x", "horiz-origin-x", "image-rendering", "letter-spacing", "lighting-color", "marker-end", "marker-mid", "marker-start", "overline-position", "overline-thickness", "paint-order", "panose-1", "pointer-events", "rendering-intent", "shape-rendering", "stop-color", "stop-opacity", "strikethrough-position", "strikethrough-thickness", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-anchor", "text-decoration", "text-rendering", "underline-position", "underline-thickness", "unicode-bidi", "unicode-range", "units-per-em", "v-alphabetic", "v-hanging", "v-ideographic", "v-mathematical", "vector-effect", "vert-adv-y", "vert-origin-x", "vert-origin-y", "word-spacing", "writing-mode", "xmlns:xlink", "x-height"].forEach(function(attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord( + name, + STRING, + false, + // mustUseProperty + attributeName, + null, + // attributeNamespace + false + ); + }); + ["xlink:actuate", "xlink:arcrole", "xlink:role", "xlink:show", "xlink:title", "xlink:type"].forEach(function(attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord( + name, + STRING, + false, + // mustUseProperty + attributeName, + "http://www.w3.org/1999/xlink", + false + ); + }); + ["xml:base", "xml:lang", "xml:space"].forEach(function(attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord( + name, + STRING, + false, + // mustUseProperty + attributeName, + "http://www.w3.org/XML/1998/namespace", + false + ); + }); + ["tabIndex", "crossOrigin"].forEach(function(attributeName) { + properties[attributeName] = new PropertyInfoRecord( + attributeName, + STRING, + false, + // mustUseProperty + attributeName.toLowerCase(), + // attributeName + null, + // attributeNamespace + false + ); + }); + var xlinkHref = "xlinkHref"; + properties[xlinkHref] = new PropertyInfoRecord( + "xlinkHref", + STRING, + false, + // mustUseProperty + "xlink:href", + "http://www.w3.org/1999/xlink", + true + ); + ["src", "href", "action", "formAction"].forEach(function(attributeName) { + properties[attributeName] = new PropertyInfoRecord( + attributeName, + STRING, + false, + // mustUseProperty + attributeName.toLowerCase(), + // attributeName + null, + // attributeNamespace + true + ); + }); + var ReactDebugCurrentFrame$1 = null; + { + ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; + } + var isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i; + var didWarn = false; + function sanitizeURL(url) { + if (disableJavaScriptURLs) { + if (!!isJavaScriptProtocol.test(url)) { + { + throw Error("React has blocked a javascript: URL as a security precaution." + ReactDebugCurrentFrame$1.getStackAddendum()); + } + } + } else if (!didWarn && isJavaScriptProtocol.test(url)) { + didWarn = true; + warning$1(false, "A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed %s.", JSON.stringify(url)); + } + } + function toString(value) { + return "" + value; + } + function getToStringValue(value) { + switch (typeof value) { + case "boolean": + case "number": + case "object": + case "string": + case "undefined": + return value; + default: + return ""; + } + } + var toStringOrTrustedType = toString; + if (enableTrustedTypesIntegration && typeof trustedTypes !== "undefined") { + toStringOrTrustedType = function(value) { + if (typeof value === "object" && (trustedTypes.isHTML(value) || trustedTypes.isScript(value) || trustedTypes.isScriptURL(value) || /* TrustedURLs are deprecated and will be removed soon: https://github.com/WICG/trusted-types/pull/204 */ + trustedTypes.isURL && trustedTypes.isURL(value))) { + return value; + } + return toString(value); + }; + } + function setAttribute(node, attributeName, attributeValue) { + node.setAttribute(attributeName, attributeValue); + } + function setAttributeNS(node, attributeNamespace, attributeName, attributeValue) { + node.setAttributeNS(attributeNamespace, attributeName, attributeValue); + } + function getValueForProperty(node, name, expected, propertyInfo) { + { + if (propertyInfo.mustUseProperty) { + var propertyName = propertyInfo.propertyName; + return node[propertyName]; + } else { + if (!disableJavaScriptURLs && propertyInfo.sanitizeURL) { + sanitizeURL("" + expected); + } + var attributeName = propertyInfo.attributeName; + var stringValue = null; + if (propertyInfo.type === OVERLOADED_BOOLEAN) { + if (node.hasAttribute(attributeName)) { + var value = node.getAttribute(attributeName); + if (value === "") { + return true; + } + if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { + return value; + } + if (value === "" + expected) { + return expected; + } + return value; + } + } else if (node.hasAttribute(attributeName)) { + if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { + return node.getAttribute(attributeName); + } + if (propertyInfo.type === BOOLEAN) { + return expected; + } + stringValue = node.getAttribute(attributeName); + } + if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { + return stringValue === null ? expected : stringValue; + } else if (stringValue === "" + expected) { + return expected; + } else { + return stringValue; + } + } + } + } + function getValueForAttribute(node, name, expected) { + { + if (!isAttributeNameSafe(name)) { + return; + } + if (!node.hasAttribute(name)) { + return expected === void 0 ? void 0 : null; + } + var value = node.getAttribute(name); + if (value === "" + expected) { + return expected; + } + return value; + } + } + function setValueForProperty(node, name, value, isCustomComponentTag) { + var propertyInfo = getPropertyInfo(name); + if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) { + return; + } + if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) { + value = null; + } + if (isCustomComponentTag || propertyInfo === null) { + if (isAttributeNameSafe(name)) { + var _attributeName = name; + if (value === null) { + node.removeAttribute(_attributeName); + } else { + setAttribute(node, _attributeName, toStringOrTrustedType(value)); + } + } + return; + } + var mustUseProperty = propertyInfo.mustUseProperty; + if (mustUseProperty) { + var propertyName = propertyInfo.propertyName; + if (value === null) { + var type = propertyInfo.type; + node[propertyName] = type === BOOLEAN ? false : ""; + } else { + node[propertyName] = value; + } + return; + } + var attributeName = propertyInfo.attributeName, attributeNamespace = propertyInfo.attributeNamespace; + if (value === null) { + node.removeAttribute(attributeName); + } else { + var _type = propertyInfo.type; + var attributeValue; + if (_type === BOOLEAN || _type === OVERLOADED_BOOLEAN && value === true) { + attributeValue = ""; + } else { + attributeValue = toStringOrTrustedType(value); + if (propertyInfo.sanitizeURL) { + sanitizeURL(attributeValue.toString()); + } + } + if (attributeNamespace) { + setAttributeNS(node, attributeNamespace, attributeName, attributeValue); + } else { + setAttribute(node, attributeName, attributeValue); + } + } + } + var ReactDebugCurrentFrame$2 = null; + var ReactControlledValuePropTypes = { + checkPropTypes: null + }; + { + ReactDebugCurrentFrame$2 = ReactSharedInternals.ReactDebugCurrentFrame; + var hasReadOnlyValue = { + button: true, + checkbox: true, + image: true, + hidden: true, + radio: true, + reset: true, + submit: true + }; + var propTypes = { + value: function(props, propName, componentName) { + if (hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled || props[propName] == null || enableFlareAPI && props.listeners) { + return null; + } + return new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`."); + }, + checked: function(props, propName, componentName) { + if (props.onChange || props.readOnly || props.disabled || props[propName] == null || enableFlareAPI && props.listeners) { + return null; + } + return new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`."); + } + }; + ReactControlledValuePropTypes.checkPropTypes = function(tagName, props) { + checkPropTypes(propTypes, props, "prop", tagName, ReactDebugCurrentFrame$2.getStackAddendum); + }; + } + function isCheckable(elem) { + var type = elem.type; + var nodeName = elem.nodeName; + return nodeName && nodeName.toLowerCase() === "input" && (type === "checkbox" || type === "radio"); + } + function getTracker(node) { + return node._valueTracker; + } + function detachTracker(node) { + node._valueTracker = null; + } + function getValueFromNode(node) { + var value = ""; + if (!node) { + return value; + } + if (isCheckable(node)) { + value = node.checked ? "true" : "false"; + } else { + value = node.value; + } + return value; + } + function trackValueOnNode(node) { + var valueField = isCheckable(node) ? "checked" : "value"; + var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField); + var currentValue = "" + node[valueField]; + if (node.hasOwnProperty(valueField) || typeof descriptor === "undefined" || typeof descriptor.get !== "function" || typeof descriptor.set !== "function") { + return; + } + var get2 = descriptor.get, set2 = descriptor.set; + Object.defineProperty(node, valueField, { + configurable: true, + get: function() { + return get2.call(this); + }, + set: function(value) { + currentValue = "" + value; + set2.call(this, value); + } + }); + Object.defineProperty(node, valueField, { + enumerable: descriptor.enumerable + }); + var tracker = { + getValue: function() { + return currentValue; + }, + setValue: function(value) { + currentValue = "" + value; + }, + stopTracking: function() { + detachTracker(node); + delete node[valueField]; + } + }; + return tracker; + } + function track(node) { + if (getTracker(node)) { + return; + } + node._valueTracker = trackValueOnNode(node); + } + function updateValueIfChanged(node) { + if (!node) { + return false; + } + var tracker = getTracker(node); + if (!tracker) { + return true; + } + var lastValue = tracker.getValue(); + var nextValue = getValueFromNode(node); + if (nextValue !== lastValue) { + tracker.setValue(nextValue); + return true; + } + return false; + } + var didWarnValueDefaultValue = false; + var didWarnCheckedDefaultChecked = false; + var didWarnControlledToUncontrolled = false; + var didWarnUncontrolledToControlled = false; + function isControlled(props) { + var usesChecked = props.type === "checkbox" || props.type === "radio"; + return usesChecked ? props.checked != null : props.value != null; + } + function getHostProps(element, props) { + var node = element; + var checked = props.checked; + var hostProps = _assign({}, props, { + defaultChecked: void 0, + defaultValue: void 0, + value: void 0, + checked: checked != null ? checked : node._wrapperState.initialChecked + }); + return hostProps; + } + function initWrapperState(element, props) { + { + ReactControlledValuePropTypes.checkPropTypes("input", props); + if (props.checked !== void 0 && props.defaultChecked !== void 0 && !didWarnCheckedDefaultChecked) { + warning$1(false, "%s contains an input of type %s with both checked and defaultChecked props. Input elements must be either controlled or uncontrolled (specify either the checked prop, or the defaultChecked prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://fb.me/react-controlled-components", getCurrentFiberOwnerNameInDevOrNull() || "A component", props.type); + didWarnCheckedDefaultChecked = true; + } + if (props.value !== void 0 && props.defaultValue !== void 0 && !didWarnValueDefaultValue) { + warning$1(false, "%s contains an input of type %s with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://fb.me/react-controlled-components", getCurrentFiberOwnerNameInDevOrNull() || "A component", props.type); + didWarnValueDefaultValue = true; + } + } + var node = element; + var defaultValue = props.defaultValue == null ? "" : props.defaultValue; + node._wrapperState = { + initialChecked: props.checked != null ? props.checked : props.defaultChecked, + initialValue: getToStringValue(props.value != null ? props.value : defaultValue), + controlled: isControlled(props) + }; + } + function updateChecked(element, props) { + var node = element; + var checked = props.checked; + if (checked != null) { + setValueForProperty(node, "checked", checked, false); + } + } + function updateWrapper(element, props) { + var node = element; + { + var controlled = isControlled(props); + if (!node._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) { + warning$1(false, "A component is changing an uncontrolled input of type %s to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components", props.type); + didWarnUncontrolledToControlled = true; + } + if (node._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) { + warning$1(false, "A component is changing a controlled input of type %s to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components", props.type); + didWarnControlledToUncontrolled = true; + } + } + updateChecked(element, props); + var value = getToStringValue(props.value); + var type = props.type; + if (value != null) { + if (type === "number") { + if (value === 0 && node.value === "" || // We explicitly want to coerce to number here if possible. + // eslint-disable-next-line + node.value != value) { + node.value = toString(value); + } + } else if (node.value !== toString(value)) { + node.value = toString(value); + } + } else if (type === "submit" || type === "reset") { + node.removeAttribute("value"); + return; + } + if (disableInputAttributeSyncing) { + if (props.hasOwnProperty("defaultValue")) { + setDefaultValue(node, props.type, getToStringValue(props.defaultValue)); + } + } else { + if (props.hasOwnProperty("value")) { + setDefaultValue(node, props.type, value); + } else if (props.hasOwnProperty("defaultValue")) { + setDefaultValue(node, props.type, getToStringValue(props.defaultValue)); + } + } + if (disableInputAttributeSyncing) { + if (props.defaultChecked == null) { + node.removeAttribute("checked"); + } else { + node.defaultChecked = !!props.defaultChecked; + } + } else { + if (props.checked == null && props.defaultChecked != null) { + node.defaultChecked = !!props.defaultChecked; + } + } + } + function postMountWrapper(element, props, isHydrating2) { + var node = element; + if (props.hasOwnProperty("value") || props.hasOwnProperty("defaultValue")) { + var type = props.type; + var isButton = type === "submit" || type === "reset"; + if (isButton && (props.value === void 0 || props.value === null)) { + return; + } + var initialValue = toString(node._wrapperState.initialValue); + if (!isHydrating2) { + if (disableInputAttributeSyncing) { + var value = getToStringValue(props.value); + if (value != null) { + if (isButton || value !== node.value) { + node.value = toString(value); + } + } + } else { + if (initialValue !== node.value) { + node.value = initialValue; + } + } + } + if (disableInputAttributeSyncing) { + var defaultValue = getToStringValue(props.defaultValue); + if (defaultValue != null) { + node.defaultValue = toString(defaultValue); + } + } else { + node.defaultValue = initialValue; + } + } + var name = node.name; + if (name !== "") { + node.name = ""; + } + if (disableInputAttributeSyncing) { + if (!isHydrating2) { + updateChecked(element, props); + } + if (props.hasOwnProperty("defaultChecked")) { + node.defaultChecked = !node.defaultChecked; + node.defaultChecked = !!props.defaultChecked; + } + } else { + node.defaultChecked = !node.defaultChecked; + node.defaultChecked = !!node._wrapperState.initialChecked; + } + if (name !== "") { + node.name = name; + } + } + function restoreControlledState$1(element, props) { + var node = element; + updateWrapper(node, props); + updateNamedCousins(node, props); + } + function updateNamedCousins(rootNode, props) { + var name = props.name; + if (props.type === "radio" && name != null) { + var queryRoot = rootNode; + while (queryRoot.parentNode) { + queryRoot = queryRoot.parentNode; + } + var group = queryRoot.querySelectorAll("input[name=" + JSON.stringify("" + name) + '][type="radio"]'); + for (var i2 = 0; i2 < group.length; i2++) { + var otherNode = group[i2]; + if (otherNode === rootNode || otherNode.form !== rootNode.form) { + continue; + } + var otherProps = getFiberCurrentPropsFromNode$1(otherNode); + if (!otherProps) { + { + throw Error("ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported."); + } + } + updateValueIfChanged(otherNode); + updateWrapper(otherNode, otherProps); + } + } + } + function setDefaultValue(node, type, value) { + if ( + // Focused number inputs synchronize on blur. See ChangeEventPlugin.js + type !== "number" || node.ownerDocument.activeElement !== node + ) { + if (value == null) { + node.defaultValue = toString(node._wrapperState.initialValue); + } else if (node.defaultValue !== toString(value)) { + node.defaultValue = toString(value); + } + } + } + var didWarnSelectedSetOnOption = false; + var didWarnInvalidChild = false; + function flattenChildren(children) { + var content = ""; + React7.Children.forEach(children, function(child) { + if (child == null) { + return; + } + content += child; + }); + return content; + } + function validateProps(element, props) { + { + if (typeof props.children === "object" && props.children !== null) { + React7.Children.forEach(props.children, function(child) { + if (child == null) { + return; + } + if (typeof child === "string" || typeof child === "number") { + return; + } + if (typeof child.type !== "string") { + return; + } + if (!didWarnInvalidChild) { + didWarnInvalidChild = true; + warning$1(false, "Only strings and numbers are supported as