1
0
mirror of https://github.com/XFox111/PasswordGeneratorExtension.git synced 2026-04-22 08:08:01 +03:00

Patch 2.0.2 (#53)

* Fix for issue #51 - Extension is not working on firefox (#52)

* Fix for issue #51 - Extension is not working on firefox

* Fix for issue #51 - Replaced @types/chrome with webextension-polyfill

* re-Added @types/chrome to allow development tests

* Moved local import after module imports

* Bump @types/node from 18.7.18 to 18.11.0 (#48)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.7.18 to 18.11.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @fluentui/react-icons from 2.0.183 to 2.0.185 (#50)

Bumps [@fluentui/react-icons](https://github.com/microsoft/fluentui-system-icons) from 2.0.183 to 2.0.185.
- [Release notes](https://github.com/microsoft/fluentui-system-icons/releases)
- [Commits](https://github.com/microsoft/fluentui-system-icons/commits)

---
updated-dependencies:
- dependency-name: "@fluentui/react-icons"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>

* Bump typescript from 4.8.3 to 4.8.4 (#47)

Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.8.3 to 4.8.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.8.3...v4.8.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>

* Bump @types/jest from 29.1.1 to 29.1.2 (#44)

Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.1.1 to 29.1.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>

* Bump @fluentui/react-components from 9.3.2 to 9.5.1 (#49)

Bumps [@fluentui/react-components](https://github.com/microsoft/fluentui) from 9.3.2 to 9.5.1.
- [Release notes](https://github.com/microsoft/fluentui/releases)
- [Changelog](https://github.com/microsoft/fluentui/blob/master/azure-pipelines.release-fluentui.yml)
- [Commits](https://github.com/microsoft/fluentui/compare/@fluentui/react-components_v9.3.2...@fluentui/react-components_v9.5.1)

---
updated-dependencies:
- dependency-name: "@fluentui/react-components"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugene Fox <eugene.xfox@outlook.com>

* - Updated craco.config.ts to improve extension's stability
- Renamed manifest.firefox.json to manifest.v2.json
- Added fallbacks for invalid password length
- Migrated FluentUI Dialog to stable release
- Updated WebExt compat API

* Updated project formatting

* Feedback button now navigates to webstore page

* Update code style guidelines

* Updated extension version to 2.0.2

* Fixed pipelines

* Fixed AboutSection.tsx

* Fixed CodeQL issues

* Fixed feedback link not working

* Fixed feedback button again

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Andry Herizo RAJOELISON <finalherizo@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Eugene Fox
2022-10-22 15:55:44 +03:00
committed by GitHub
parent 4f578da227
commit 68aace475e
27 changed files with 1221 additions and 1105 deletions
+6
View File
@@ -49,6 +49,7 @@ main
animation-timing-function: var(--curveEasyEaseMax);
animation-duration: .5s;
}
@keyframes scaleUpInAnim
{
from
@@ -56,6 +57,7 @@ main
transform: scale(.5);
opacity: 0;
}
to
{
transform: scale(1);
@@ -69,12 +71,14 @@ main
animation-timing-function: var(--curveEasyEaseMax);
animation-duration: .5s;
}
@keyframes spinAnim
{
from
{
transform: rotate(0deg);
}
to
{
transform: rotate(360deg);
@@ -87,12 +91,14 @@ main
animation-timing-function: var(--curveDecelerateMin);
animation-duration: .5s;
}
@keyframes fadeInAnim
{
from
{
opacity: 0;
}
to
{
opacity: 1;
-1
View File
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg SYSTEM "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-flat-20110816.dtd">
<svg id="BuyMeACoffee" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 884 1279" width="16" height="16">
<defs>
<style type="text/css">

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

+26 -1
View File
@@ -3,6 +3,7 @@ import { InfoRegular, PersonFeedbackRegular } from "@fluentui/react-icons";
import { ReactComponent as BuyMeACoffee } from "../Assets/BuyMeACoffee.svg";
import React from "react";
import { loc } from "../Utils/Localization";
import browser from "../Utils/Browser";
export default class AboutSection extends React.Component
{
@@ -32,7 +33,7 @@ export default class AboutSection extends React.Component
<div className="stack horizontal gap">
<Button
as="a" target="_blank"
href="mailto:feedback@xfox111.net"
href={ this.GetFeedbackLink() }
appearance="primary" icon={ <PersonFeedbackRegular /> }>
{ loc("Leave feedback") }
@@ -51,4 +52,28 @@ export default class AboutSection extends React.Component
</AccordionItem>
);
}
private GetFeedbackLink(): string
{
let manifest: { [key: string]: string | any } = browser?.runtime?.getManifest();
if (!manifest)
return "mailto:feedback@xfox111.net";
else
{
if (manifest.update_url)
{
let host: string = new URL(manifest.update_url).host;
if (host.endsWith("edge.microsoft.com"))
return "https://microsoftedge.microsoft.com/addons/detail/password-generator/manimdhobjbkfpeeehlhhneookiokpbj";
else if (host.endsWith("google.com"))
return "https://chrome.google.com/webstore/detail/password-generator/jnjobgjobffgmgfnkpkjfjkkfhfikmfl";
else
return "mailto:feedback@xfox111.net";
}
else
return "mailto:feedback@xfox111.net";
}
}
}
+56 -54
View File
@@ -1,5 +1,5 @@
import { Button, Text } from "@fluentui/react-components";
import { Dialog, DialogTrigger, DialogSurface, DialogTitle, DialogBody, Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell, DialogActions } from "@fluentui/react-components/unstable";
import { Button, Text, Dialog, DialogTrigger, DialogSurface, DialogTitle, DialogBody, DialogActions, DialogContent } from "@fluentui/react-components";
import { Table, TableHeader, TableRow, TableHeaderCell, TableBody, TableCell } from "@fluentui/react-components/unstable";
import { QuestionCircleRegular } from "@fluentui/react-icons";
import React from "react";
import Generator from "../Utils/Generator";
@@ -15,60 +15,62 @@ export default class CharacterHelpDialog extends React.Component
<Button appearance="subtle" style={ { marginLeft: 5 } } icon={ <QuestionCircleRegular /> } />
</DialogTrigger>
<DialogSurface aria-label="label">
<DialogTitle>{ loc("Character options") }</DialogTitle>
<DialogBody>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>{ loc("Set_name") }</TableHeaderCell>
<TableHeaderCell>{ loc("Characters") }</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>{ loc("Lowercase") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Lowercase.substring(0, 10) }{ loc(", etc.") }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Uppercase") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Uppercase.substring(0, 10) }{ loc(", etc.") }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Numeric") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Numeric }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Special symbols") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.SpecialCharacters }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Ambiguous") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.AmbiguousCharacters }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Similar") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.SimilarCharacters }</Text>
</TableCell>
</TableRow>
</TableBody>
</Table>
<DialogTitle>{ loc("Character options") }</DialogTitle>
<DialogContent>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>{ loc("Set_name") }</TableHeaderCell>
<TableHeaderCell>{ loc("Characters") }</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>{ loc("Lowercase") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Lowercase.substring(0, 10) }{ loc(", etc.") }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Uppercase") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Uppercase.substring(0, 10) }{ loc(", etc.") }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Numeric") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.Numeric }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Special symbols") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.SpecialCharacters }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Ambiguous") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.AmbiguousCharacters }</Text>
</TableCell>
</TableRow>
<TableRow>
<TableCell>{ loc("Similar") }</TableCell>
<TableCell>
<Text font="monospace">{ Generator.SimilarCharacters }</Text>
</TableCell>
</TableRow>
</TableBody>
</Table>
</DialogContent>
<DialogActions>
<DialogTrigger>
<Button appearance="secondary">{ loc("OK") }</Button>
</DialogTrigger>
</DialogActions>
</DialogBody>
<DialogActions>
<DialogTrigger>
<Button appearance="secondary">{ loc("OK") }</Button>
</DialogTrigger>
</DialogActions>
</DialogSurface>
</Dialog>
);
+7 -7
View File
@@ -33,7 +33,7 @@ export default class PasswordView extends React.Component<IProps, IStates>
};
}
private OnCopyPassword(password : string): void
private OnCopyPassword(password: string): void
{
console.log("PasswordView.OnCopyPassword");
@@ -50,7 +50,7 @@ export default class PasswordView extends React.Component<IProps, IStates>
{
console.log("PasswordView.OnRefreshPassword");
let password : string = Generator.GeneratePassword(this.props.generatorOptions);
let password: string = Generator.GeneratePassword(this.props.generatorOptions);
this.setState({ password });
@@ -69,7 +69,7 @@ export default class PasswordView extends React.Component<IProps, IStates>
if (JSON.stringify(prevProps.generatorOptions) === JSON.stringify(this.props.generatorOptions))
return;
let error : string = Generator.ValidateProps(this.props.generatorOptions);
let error: string = Generator.ValidateProps(this.props.generatorOptions);
let password = Generator.GeneratePassword(this.props.generatorOptions);
this.setState({ password, error });
@@ -78,14 +78,14 @@ export default class PasswordView extends React.Component<IProps, IStates>
this.OnCopyPassword(password);
}
private AlterSpecialsOnce(useSpecials : boolean) : void
private AlterSpecialsOnce(useSpecials: boolean): void
{
console.log("PasswordView.AlterSpecialsOnce", `useSpecials: ${useSpecials}`);
let options : GeneratorOptions = { ...this.props.generatorOptions, Special: useSpecials, ExcludeAmbiguous: true };
let options: GeneratorOptions = { ...this.props.generatorOptions, Special: useSpecials, ExcludeAmbiguous: true };
let error : string = Generator.ValidateProps(options);
let password : string = Generator.GeneratePassword(options);
let error: string = Generator.ValidateProps(options);
let password: string = Generator.GeneratePassword(options);
this.setState({ password, error });
+5 -5
View File
@@ -16,8 +16,8 @@ export default class SettingsSection extends React.Component<IProps>
{
public render(): JSX.Element
{
let options : GeneratorOptions = this.props.generatorOptions;
let settings : Settings = this.props.settings;
let options: GeneratorOptions = this.props.generatorOptions;
let settings: Settings = this.props.settings;
return (
<AccordionItem value="settings">
@@ -28,9 +28,9 @@ export default class SettingsSection extends React.Component<IProps>
<div className="stack">
<Input
id="pwd-length"
value={ options.Length.toString() }
value={ options.Length?.toString() }
onChange={ (_, e) => GeneratorOptions.Update({ Length: parseInt(e.value) }) }
type="number" min={ 4 } />
type="number" min={ 4 } minLength={ 1 } />
<Text size={ 200 }>{ loc("Recommended password length") } <b>16-32</b></Text>
</div>
<Divider />
@@ -66,7 +66,7 @@ export default class SettingsSection extends React.Component<IProps>
<div className="stack">
<div>
<Tooltip content={ loc("Right-click password field to quickly generate password") } relationship="description">
<Checkbox label={ <Text>{loc("Add shortcut to context menu")} <QuestionCircleRegular /></Text> }
<Checkbox label={ <Text>{ loc("Add shortcut to context menu") } <QuestionCircleRegular /></Text> }
checked={ settings.AddContext } onChange={ (_, e) => Settings.Update({ AddContext: e.checked as boolean }) } />
</Tooltip>
</div>
+38 -34
View File
@@ -1,51 +1,55 @@
// BackgroundService.ts
// Background script that handles the context menu visibility
import { Tabs, Menus } from "webextension-polyfill";
import browser from "../Utils/Browser";
import { loc } from "../Utils/Localization";
function UpdateContextMenu(isEnabled: boolean) : void
function UpdateContextMenu(isEnabled: boolean): void
{
console.log("BackgroundService.UpdateContextMenu", isEnabled);
chrome.contextMenus.update("generatePassword", { visible: isEnabled });
browser.contextMenus.update("generatePassword", { visible: isEnabled });
}
async function OnContextClick(info : chrome.contextMenus.OnClickData) : Promise<void>
async function OnContextClick(info: Menus.OnClickData): Promise<void>
{
console.log("BackgroundService.OnContextClick", info);
let tabInfo : chrome.tabs.Tab[] = await chrome.tabs.query({ active: true, currentWindow: true });
let tabInfo: Tabs.Tab[] = await browser.tabs.query({ active: true, currentWindow: true });
console.log("BackgroundService.OnContextClick", tabInfo);
chrome.tabs.sendMessage<string>(tabInfo[0].id, info.menuItemId as string);
browser.tabs.sendMessage(tabInfo[0].id, info.menuItemId as string);
}
if (!chrome.runtime.onInstalled.hasListeners())
chrome.runtime.onInstalled.addListener(async () =>
{
console.log("[BackgroundService] chrome.runtime.onInstalled");
chrome.contextMenus.removeAll();
async function OnInstalled(): Promise<void>
{
console.log("[BackgroundService] browser.runtime.onInstalled");
browser.contextMenus.removeAll();
chrome.contextMenus.create(
{
title: loc("Quick generate password"),
contexts: [ "editable" ],
id: "generatePassword"
}
);
let settings : { [key : string]: any } = await chrome.storage.sync.get({ AddContext: true });
UpdateContextMenu(settings.AddContext);
});
if (!chrome.contextMenus.onClicked.hasListeners())
chrome.contextMenus.onClicked.addListener(OnContextClick);
if (!chrome.storage.sync.onChanged.hasListeners())
chrome.storage.sync.onChanged.addListener(changes =>
browser.contextMenus.create(
{
console.log("[BackgroundService] chrome.storage.sync.onChanged", changes);
if (changes.AddContext?.newValue !== undefined)
UpdateContextMenu(changes.AddContext.newValue);
});
title: loc("Quick generate password"),
contexts: ["editable"],
id: "generatePassword"
}
);
let settings: { [key: string]: any; } = await browser.storage.sync.get({ AddContext: true });
UpdateContextMenu(settings.AddContext);
}
async function OnStorageChanged(changes: any): Promise<void>
{
console.log("[BackgroundService] browser.storage.sync.onChanged", changes);
if (changes.AddContext?.newValue !== undefined)
UpdateContextMenu(changes.AddContext.newValue);
}
if (!browser.runtime.onInstalled.hasListener(OnInstalled))
browser.runtime.onInstalled.addListener(OnInstalled);
if (!browser.contextMenus.onClicked.hasListener(OnContextClick))
browser.contextMenus.onClicked.addListener(OnContextClick);
if (!browser.storage.sync.onChanged.hasListener(OnStorageChanged))
browser.storage.sync.onChanged.addListener(OnStorageChanged);
+27 -24
View File
@@ -1,37 +1,40 @@
// ContentService.ts
// Content script that handles quick password generation through context menu
import browser from "../Utils/Browser";
import Generator from "../Utils/Generator";
import GeneratorOptions from "../Utils/GeneratorOptions";
import { loc } from "../Utils/Localization";
if (!chrome.runtime.onMessage.hasListeners())
chrome.runtime.onMessage.addListener(async message =>
async function OnMessage(message: any): Promise<void>
{
console.log("[ContentService] browser.runtime.onMessage", message);
if (message === "generatePassword")
{
console.log("[ContentService] chrome.runtime.onMessage", message);
let generatorOptions: GeneratorOptions = await GeneratorOptions.Init();
let password: string = Generator.GeneratePassword(generatorOptions);
if (message === "generatePassword")
let input: HTMLInputElement = document.activeElement as HTMLInputElement;
if (!["INPUT", "TEXTAREA"].includes(input.tagName))
return;
console.log("[ContentService] browser.runtime.onMessage", input);
if (input.tagName !== "INPUT" || input.readOnly || !["text", "password"].includes(input.type))
{
let generatorOptions : GeneratorOptions = await GeneratorOptions.Init();
let password : string = Generator.GeneratePassword(generatorOptions);
let input : HTMLInputElement = document.activeElement as HTMLInputElement;
if (![ "INPUT", "TEXTAREA" ].includes(input.tagName))
return;
console.log("[ContentService] chrome.runtime.onMessage", input);
if (input.tagName !== "INPUT" || input.readOnly || ![ "text", "password" ].includes(input.type))
{
window.alert(loc("Quick generator is only available on password fields"));
return;
}
input.focus();
input.value = password;
window.navigator.clipboard.writeText(password);
window.alert(loc("Quick generator is only available on password fields"));
return;
}
});
input.focus();
input.value = password;
window.navigator.clipboard.writeText(password);
}
}
if (!browser.runtime.onMessage.hasListener(OnMessage))
browser.runtime.onMessage.addListener(OnMessage);
console.log("[ContentService] Loaded");
+4
View File
@@ -0,0 +1,4 @@
import Browser from "webextension-polyfill";
const browser: typeof Browser = (process.env.NODE_ENV !== "development") ? require("webextension-polyfill") : null;
export default browser;
+19 -13
View File
@@ -10,21 +10,24 @@ export default class Generator
public static AmbiguousCharacters = "{}[]()/\\'\"`~,;:.<>";
public static SimilarCharacters = "il1Lo0O";
public static GeneratePassword(props : GeneratorOptions) : string
public static GeneratePassword(props: GeneratorOptions): string
{
if (!props.Length || isNaN(props.Length) || props.Length < 4)
props.Length = 4;
// Validating parameters
if (this.ValidateProps(props))
return "";
// Generating password
let availableCharacters : string = this.GetAvailableCharacters(props);
let requiredCharacters : string = this.GetRequiredCharacters(props);
let availableCharacters: string = this.GetAvailableCharacters(props);
let requiredCharacters: string = this.GetRequiredCharacters(props);
let password : string = "";
let password: string = "";
for (let i = 0; i < props.Length; i++)
{
let char : string = this.PickRandomFromArray(availableCharacters);
let char: string = this.PickRandomFromArray(availableCharacters);
if (props.ExcludeRepeating && password.includes(char))
i--;
@@ -43,12 +46,15 @@ export default class Generator
return password;
}
public static ValidateProps(props : GeneratorOptions): string
public static ValidateProps(props: GeneratorOptions): string
{
if (!props.Length || isNaN(props.Length) || props.Length < 4)
props.Length = 4;
if (!props.Lowercase && !props.Uppercase)
return loc("Either lowercase or uppercase characters must be included");
let availableCharacters : string = this.GetAvailableCharacters(props);
let availableCharacters: string = this.GetAvailableCharacters(props);
if (props.ExcludeRepeating && availableCharacters.length < props.Length)
return loc("Selected length is too long to exclude repeating characters");
@@ -56,9 +62,9 @@ export default class Generator
return "";
}
private static GetAvailableCharacters(props : GeneratorOptions) : string
private static GetAvailableCharacters(props: GeneratorOptions): string
{
let availableCharacters : string = "";
let availableCharacters: string = "";
if (props.Special)
availableCharacters += this.SpecialCharacters;
@@ -77,9 +83,9 @@ export default class Generator
return availableCharacters;
}
private static GetRequiredCharacters(props : GeneratorOptions) : string
private static GetRequiredCharacters(props: GeneratorOptions): string
{
let requiredCharacters : string = "";
let requiredCharacters: string = "";
if (props.Special)
requiredCharacters += this.PickRandomFromArray(this.SpecialCharacters);
@@ -100,12 +106,12 @@ export default class Generator
// See https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random
// min is inclusive, max is exclusive
private static GetRandomInt(min : number, max : number) : number
private static GetRandomInt(min: number, max: number): number
{
return Math.floor(Math.random() * (max - min)) + min;
}
private static PickRandomFromArray(array : string) : string
private static PickRandomFromArray(array: string): string
{
return array[this.GetRandomInt(0, array.length)];
}
+15 -12
View File
@@ -1,3 +1,6 @@
import { Storage } from "webextension-polyfill";
import browser from "../Utils/Browser";
export default class GeneratorOptions
{
public Length: number = 16;
@@ -11,34 +14,34 @@ export default class GeneratorOptions
public ExcludeAmbiguous: boolean = true;
public ExcludeRepeating: boolean = false;
public static OnChanged : (changes : Partial<GeneratorOptions>) => void;
public static OnChanged: (changes: Partial<GeneratorOptions>) => void;
public static async Init() : Promise<GeneratorOptions>
public static async Init(): Promise<GeneratorOptions>
{
let fallbackOptions : GeneratorOptions = new GeneratorOptions();
let fallbackOptions: GeneratorOptions = new GeneratorOptions();
if (!chrome?.storage?.sync) // Extension is running as a standalone app
if (!browser?.storage?.sync) // Extension is running as a standalone app
return fallbackOptions;
let props : { [key: string]: any } = await chrome.storage.sync.get(fallbackOptions);
let props: { [key: string]: any; } = await browser.storage.sync.get(fallbackOptions);
chrome.storage.sync.onChanged.addListener(GeneratorOptions.OnStorageChanged);
browser.storage.sync.onChanged.addListener(GeneratorOptions.OnStorageChanged);
return props as GeneratorOptions;
}
public static async Update(changes : Partial<GeneratorOptions>) : Promise<void>
public static async Update(changes: Partial<GeneratorOptions>): Promise<void>
{
if (chrome?.storage?.sync)
await chrome?.storage?.sync?.set(changes);
if (browser?.storage?.sync)
await browser?.storage?.sync?.set(changes);
else
GeneratorOptions.OnChanged(changes);
}
private static OnStorageChanged(changes : { [key: string]: chrome.storage.StorageChange }) : void
private static OnStorageChanged(changes: { [key: string]: Storage.StorageChange; }): void
{
let propsList : string[] = Object.keys(new GeneratorOptions());
let options : { [key: string]: any } = { };
let propsList: string[] = Object.keys(new GeneratorOptions());
let options: { [key: string]: any; } = {};
Object.entries(changes)
.filter(i => propsList.includes(i[0]))
+6 -4
View File
@@ -1,8 +1,10 @@
import browser from "../Utils/Browser";
export default class Localization
{
public static GetString(key : string) : string
public static GetString(key: string): string
{
let sanitizedKey : string = key
let sanitizedKey: string = key
.replaceAll(".", "_")
.replaceAll(",", "_")
.replaceAll(" ", "_")
@@ -10,13 +12,13 @@ export default class Localization
.replaceAll("?", "_")
.replaceAll("!", "_");
let str : string = chrome?.i18n?.getMessage(sanitizedKey);
let str: string = browser?.i18n?.getMessage(sanitizedKey);
return str ?? key;
}
}
export function loc(key : string) : string
export function loc(key: string): string
{
return Localization.GetString(key);
}
+16 -13
View File
@@ -1,36 +1,39 @@
import { Storage } from "webextension-polyfill";
import browser from "../Utils/Browser";
export default class Settings
{
public AddContext : boolean = true;
public Autocopy : boolean = true;
public AddContext: boolean = true;
public Autocopy: boolean = true;
public static OnChanged : (changes : Partial<Settings>) => void;
public static OnChanged: (changes: Partial<Settings>) => void;
public static async Init() : Promise<Settings>
public static async Init(): Promise<Settings>
{
let fallbackOptions = new Settings();
if (!chrome?.storage?.sync)
if (!browser?.storage?.sync)
return fallbackOptions;
let props : { [key: string]: any } = await chrome.storage.sync.get(fallbackOptions);
let props: { [key: string]: any; } = await browser.storage.sync.get(fallbackOptions);
chrome.storage.sync.onChanged.addListener(Settings.OnStorageChanged);
browser.storage.sync.onChanged.addListener(Settings.OnStorageChanged);
return props as Settings;
}
public static async Update(changes : Partial<Settings>) : Promise<void>
public static async Update(changes: Partial<Settings>): Promise<void>
{
if (chrome?.storage?.sync)
await chrome?.storage?.sync?.set(changes);
if (browser?.storage?.sync)
await browser?.storage?.sync?.set(changes);
else
Settings.OnChanged(changes);
}
private static OnStorageChanged(changes : { [key: string]: chrome.storage.StorageChange }) : void
private static OnStorageChanged(changes: { [key: string]: Storage.StorageChange; }): void
{
let propsList : string[] = Object.keys(new Settings());
let settings : { [key: string]: any } = { };
let propsList: string[] = Object.keys(new Settings());
let settings: { [key: string]: any; } = {};
Object.entries(changes)
.filter(i => propsList.includes(i[0]))