266 lines
8.4 KiB
TypeScript
266 lines
8.4 KiB
TypeScript
import { BrowserApi } from "../browser/browser-api";
|
|
|
|
import { ScrollOptions } from "./abstractions/browser-popup-utils.abstractions";
|
|
|
|
class BrowserPopupUtils {
|
|
/**
|
|
* Identifies if the popup is within the sidebar.
|
|
*
|
|
* @param win - The passed window object.
|
|
*/
|
|
static inSidebar(win: Window): boolean {
|
|
return BrowserPopupUtils.urlContainsSearchParams(win, "uilocation", "sidebar");
|
|
}
|
|
|
|
/**
|
|
* Identifies if the popup is within the popout.
|
|
*
|
|
* @param win - The passed window object.
|
|
*/
|
|
static inPopout(win: Window): boolean {
|
|
return BrowserPopupUtils.urlContainsSearchParams(win, "uilocation", "popout");
|
|
}
|
|
|
|
/**
|
|
* Identifies if the popup is within the single action popout.
|
|
*
|
|
* @param win - The passed window object.
|
|
* @param popoutKey - The single action popout key used to identify the popout.
|
|
*/
|
|
static inSingleActionPopout(win: Window, popoutKey: string): boolean {
|
|
return BrowserPopupUtils.urlContainsSearchParams(win, "singleActionPopout", popoutKey);
|
|
}
|
|
|
|
/**
|
|
* Identifies if the popup is within the popup.
|
|
*
|
|
* @param win - The passed window object.
|
|
*/
|
|
static inPopup(win: Window): boolean {
|
|
return (
|
|
win.location.href.indexOf("uilocation=") === -1 ||
|
|
win.location.href.indexOf("uilocation=popup") > -1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Gets the scroll position of the popup.
|
|
*
|
|
* @param win - The passed window object.
|
|
* @param scrollingContainer - Element tag name of the scrolling container.
|
|
*/
|
|
static getContentScrollY(win: Window, scrollingContainer = "main"): number {
|
|
const content = win.document.getElementsByTagName(scrollingContainer)[0];
|
|
return content.scrollTop;
|
|
}
|
|
|
|
/**
|
|
* Sets the scroll position of the popup.
|
|
*
|
|
* @param win - The passed window object.
|
|
* @param scrollYAmount - The amount to scroll the popup.
|
|
* @param options - Allows for setting the delay in ms to wait before scrolling the popup and the scrolling container tag name.
|
|
*/
|
|
static async setContentScrollY(
|
|
win: Window,
|
|
scrollYAmount: number | undefined,
|
|
options: ScrollOptions = {
|
|
delay: 0,
|
|
containerSelector: "main",
|
|
},
|
|
) {
|
|
const { delay, containerSelector } = options;
|
|
return new Promise<void>((resolve) =>
|
|
win.setTimeout(() => {
|
|
const container = win.document.querySelector(containerSelector);
|
|
if (!isNaN(scrollYAmount) && container) {
|
|
container.scrollTop = scrollYAmount;
|
|
}
|
|
|
|
resolve();
|
|
}, delay),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Identifies if the background page needs to be initialized.
|
|
*/
|
|
static backgroundInitializationRequired() {
|
|
return !BrowserApi.getBackgroundPage();
|
|
}
|
|
|
|
/**
|
|
* Identifies if the popup is loading in private mode.
|
|
*/
|
|
static inPrivateMode() {
|
|
return BrowserPopupUtils.backgroundInitializationRequired() && !BrowserApi.isManifestVersion(3);
|
|
}
|
|
|
|
/**
|
|
* Opens a popout window of any extension page. If the popout window is already open, it will be focused.
|
|
*
|
|
* @param extensionUrlPath - A relative path to the extension page. Example: "popup/index.html#/tabs/vault"
|
|
* @param options - Options for the popout window that overrides the default options.
|
|
*/
|
|
static async openPopout(
|
|
extensionUrlPath: string,
|
|
options: {
|
|
senderWindowId?: number;
|
|
singleActionKey?: string;
|
|
forceCloseExistingWindows?: boolean;
|
|
windowOptions?: Partial<chrome.windows.CreateData>;
|
|
} = {},
|
|
) {
|
|
const { senderWindowId, singleActionKey, forceCloseExistingWindows, windowOptions } = options;
|
|
const defaultPopoutWindowOptions: chrome.windows.CreateData = {
|
|
type: "popup",
|
|
focused: true,
|
|
width: 380,
|
|
height: 630,
|
|
};
|
|
const offsetRight = 15;
|
|
const offsetTop = 90;
|
|
const popupWidth = defaultPopoutWindowOptions.width;
|
|
const senderWindow = await BrowserApi.getWindow(senderWindowId);
|
|
const popoutWindowOptions = {
|
|
left: senderWindow.left + senderWindow.width - popupWidth - offsetRight,
|
|
top: senderWindow.top + offsetTop,
|
|
...defaultPopoutWindowOptions,
|
|
...windowOptions,
|
|
url: BrowserPopupUtils.buildPopoutUrl(extensionUrlPath, singleActionKey),
|
|
};
|
|
|
|
if (
|
|
(await BrowserPopupUtils.isSingleActionPopoutOpen(
|
|
singleActionKey,
|
|
popoutWindowOptions,
|
|
forceCloseExistingWindows,
|
|
)) &&
|
|
!forceCloseExistingWindows
|
|
) {
|
|
return;
|
|
}
|
|
|
|
return await BrowserApi.createWindow(popoutWindowOptions);
|
|
}
|
|
|
|
/**
|
|
* Closes the single action popout window.
|
|
*
|
|
* @param popoutKey - The single action popout key used to identify the popout.
|
|
* @param delayClose - The amount of time to wait before closing the popout. Defaults to 0.
|
|
*/
|
|
static async closeSingleActionPopout(popoutKey: string, delayClose = 0): Promise<void> {
|
|
const extensionUrl = chrome.runtime.getURL("popup/index.html");
|
|
const tabs = await BrowserApi.tabsQuery({ url: `${extensionUrl}*` });
|
|
for (const tab of tabs) {
|
|
if (!tab.url.includes(`singleActionPopout=${popoutKey}`)) {
|
|
continue;
|
|
}
|
|
|
|
setTimeout(() => BrowserApi.removeWindow(tab.windowId), delayClose);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens a popout window for the current page.
|
|
* If the current page is set for the current tab, then the
|
|
* popout window will be set for the vault items listing tab.
|
|
*
|
|
* @param win - The passed window object.
|
|
* @param href - The href to open in the popout window.
|
|
*/
|
|
static async openCurrentPagePopout(win: Window, href: string = null) {
|
|
const popoutUrl = href || win.location.href;
|
|
const parsedUrl = new URL(popoutUrl);
|
|
let hashRoute = parsedUrl.hash;
|
|
if (hashRoute.startsWith("#/tabs/current")) {
|
|
hashRoute = "#/tabs/vault";
|
|
}
|
|
|
|
await BrowserPopupUtils.openPopout(`${parsedUrl.pathname}${hashRoute}`);
|
|
|
|
if (BrowserPopupUtils.inPopup(win)) {
|
|
BrowserApi.closePopup(win);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Identifies if a single action window is open based on the passed popoutKey.
|
|
* Will focus the existing window, and close any other windows that might exist
|
|
* with the same popout key.
|
|
*
|
|
* @param popoutKey - The single action popout key used to identify the popout.
|
|
* @param windowInfo - The window info to use to update the existing window.
|
|
* @param forceCloseExistingWindows - Identifies if the existing windows should be closed.
|
|
*/
|
|
private static async isSingleActionPopoutOpen(
|
|
popoutKey: string | undefined,
|
|
windowInfo: chrome.windows.CreateData,
|
|
forceCloseExistingWindows = false,
|
|
) {
|
|
if (!popoutKey) {
|
|
return false;
|
|
}
|
|
|
|
const extensionUrl = chrome.runtime.getURL("popup/index.html");
|
|
const popoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter((tab) =>
|
|
tab.url.includes(`singleActionPopout=${popoutKey}`),
|
|
);
|
|
if (popoutTabs.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!forceCloseExistingWindows) {
|
|
// Update first, remove it from list
|
|
const tab = popoutTabs.shift();
|
|
await BrowserApi.updateWindowProperties(tab.windowId, {
|
|
focused: true,
|
|
width: windowInfo.width,
|
|
height: windowInfo.height,
|
|
top: windowInfo.top,
|
|
left: windowInfo.left,
|
|
});
|
|
}
|
|
|
|
popoutTabs.forEach((tab) => BrowserApi.removeWindow(tab.windowId));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Identifies if the url contains the specified search param and value.
|
|
*
|
|
* @param win - The passed window object.
|
|
* @param searchParam - The search param to identify.
|
|
* @param searchValue - The search value to identify.
|
|
*/
|
|
private static urlContainsSearchParams(
|
|
win: Window,
|
|
searchParam: string,
|
|
searchValue: string,
|
|
): boolean {
|
|
return win.location.href.indexOf(`${searchParam}=${searchValue}`) > -1;
|
|
}
|
|
|
|
/**
|
|
* Builds the popout url path. Ensures that the uilocation param is set to
|
|
* `popout` and that the singleActionPopout param is set to the passed singleActionKey.
|
|
*
|
|
* @param extensionUrlPath - A relative path to the extension page. Example: "popup/index.html#/tabs/vault"
|
|
* @param singleActionKey - The single action popout key used to identify the popout.
|
|
*/
|
|
private static buildPopoutUrl(extensionUrlPath: string, singleActionKey: string) {
|
|
const parsedUrl = new URL(chrome.runtime.getURL(extensionUrlPath));
|
|
parsedUrl.searchParams.set("uilocation", "popout");
|
|
|
|
if (singleActionKey) {
|
|
parsedUrl.searchParams.set("singleActionPopout", singleActionKey);
|
|
}
|
|
|
|
return parsedUrl.toString();
|
|
}
|
|
}
|
|
|
|
export default BrowserPopupUtils;
|