Merge branch 'development-GUI' of https://codeberg.org/buzzcode2007/ShopAI into development-GUI
This commit is contained in:
commit
de986f56dc
22 changed files with 653 additions and 479 deletions
|
@ -86,6 +86,9 @@
|
|||
"term_refresh": {
|
||||
"message": "Refresh"
|
||||
},
|
||||
"page_opening": {
|
||||
"message": "Opening..."
|
||||
},
|
||||
|
||||
"settings_general_showApplicable": {
|
||||
"message": "Show product's ratings in this extension's icon"
|
||||
|
@ -94,7 +97,7 @@
|
|||
"message": "Inject a quick access button"
|
||||
},
|
||||
"settings_general_autoOpen": {
|
||||
"message": "Automatically open the side panel"
|
||||
"message": "Automatically open the popup"
|
||||
},
|
||||
"settings_behavior_autoRun": {
|
||||
"message": "Automatically run this extension on a supported page"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<script src="../scripts/pages/popup.js" type="module"></script>
|
||||
<link href="/styles/popup.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body role="window">
|
||||
<body>
|
||||
<header>
|
||||
<nav id="header" class="nav-wrapper transparent">
|
||||
<ul class="right">
|
||||
|
|
|
@ -4,6 +4,8 @@ Window and window content management */
|
|||
import texts from "../../mapping/read.js";
|
||||
import net from "/scripts/utils/net.js";
|
||||
import Window from "../window.js";
|
||||
import logging from '/scripts/logging.js';
|
||||
import {global, observe} from "/scripts/secretariat.js";
|
||||
|
||||
export default class windowman {
|
||||
static new(URL, height, width) {
|
||||
|
@ -39,14 +41,11 @@ export default class windowman {
|
|||
throw new ReferenceError((new texts(`error_msg_fileNotFound`, [source])).localized);
|
||||
}
|
||||
} catch(err) {
|
||||
const secretariat = (await import(chrome.runtime.getURL(`/scripts/secretariat.js`)));
|
||||
const logging = (await import(chrome.runtime.getURL(`/scripts/logging.js`))).default;
|
||||
|
||||
// Raise an alert.
|
||||
logging.error(err.name, err.message, err.stack, true, [source]);
|
||||
|
||||
// Stop loading the page when an error has occured; it's not going to work!
|
||||
if ((await secretariat.read(`debug`, -1) != null) ? await secretariat.read(`debug`, -1) : true) {
|
||||
if ((await global.read(`debug`, -1) != null) ? await global.read(`debug`, -1) : true) {
|
||||
window.close();
|
||||
};
|
||||
};
|
||||
|
@ -199,14 +198,16 @@ export default class windowman {
|
|||
: null;
|
||||
target[`dimensions`][`width`] = (button.getAttribute(`tab-width`)) ? parseInt(button.getAttribute(`tab-width`))
|
||||
: null;
|
||||
target[`path`] = (
|
||||
!target[`source`].includes(`://`)
|
||||
? window.location.pathname.split(`/`).slice(0, -1).join(`/`).concat(`/`)
|
||||
: ``
|
||||
).concat(target[`source`]);
|
||||
|
||||
const event = function () {
|
||||
const event = () => {
|
||||
// Get the correct path.
|
||||
target[`path`] = (
|
||||
!target[`source`].includes(`://`)
|
||||
? window.location.pathname.split(`/`).slice(0, -1).join(`/`).concat(`/`)
|
||||
: ``
|
||||
).concat(target[`source`]);
|
||||
|
||||
new logging((new texts(`page_opening`)).localized, target[`path`]);
|
||||
|
||||
// Open the window as a popup.
|
||||
new Window(target[`path`], Object.assign(target[`dimensions`], {"type": "popup"}));
|
||||
|
@ -226,12 +227,12 @@ export default class windowman {
|
|||
(document.querySelectorAll(`.sidenav`)).forEach(function (sidebar_element) {
|
||||
if (sidebar_element.getAttribute(`name`)) {
|
||||
document.querySelector(`[works-sidebar="${sidebar_element.getAttribute(`name`)}"]`)
|
||||
.addEventListener(`click`, function () {
|
||||
.addEventListener(`click`, () => {
|
||||
M.Sidenav.getInstance(sidebar_element).open();
|
||||
});
|
||||
} else if (document.querySelector(`[data-action="ui,open,navbar"]`)) {
|
||||
document.querySelector(`[data-action="ui,open,navbar"]`).forEach(function (button_element) {
|
||||
button_element.addEventListener(`click`, function() {
|
||||
button_element.addEventListener(`click`, () => {
|
||||
M.Sidenav.getInstance(sidebar).open();
|
||||
});
|
||||
});
|
||||
|
@ -257,10 +258,6 @@ export default class windowman {
|
|||
|
||||
/* Run this function if you would like to synchronize with data. */
|
||||
async sync() {
|
||||
// Import the module.
|
||||
const secretariat = await import(chrome.runtime.getURL("scripts/secretariat.js"));
|
||||
const logging = (await import(chrome.runtime.getURL(`/scripts/logging.js`))).default;
|
||||
|
||||
async function fill() {
|
||||
let input_elements = document.querySelectorAll("[data-store]");
|
||||
|
||||
|
@ -270,7 +267,7 @@ export default class windowman {
|
|||
let data = {};
|
||||
data[`source`] = input_element.getAttribute(`data-store`);
|
||||
// data[`origin`] = (input_element.hasAttribute(`data-store-location`)) ? parseInt(input_element.getAttribute(`data-store-location`)) : -1
|
||||
data[`value`] = secretariat.read(data[`source`]);
|
||||
data[`value`] = global.read(data[`source`]);
|
||||
|
||||
data[`value`].then(async function(value) {
|
||||
switch (input_element.getAttribute(`type`).toLowerCase()) {
|
||||
|
@ -315,7 +312,7 @@ export default class windowman {
|
|||
UI_item[`source`] = this.getAttribute(`data-store`);
|
||||
UI_item[`value`] = this.checked;
|
||||
UI_item[`store`] = (this.hasAttribute(`data-store-location`)) ? parseInt(this.getAttribute(`data-store-location`)) : -1;
|
||||
secretariat.write(UI_item[`source`], UI_item[`value`], UI_item[`store`]);
|
||||
global.write(UI_item[`source`], UI_item[`value`], UI_item[`store`]);
|
||||
};
|
||||
break;
|
||||
default:
|
||||
|
@ -337,7 +334,7 @@ export default class windowman {
|
|||
: parseInt(this.value)
|
||||
: this.value;
|
||||
UI_item[`store`] = (this.hasAttribute(`data-store-location`)) ? parseInt(this.getAttribute(`data-store-location`)) : -1;
|
||||
secretariat.write(UI_item[`source`], UI_item[`value`], UI_item[`store`]);
|
||||
global.write(UI_item[`source`], UI_item[`value`], UI_item[`store`]);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
@ -351,7 +348,7 @@ export default class windowman {
|
|||
*/
|
||||
async function updates() {
|
||||
// Get the storage data.
|
||||
let storage_data = await secretariat.read();
|
||||
let storage_data = await global.read();
|
||||
|
||||
async function enable() {
|
||||
let input_elements = document.querySelectorAll("[data-enable]");
|
||||
|
@ -360,10 +357,10 @@ export default class windowman {
|
|||
input_elements.forEach(async (input_element) => {
|
||||
if (input_element.getAttribute("data-enable")) {
|
||||
// Enable the element.
|
||||
input_element.disabled = ((await secretariat.read(input_element.getAttribute("data-enable"))) != null
|
||||
? (typeof (await secretariat.read(input_element.getAttribute("data-enable")))).includes(`obj`)
|
||||
? (Object.keys(await secretariat.read(input_element.getAttribute("data-enable")))).length <= 0
|
||||
: !(!!(await secretariat.read(input_element.getAttribute("data-enable"))))
|
||||
input_element.disabled = ((await global.read(input_element.getAttribute("data-enable"))) != null
|
||||
? (typeof (await global.read(input_element.getAttribute("data-enable")))).includes(`obj`)
|
||||
? (Object.keys(await global.read(input_element.getAttribute("data-enable")))).length <= 0
|
||||
: !(!!(await global.read(input_element.getAttribute("data-enable"))))
|
||||
: true);
|
||||
(input_element.disabled) ? input_element.classList.add(`disabled`) : input_element.classList.remove(`disabled`);
|
||||
|
||||
|
@ -378,7 +375,7 @@ export default class windowman {
|
|||
}
|
||||
|
||||
// Update the input elements.
|
||||
secretariat.observe((what) => {
|
||||
observe((what) => {
|
||||
enable();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {read, write, search, observe} from "/scripts/secretariat.js";
|
||||
import {global, observe} from "/scripts/secretariat.js";
|
||||
import logging from "/scripts/logging.js"
|
||||
import texts from "/scripts/mapping/read.js";
|
||||
|
||||
|
@ -114,7 +114,7 @@ export default class UI {
|
|||
var DATA = {};
|
||||
|
||||
DATA[`target`] = ((ELEMENT.getAttribute(`data-result-store`).split(`,`))[0] == ``) ? [...(ELEMENT.getAttribute(`data-result-store`).split(`,`).slice(1)), ...[NAME]] : [...AREA, ...[NAME], ...(ELEMENT.getAttribute(`data-result-store`).split(`,`))];
|
||||
DATA[`value`] = ((Object.keys(ITEM).includes(ELEMENT.getAttribute(`data-result-store`))) ? ITEM[ELEMENT.getAttribute(`data-result-store`)] : await read(DATA[`target`], (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1));
|
||||
DATA[`value`] = ((Object.keys(ITEM).includes(ELEMENT.getAttribute(`data-result-store`))) ? ITEM[ELEMENT.getAttribute(`data-result-store`)] : await global.read(DATA[`target`], (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1));
|
||||
|
||||
switch (ELEMENT[`type`]) {
|
||||
case `checkbox`:
|
||||
|
@ -122,7 +122,7 @@ export default class UI {
|
|||
|
||||
ELEMENT[`function`] = function() {
|
||||
DATA[`target`] = ((ELEMENT.getAttribute(`data-result-store`).split(`,`))[0] == ``) ? [...(ELEMENT.getAttribute(`data-result-store`).split(`,`).slice(1)), ...[NAME]] : [...AREA, ...[NAME], ...(ELEMENT.getAttribute(`data-result-store`).split(`,`))];
|
||||
write(DATA[`target`], ELEMENT.checked, (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
globa.write(DATA[`target`], ELEMENT.checked, (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
};
|
||||
break;
|
||||
default:
|
||||
|
@ -133,7 +133,7 @@ export default class UI {
|
|||
try {
|
||||
DATA[`target`] = ((ELEMENT.getAttribute(`data-result-store`).split(`,`))[0] == ``) ? [...(ELEMENT.getAttribute(`data-result-store`).split(`,`).slice(1)), ...[NAME]] : [...AREA, ...[NAME], ...(ELEMENT.getAttribute(`data-result-store`).split(`,`))];
|
||||
DATA[`value`] = JSON.parse(ELEMENT.value.trim());
|
||||
write(DATA[`target`], DATA[`value`], (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
globa.write(DATA[`target`], DATA[`value`], (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
} catch(err) {
|
||||
// The JSON isn't valid.
|
||||
logging.error(err.name, texts.localized(`JSON_parse_error`), err.stack, false);
|
||||
|
@ -144,7 +144,7 @@ export default class UI {
|
|||
|
||||
ELEMENT[`function`] = function() {
|
||||
DATA[`target`] = ((ELEMENT.getAttribute(`data-result-store`).split(`,`))[0] == ``) ? [...(ELEMENT.getAttribute(`data-result-store`).split(`,`).slice(1)), ...[NAME]] : [...AREA, ...[NAME], ...(ELEMENT.getAttribute(`data-result-store`).split(`,`))];
|
||||
write(DATA[`target`], ELEMENT.value.trim(), (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
globa.write(DATA[`target`], ELEMENT.value.trim(), (ELEMENT.hasAttribute(`data-store-location`)) ? parseInt(ELEMENT.getAttribute(`data-store-location`)) : -1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -162,7 +162,7 @@ export default class UI {
|
|||
: ITEM[ELEMENT.getAttribute(`data-result-content`)])
|
||||
: ((ITEM[ELEMENT.getAttribute(`data-result-store`)])
|
||||
? (ITEM[ELEMENT.getAttribute(`data-result-store`)])
|
||||
: null) /*read(((ITEM[(ELEMENT.getAttribute(`data-result-store`).split(`,`))])[ITEM])));*/
|
||||
: null) /*global.read(((ITEM[(ELEMENT.getAttribute(`data-result-store`).split(`,`))])[ITEM])));*/
|
||||
}
|
||||
} else {
|
||||
if (ELEMENT.getAttribute(`data-result-store`) && ELEMENT.type) {
|
||||
|
@ -212,9 +212,9 @@ export default class UI {
|
|||
.getAttribute(`data-results-filters`)
|
||||
.split(`,`);
|
||||
}
|
||||
SEARCH[element.getAttribute(`data-result`)][`results`] = await search(element.getAttribute(`data-result`), SEARCH[element.getAttribute(`data-result`)][`criteria`], SEARCH[element.getAttribute(`data-result`)][`additional criteria`]);
|
||||
SEARCH[element.getAttribute(`data-result`)][`results`] = await global.search(element.getAttribute(`data-result`), SEARCH[element.getAttribute(`data-result`)][`criteria`], SEARCH[element.getAttribute(`data-result`)][`additional criteria`]);
|
||||
} else {
|
||||
SEARCH[element.getAttribute(`data-result`)][`results`] = await read(element.getAttribute(`data-result`));
|
||||
SEARCH[element.getAttribute(`data-result`)][`results`] = await global.read(element.getAttribute(`data-result`));
|
||||
};
|
||||
|
||||
display(element.getAttribute(`data-result`), SEARCH[element.getAttribute(`data-result`)][`results`], `name`);
|
||||
|
|
|
@ -2,7 +2,7 @@ import Window from "/scripts/GUI/window.js";
|
|||
|
||||
export default class ManagedWindow {
|
||||
constructor () {
|
||||
this.instance = new Window("/pages/popup.htm", {"width": "120", "height": "200", "type": "popup", "hidden": true});
|
||||
this.instance = new Window("/pages/popup.htm", {"width": 500, "height": 500, "type": "popup", "hidden": true});
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class EntryManager {
|
|||
Window.addActionListener(`onFocusChanged`, (data) => {this.onRefresh()});
|
||||
|
||||
// Add the context menu event.
|
||||
IconIndicator.set(this.instances.popup.show);
|
||||
IconIndicator.set(() => {this.instances.popup.show()});
|
||||
this.instances.menu.menu.onclick(() => {this.instances.popup.show()});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,36 +5,46 @@ export default class Window {
|
|||
|
||||
constructor(url, options) {
|
||||
this.url = url;
|
||||
|
||||
if ((typeof options).includes(`obj`) && options != null) {
|
||||
this.#options = options;
|
||||
(Object.keys(options)).forEach((OPTION) => {
|
||||
this[OPTION] = options[OPTION];
|
||||
});
|
||||
}
|
||||
|
||||
// Check if the URL starts with a valid protocol. If not, it is most likely an extension page.
|
||||
if (!(this.url.startsWith(`http`) && this.url.contains(`://`))) {
|
||||
this.url = chrome.runtime.getURL(this.url);
|
||||
}
|
||||
|
||||
this.#options = (this.#options) ? this.#options : {};
|
||||
this.#options[`url`] = (this.url != this.#options[`url`]) ? this.url : this.#options[`url`];
|
||||
|
||||
// Remove options only readable here and not in the API.
|
||||
(this.hidden != null) ? delete this.#options.hidden : this.hidden = false;
|
||||
this.update(options);
|
||||
|
||||
// Show the window if it's not hidden.
|
||||
(!this.hidden) ? this.show() : false;
|
||||
}
|
||||
|
||||
/*
|
||||
Check this window's state.
|
||||
*/
|
||||
#check() {
|
||||
const deactivate = () => {
|
||||
delete this.ID;
|
||||
this.hidden = true;
|
||||
};
|
||||
|
||||
// Determine if this window is still open.
|
||||
try {
|
||||
(this.ID)
|
||||
? chrome.windows.get(this.ID, (window) => {
|
||||
(window == null) ? deactivate() : false;
|
||||
})
|
||||
: false;
|
||||
} catch(err) {
|
||||
deactivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Show the window.
|
||||
*/
|
||||
show() {
|
||||
chrome.windows.create(this.#options, (window) => {
|
||||
this.ID = window.id;
|
||||
});
|
||||
this.#check();
|
||||
|
||||
if (!this.ID) {
|
||||
chrome.windows.create(this.#options, (window) => {
|
||||
this.hidden = false;
|
||||
this.ID = window.id;
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,11 +52,14 @@ export default class Window {
|
|||
Hide the window.
|
||||
*/
|
||||
hide() {
|
||||
this.#check();
|
||||
|
||||
// Close the window if it is still defined.
|
||||
(this.ID) ? chrome.windows.remove(this.ID) : false;
|
||||
|
||||
// Remove the ID.
|
||||
delete this.ID;
|
||||
this.hidden = true;
|
||||
}
|
||||
|
||||
/* Create an action listener.
|
||||
|
@ -61,4 +74,46 @@ export default class Window {
|
|||
// Add the event.
|
||||
chrome.windows[event].addListener(callback);
|
||||
}
|
||||
|
||||
/*
|
||||
Set the options of the window.
|
||||
*/
|
||||
update(options) {
|
||||
if ((typeof options).includes(`obj`) && options != null) {
|
||||
// Merge the options if defined. If not, set it to the options itself.
|
||||
this.#options = (this.#options)
|
||||
? Object.assign(this.#options, options)
|
||||
: options;
|
||||
|
||||
(Object.keys(options)).forEach((OPTION) => {
|
||||
this[OPTION] = options[OPTION];
|
||||
});
|
||||
}
|
||||
|
||||
// Check if the URL starts with a valid protocol. If not, it is most likely an extension page.
|
||||
(!(this.url.indexOf(`://`) > 2))
|
||||
? this.url = chrome.runtime.getURL(this.url)
|
||||
: false;
|
||||
|
||||
this.#options[`url`] = (this.url != this.#options[`url`]) ? this.url : this.#options[`url`];
|
||||
|
||||
// Make sure height and width are integers.
|
||||
[`height`, `width`].forEach((DIMENSION) => {
|
||||
if (this[DIMENSION]) {
|
||||
this[DIMENSION] = Math.abs(parseInt(this[DIMENSION]));
|
||||
this.#options[DIMENSION] = this[DIMENSION];
|
||||
}
|
||||
});
|
||||
|
||||
// Remove options only readable here and not in the API.
|
||||
(this.hidden != null)
|
||||
? delete this.#options.hidden
|
||||
: this.hidden = ((this.hidden != null)
|
||||
? this.hidden
|
||||
: false);
|
||||
|
||||
// Update windows already open.
|
||||
this.#check();
|
||||
(this.ID) ? chrome.windows.update(this.ID, options) : false;
|
||||
}
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
import Tabs from "/scripts/GUI/tabs.js";
|
||||
import EntryManager from "/scripts/GUI/entrypoints/manager.js"
|
||||
|
||||
export default class user_actions {
|
||||
static init() {
|
||||
user_actions.tabs();
|
||||
};
|
||||
|
||||
static tabs() {
|
||||
new EntryManager();
|
||||
}
|
||||
};
|
7
scripts/background/background.check.js
Normal file
7
scripts/background/background.check.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import EntryManager from "/scripts/GUI/entrypoints/manager.js"
|
||||
|
||||
export default class BackgroundCheck {
|
||||
static init() {
|
||||
new EntryManager();
|
||||
};
|
||||
};
|
14
scripts/background/background.messaging.js
Normal file
14
scripts/background/background.messaging.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* link.js
|
||||
Link the content scripts to the background scripts.
|
||||
*/
|
||||
|
||||
class BackgroundMessaging {
|
||||
constructor () {
|
||||
// Create a listener for events.
|
||||
/* chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
|
||||
|
||||
});*/
|
||||
}
|
||||
}
|
||||
|
||||
export default BackgroundMessaging;
|
|
@ -2,7 +2,7 @@
|
|||
This script provides installation run scripts.
|
||||
*/
|
||||
|
||||
import { init, read, write, observe } from "../secretariat.js";
|
||||
import { template, global, observe } from "../secretariat.js";
|
||||
import filters from "../filters.js";
|
||||
let config = chrome.runtime.getURL("config/config.json");
|
||||
|
||||
|
@ -36,7 +36,7 @@ export default class fc {
|
|||
|
||||
// Run the storage initialization.
|
||||
delete configuration[`OOBE`];
|
||||
init(configuration);
|
||||
template.set(configuration);
|
||||
|
||||
// Update the filters to sync with synchronized storage data.
|
||||
(new filters).update();
|
||||
|
@ -60,14 +60,14 @@ export default class fc {
|
|||
}
|
||||
|
||||
static async every() {
|
||||
read([`settings`,`sync`]).then(async (DURATION_PREFERENCES) => {
|
||||
global.read([`settings`,`sync`]).then(async (DURATION_PREFERENCES) => {
|
||||
// Forcibly create the preference if it doesn't exist. It's required!
|
||||
if (!(typeof DURATION_PREFERENCES).includes(`obj`) || DURATION_PREFERENCES == null || Array.isArray(DURATION_PREFERENCES)) {
|
||||
DURATION_PREFERENCES = {};
|
||||
DURATION_PREFERENCES[`duration`] = 24;
|
||||
|
||||
// Write it.
|
||||
await write([`settings`, `sync`], DURATION_PREFERENCES, -1, {"silent": true});
|
||||
await global.write([`settings`, `sync`], DURATION_PREFERENCES, -1, {"silent": true});
|
||||
};
|
||||
|
||||
if (((typeof DURATION_PREFERENCES).includes(`obj`) && DURATION_PREFERENCES != null && !Array.isArray(DURATION_PREFERENCES)) ? ((DURATION_PREFERENCES[`duration`]) ? (DURATION_PREFERENCES[`duration`] > 0) : false) : false) {
|
||||
|
@ -92,8 +92,8 @@ export default class fc {
|
|||
|
||||
let updater_interval = async () => {
|
||||
|
||||
if ((await read([`settings`, `sync`, `duration`])) ? (await read([`settings`, `sync`, `duration`] * (60 ** 2) * 1000 != DURATION_PREFERENCES[`duration`])) : false) {
|
||||
DURATION_PREFERENCES[`duration`] = await read([`settings`, `sync`, `duration`]) * (60 ** 2) * 1000;
|
||||
if ((await global.read([`settings`, `sync`, `duration`])) ? (await global.read([`settings`, `sync`, `duration`] * (60 ** 2) * 1000 != DURATION_PREFERENCES[`duration`])) : false) {
|
||||
DURATION_PREFERENCES[`duration`] = await global.global.read([`settings`, `sync`, `duration`]) * (60 ** 2) * 1000;
|
||||
|
||||
// Reset the updater.
|
||||
updater_cancel(UPDATER);
|
||||
|
|
|
@ -3,7 +3,9 @@ Shop wisely with AI!
|
|||
*/
|
||||
|
||||
import fc from './fc.js';
|
||||
import user_actions from "../actions.js";
|
||||
import BackgroundCheck from "./background.check.js";
|
||||
import BackgroundMessaging from "./background.messaging.js";
|
||||
|
||||
fc.run();
|
||||
user_actions.init();
|
||||
BackgroundCheck.init();
|
||||
new BackgroundMessaging();
|
26
scripts/external/watch.js
vendored
26
scripts/external/watch.js
vendored
|
@ -24,20 +24,28 @@ export default class watch {
|
|||
new logging((new texts(`message_external_supported`)).localized);
|
||||
|
||||
// Begin only when the page is fully loaded.
|
||||
window.addEventListener(`DOMContentLoaded`, (event) => {
|
||||
// Begin processing.
|
||||
console.log(`processing...`);
|
||||
let PROC = new processor(filter);
|
||||
});
|
||||
document.onreadystatechange = () => {
|
||||
if (document.readyState == 'complete') {
|
||||
let PROC = new processor(filter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static main() {
|
||||
/* The main action. */
|
||||
(check.platform()).then((RULES) => {
|
||||
if (RULES && Object.keys(RULES).length > 0) {
|
||||
watch.process(RULES);
|
||||
(check.platform()).then((FILTER_RESULT) => {
|
||||
if (FILTER_RESULT && Object.keys(FILTER_RESULT).length > 0) {
|
||||
watch.process(FILTER_RESULT);
|
||||
watch.callGUI();
|
||||
|
||||
// Create a listener for messages indicating re-processing.
|
||||
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
|
||||
// Get the tabId where this content script is running.
|
||||
|
||||
|
||||
(((typeof message).includes(`obj`) && !Array.isArray(message)) ? message[`refresh`] : false) ? watch.process(FILTER_RESULT) : false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
Manage filters.
|
||||
*/
|
||||
|
||||
import {read, write, forget, search} from "./secretariat.js";
|
||||
import {global} from "./secretariat.js";
|
||||
import net from "/scripts/utils/net.js";
|
||||
import texts from "/scripts/mapping/read.js";
|
||||
import {Queue} from "/scripts/utils/common.js";
|
||||
|
@ -11,13 +11,16 @@ import logging from "/scripts/logging.js"
|
|||
|
||||
export default class filters {
|
||||
constructor() {
|
||||
this.all = async () => {
|
||||
return read(`filters`, -1).then((filters) => {
|
||||
return filters;
|
||||
});
|
||||
};
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/*
|
||||
Get all filters.
|
||||
*/
|
||||
async refresh() {
|
||||
this.all = await global.read(`filters`);
|
||||
};
|
||||
|
||||
/* Select the most appropriate filter based on a URL.
|
||||
|
||||
@param {string} URL the current URL
|
||||
|
@ -32,7 +35,7 @@ export default class filters {
|
|||
if (URL) {
|
||||
let SELECTED = await (async () => {
|
||||
// Get the filters.
|
||||
let filter = await search(`filters`, URL, `URL`, 0.5, {"cloud": -1});
|
||||
let filter = await global.search(`filters`, URL, `URL`, 0.5, {"cloud": -1});
|
||||
|
||||
// If there are filters, then filter the URL.
|
||||
return filter;
|
||||
|
@ -62,7 +65,7 @@ export default class filters {
|
|||
}
|
||||
} else {
|
||||
// Add every item to the queue based on what was loaded first.
|
||||
let FILTERS_ALL = await read(["settings", `filters`]);
|
||||
let FILTERS_ALL = await global.read(["settings", `filters`]);
|
||||
if (((typeof (FILTERS_ALL)).includes(`obj`) && !Array.isArray(FILTERS_ALL)) ? Object.keys(FILTERS_ALL).length > 0 : false) {
|
||||
for (let FILTER_URL_INDEX = 0; FILTER_URL_INDEX < Object.keys(FILTERS_ALL).length; FILTER_URL_INDEX++) {
|
||||
let FILTER_URL = (Object.keys(FILTERS_ALL, 1))[FILTER_URL_INDEX];
|
||||
|
@ -87,12 +90,12 @@ export default class filters {
|
|||
// Only work when the filter is valid.
|
||||
if (result) {
|
||||
// Write the filter to storage.
|
||||
await write(["filters", filter_URL], result, -1, {"silent": true});
|
||||
await global.write(["filters", filter_URL], result, -1, {"silent": true});
|
||||
new logging(texts.localized(`settings_filters_update_status_complete`,null,[filter_URL]));
|
||||
|
||||
// Add the filter to the sync list.
|
||||
if ((await read(["settings", `filters`])) ? !((Object.keys(await read(["settings", `filters`]))).includes(filter_URL)) : true) {
|
||||
write(["settings", `filters`, filter_URL], true, 1, {"silent": true});
|
||||
if ((await global.read(["settings", `filters`])) ? !((Object.keys(await global.read(["settings", `filters`]))).includes(filter_URL)) : true) {
|
||||
global.write(["settings", `filters`, filter_URL], true, 1, {"silent": true});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -107,7 +110,7 @@ export default class filters {
|
|||
}
|
||||
|
||||
// Regardless of the download result, update will also mean setting the filters object to whatever is in storage.
|
||||
this.all = await read(`filters`, -1);
|
||||
this.all = await global.read(`filters`, -1);
|
||||
|
||||
return this.all;
|
||||
}
|
||||
|
@ -118,7 +121,7 @@ export default class filters {
|
|||
*/
|
||||
async remove(URL) {
|
||||
if (URL.includes(`://`)) {
|
||||
return((await forget([`filters`, URL], -1, false)) ? await forget([`settings`, `filters`, URL], 1, true) : false);
|
||||
return((await global.forget([`filters`, URL], -1, false)) ? global.forget([`settings`, `filters`, URL], 1, true) : false);
|
||||
} else {
|
||||
// Inform the user of the removal being unnecessary.
|
||||
logging.warn(texts.localized(`settings_filters_removal_stop`));
|
||||
|
|
|
@ -30,8 +30,13 @@ export default class logging {
|
|||
|
||||
(PRIORITY) ? this.clear() : false;
|
||||
|
||||
// Display the message.
|
||||
console.log((MESSAGE ? (this.title).concat(`\n`) : ``).concat(this.message));
|
||||
// Display the message.
|
||||
if (MESSAGE) {
|
||||
console.log('%c%s%c\n%s', 'font-weight: bold;', this.title, ``, this.message);
|
||||
} else {
|
||||
console.log(this.message);
|
||||
}
|
||||
|
||||
try {
|
||||
M.toast({ text: (MESSAGE ? (this.title).concat(`\n`) : ``).concat(this.message) });
|
||||
} catch (err) {}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Import modules.
|
||||
//import { windowman } from "../windowman.js";
|
||||
|
||||
import {read, forget} from "/scripts/secretariat.js";
|
||||
import {global} from "/scripts/secretariat.js";
|
||||
import Page from "/scripts/pages/page.js";
|
||||
import texts from "/scripts/mapping/read.js";
|
||||
|
||||
|
@ -90,8 +90,8 @@ class Page_Settings extends Page {
|
|||
document
|
||||
.querySelector(`[data-action="storage,clear"]`)
|
||||
.addEventListener(`click`, async () => {
|
||||
await forget(`sites`);
|
||||
console.log(await read(null, 1), await read(null, -1));
|
||||
await global.forget(`sites`);
|
||||
console.log(await global.read(null, 1), await global.read(null, -1));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Sidebar from '../GUI/sidebar.js';
|
||||
import {read, write} from '../secretariat.js';
|
||||
import {global} from '../secretariat.js';
|
||||
|
||||
class sidebar_handler extends Page {
|
||||
constructor () {
|
||||
|
@ -7,6 +7,6 @@ class sidebar_handler extends Page {
|
|||
}
|
||||
|
||||
async activate () {
|
||||
await read(`settings,behavior,autoRun`)
|
||||
await global.read(`settings,behavior,autoRun`)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
Ask product information to Google Gemini. */
|
||||
|
||||
// Import the storage management module.
|
||||
const secretariat = await import(chrome.runtime.getURL("scripts/secretariat.js"));
|
||||
import {global, session, compare} from "/scripts/secretariat.js";
|
||||
import hash from "/scripts/utils/hash.js";
|
||||
import texts from "/scripts/mapping/read.js";
|
||||
|
||||
|
@ -40,32 +40,40 @@ export default class product {
|
|||
/* Attach the product data to the storage. */
|
||||
async attach() {
|
||||
// Add the data digest.
|
||||
this.#snip = (await hash.digest(this.details, {"output": "Number"}));
|
||||
this.#snip = (await hash.digest(this.details, {"output": "Array"}));
|
||||
|
||||
// Add the status about this data.
|
||||
this.status = {};
|
||||
this.status[`update`] = !secretariat.compare([`sites`, this.URL, `snip`], this.#snip);
|
||||
this.status[`update`] = !(await (compare([`sites`, this.URL, `snip`], this.#snip)));
|
||||
}
|
||||
|
||||
async save() {
|
||||
// Stop when not attached (basically, not entirely initialized).
|
||||
if (!this.#snip) {throw new ReferenceError((new texts(`error_msg_notattached`)).localized)};
|
||||
|
||||
// Save the data to the storage.
|
||||
await secretariat.write([`sites`, this.URL, `snip`], this.#snip, 1);
|
||||
// Write the data to the session storage, indicating that it is the last edited.
|
||||
await session.write([`sites`, this.URL, `snip`], this.#snip, 1);
|
||||
await session.write([`last`], this.URL);
|
||||
|
||||
// There is only a need to save the data if an update is needed.
|
||||
if (this.status[`update`]) {
|
||||
// Save the data to the storage.
|
||||
await global.write([`sites`, this.URL, `snip`], this.#snip, 1);
|
||||
|
||||
// Write the analysis data to the storage.
|
||||
(this[`analysis`]) ? global.write([`sites`, this.URL, `analysis`], this.analysis, 1): false;
|
||||
}
|
||||
|
||||
// Write the analysis data to the storage.
|
||||
(this[`analysis`]) ? secretariat.write([`sites`, this.URL, `analysis`], this.analysis, 1): false;
|
||||
};
|
||||
|
||||
async analyze() {
|
||||
// Stop when the data is already analyzed.
|
||||
if (this[`analysis`]) {return(this.analysis)}
|
||||
else if (this.status ? (!this.status.update) : false) {this.analysis = await secretariat.read([`sites`, this.URL, `analysis`]);}
|
||||
else if (this.status ? (!this.status.update) : false) {this.analysis = await global.read([`sites`, this.URL, `analysis`]);}
|
||||
if ((this.analysis && this.analysis != null && this.analysis != undefined) ? !((typeof this.analysis).includes(`obj`) && !Array.isArray(this.analysis)) : true) {
|
||||
// Analyze the data.
|
||||
const gemini = (await import(chrome.runtime.getURL("scripts/AI/gemini.js"))).default;
|
||||
let analyzer = new gemini (await secretariat.read([`settings`,`analysis`,`api`,`key`]), `gemini-pro`);
|
||||
let analyzer = new gemini (await global.read([`settings`,`analysis`,`api`,`key`]), `gemini-pro`);
|
||||
|
||||
// Analyze the data.
|
||||
let PROMPT = [];
|
||||
|
|
|
@ -6,287 +6,361 @@ import logging from "/scripts/logging.js";
|
|||
import texts from "/scripts/mapping/read.js";
|
||||
import hash from "/scripts/utils/hash.js";
|
||||
|
||||
/* Read all stored data in the browser cache.
|
||||
|
||||
@param {array} DATA_NAME the data name
|
||||
@param {int} CLOUD determine cloud reading, which is otherwise set to automatic (0)
|
||||
@param {string} PARAMETER_CHECK Determine which parameter to check via regular expressions.
|
||||
@return {object} the data
|
||||
/*
|
||||
Global data storage, which refers to local and synchronized storage
|
||||
*/
|
||||
export async function read(DATA_NAME, CLOUD = 0) {
|
||||
// Initialize the selected pref data.
|
||||
let DATA, DATA_RETURNED;
|
||||
|
||||
/*
|
||||
Get all storage values.
|
||||
|
||||
@param {number} SOURCE the data source
|
||||
*/
|
||||
async function read_database(SOURCE = -1) {
|
||||
let data, data_returned;
|
||||
|
||||
async function read_database_local() {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.storage.local.get(null, function (result) {
|
||||
if (chrome.runtime.lastError) {
|
||||
// Something went wrong
|
||||
reject(new Error(chrome.runtime.lastError));
|
||||
} else {
|
||||
// If the key exists, return the value
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function read_database_sync() {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.storage.sync.get(null, function (result) {
|
||||
if (chrome.runtime.lastError) {
|
||||
// Something went wrong
|
||||
reject(new Error(chrome.runtime.lastError));
|
||||
} else {
|
||||
// If the key exists, return the value
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Return the data.
|
||||
data_returned = (SOURCE > 0) ? read_database_sync() : read_database_local();
|
||||
return data_returned;
|
||||
}
|
||||
|
||||
/* Recursively find through each data, returning either that value or null when the object is not found.
|
||||
|
||||
@param {dictionary} DATA_ALL the data
|
||||
@param {object} DATA_PATH the path of the data
|
||||
class global {
|
||||
/* Read all stored data in the browser cache.
|
||||
|
||||
@param {array} name the data name
|
||||
@param {int} cloud determines cloud reading, which is otherwise set to automatic (0)
|
||||
@return {object} the data
|
||||
*/
|
||||
function find_data(DATA_ALL, DATA_PATH) {
|
||||
let DATA = DATA_ALL;
|
||||
static async read(name, cloud = 0) {
|
||||
/*
|
||||
Get all storage values.
|
||||
|
||||
// Pull the data out.
|
||||
if (DATA_ALL != null && (Array.isArray(DATA_PATH) && DATA_PATH != null) ? DATA_PATH.length > 0 : false) {
|
||||
let DATA_PATH_SELECTED = String(DATA_PATH.shift()).trim();
|
||||
|
||||
// Get the selected data.
|
||||
DATA = DATA_ALL[DATA_PATH_SELECTED];
|
||||
|
||||
// must run if there is actually a parameter to test
|
||||
if (DATA_PATH.length > 0) {
|
||||
// Recursively run to make use of the existing data.
|
||||
DATA = find_data(DATA, DATA_PATH);
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
@param {number} SOURCE the data source
|
||||
*/
|
||||
function pull(SOURCE = -1) {
|
||||
return (chrome.storage[(SOURCE > 0) ? `sync` : `local`].get(null));
|
||||
}
|
||||
|
||||
// Now return the data.
|
||||
return DATA;
|
||||
}
|
||||
/*
|
||||
Find a data given its path.
|
||||
|
||||
// Convert the entered prefname to an array if it is not one.
|
||||
if (!Array.isArray(DATA_NAME) && DATA_NAME != null) {
|
||||
// Syntax of splitting is by commas.
|
||||
DATA_NAME = String(DATA_NAME).trim().split(",");
|
||||
}
|
||||
@param {object} DATA_ALL the data
|
||||
@param {object} path the path of the data
|
||||
*/
|
||||
function find(DATA_ALL, path) {
|
||||
let DATA_PATH = path;
|
||||
let DATA = DATA_ALL;
|
||||
|
||||
switch (CLOUD) {
|
||||
case 0:
|
||||
DATA = {}; DATA_RETURNED = {};
|
||||
// Pull the data out.
|
||||
if (DATA_ALL != null && (Array.isArray(DATA_PATH) && DATA_PATH != null) ? DATA_PATH.length > 0 : false) {
|
||||
let DATA_PATH_SELECTED = String(DATA_PATH.shift()).trim();
|
||||
|
||||
DATA[`sync`] = await read((DATA_NAME) ? [...DATA_NAME] : null, 1);
|
||||
DATA[`local`] = await read((DATA_NAME) ? [...DATA_NAME] : null, -1);
|
||||
// Get the selected data.
|
||||
DATA = DATA_ALL[DATA_PATH_SELECTED];
|
||||
|
||||
// Now return the data.
|
||||
DATA_RETURNED[`source`] = (DATA[`sync`] != null) ? `sync` : `local`;
|
||||
DATA_RETURNED[`value`] = DATA[DATA_RETURNED[`source`]];
|
||||
|
||||
return DATA_RETURNED[`value`];
|
||||
break;
|
||||
default:
|
||||
CLOUD = (CLOUD > 0) ? 1 : -1;
|
||||
DATA = await read_database(CLOUD);
|
||||
DATA_RETURNED = (DATA_NAME) ? find_data(DATA, DATA_NAME) : DATA;
|
||||
|
||||
return(DATA_RETURNED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* More enhanced searching.
|
||||
|
||||
@param {Array} SOURCE the source of the data
|
||||
@param {string} TERM the term to search
|
||||
@param {Array} ADDITIONAL_PLACES additional places to search
|
||||
@param {object} OPTIONS the options
|
||||
@return {Array} the results
|
||||
*/
|
||||
export async function search(SOURCE, TERM, ADDITIONAL_PLACES, STRICT = 0, OPTIONS = {}) {
|
||||
let DATA = await read(SOURCE, (OPTIONS[`cloud`] != null) ? OPTIONS[`cloud`] : 0);
|
||||
let RESULTS;
|
||||
|
||||
if (DATA) {
|
||||
RESULTS = {};
|
||||
|
||||
if (TERM && (!(typeof ADDITIONAL_PLACES).includes(`str`) || !ADDITIONAL_PLACES)) {
|
||||
// Sequentially search through the data, first by key.
|
||||
(Object.keys(DATA)).forEach((DATA_NAME) => {
|
||||
if (STRICT ? DATA_NAME == TERM : (DATA_NAME.includes(TERM) || TERM.includes(DATA_NAME))) {
|
||||
RESULTS[DATA_NAME] = DATA[DATA_NAME];
|
||||
}
|
||||
});
|
||||
|
||||
// Then, get the additional places.
|
||||
if ((ADDITIONAL_PLACES != null ? Array.isArray(ADDITIONAL_PLACES) : false) ? ADDITIONAL_PLACES.length > 0 : false) {
|
||||
for (let PARAMETER_PRIORITY_NUMBER = 0; PARAMETER_PRIORITY_NUMBER < ADDITIONAL_PLACES.length; PARAMETER_PRIORITY_NUMBER++) {
|
||||
// Recursively search
|
||||
RESULTS = Object.assign({}, RESULTS, search(SOURCE, TERM, ADDITIONAL_PLACES[PARAMETER_PRIORITY_NUMBER], STRICT));
|
||||
};
|
||||
}
|
||||
} else if (((typeof ADDITIONAL_PLACES).includes(`str`) && (ADDITIONAL_PLACES)) ? ADDITIONAL_PLACES.trim() : false) {
|
||||
// Perform a sequential search on the data.
|
||||
if ((typeof DATA).includes(`obj`) && !Array.isArray(DATA) && SOURCE != null) {
|
||||
let VALUE = {};
|
||||
|
||||
for (let DICTIONARY_INDEX = 0; DICTIONARY_INDEX < (Object.keys(DATA)).length; DICTIONARY_INDEX++) {
|
||||
VALUE[`parent`] = DATA[(Object.keys(DATA))[DICTIONARY_INDEX]];
|
||||
|
||||
/* Test for a valid RegEx.
|
||||
|
||||
@param {string} item the item to test
|
||||
*/
|
||||
function isRegEx(item) {
|
||||
let RESULT = {};
|
||||
RESULT[`state`] = false;
|
||||
try {
|
||||
RESULT[`expression`] = new RegExp(item);
|
||||
RESULT[`state`] = true;
|
||||
} catch(err) {};
|
||||
|
||||
return (RESULT[`state`]);
|
||||
};
|
||||
|
||||
if (((typeof VALUE[`parent`]).includes(`obj`) && !Array.isArray(VALUE[`parent`]) && VALUE[`parent`] != null) ? (Object.keys(VALUE[`parent`])).length > 0 : false) {
|
||||
VALUE[`current`] = VALUE[`parent`][ADDITIONAL_PLACES];
|
||||
}
|
||||
|
||||
if (VALUE[`current`] ? ((STRICT >= 1) ? VALUE[`current`] == TERM : (((STRICT < 0.5) ? (VALUE[`current`].includes(TERM)) : false) || TERM.includes(VALUE[`current`]) || (isRegEx(VALUE[`current`]) ? (new RegExp(VALUE[`current`])).test(TERM) : false))) : false) {
|
||||
// Add the data.
|
||||
RESULTS[(Object.keys(DATA))[DICTIONARY_INDEX]] = (Object.entries(DATA))[DICTIONARY_INDEX][1];
|
||||
};
|
||||
// must run if there is actually a parameter to test
|
||||
if (DATA_PATH.length > 0) {
|
||||
// Recursively run to make use of the existing data.
|
||||
DATA = find(DATA, DATA_PATH);
|
||||
};
|
||||
} else {
|
||||
for (let ELEMENT_INDEX = 0; ELEMENT_INDEX < DATA.length; ELEMENT_INDEX++) {
|
||||
if (
|
||||
((STRICT || (typeof DATA[ELEMENT_INDEX]).includes(`num`)) && DATA[ELEMENT_INDEX] == TERM) ||
|
||||
((!STRICT && !((typeof DATA[ELEMENT_INDEX]).includes(`num`)))
|
||||
? (TERM.includes(DATA[ELEMENT_INDEX]) || DATA[ELEMENT_INDEX].includes(TERM) ||
|
||||
(typeof(DATA[ELEMENT_INDEX])).includes(`str`)
|
||||
? new RegExp(DATA[ELEMENT_INDEX]).test(TERM)
|
||||
: false
|
||||
) : false
|
||||
)
|
||||
) {
|
||||
RESULTS[SOURCE] = DATA;
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now return the data.
|
||||
return DATA;
|
||||
};
|
||||
|
||||
// Initialize the selected pref data.
|
||||
let DATA, DATA_RETURNED;
|
||||
|
||||
// Convert the entered prefname to an array if it is not one.
|
||||
let NAME = (!Array.isArray(name) && name != null)
|
||||
? String(name).trim().split(`,`)
|
||||
: name;
|
||||
|
||||
|
||||
switch (cloud) {
|
||||
case 0:
|
||||
DATA = {}; DATA_RETURNED = {};
|
||||
|
||||
DATA[`sync`] = await global.read((NAME) ? [...NAME] : null, 1);
|
||||
DATA[`local`] = await global.read((NAME) ? [...NAME] : null, -1);
|
||||
|
||||
// Now return the data.
|
||||
DATA_RETURNED[`source`] = (DATA[`sync`] != null) ? `sync` : `local`;
|
||||
DATA_RETURNED[`value`] = DATA[DATA_RETURNED[`source`]];
|
||||
|
||||
// Override the data with managed data if available.
|
||||
if ((NAME != null) ? NAME.length : false) {
|
||||
DATA[`managed`] = await managed.read((NAME) ? [...NAME] : null);
|
||||
DATA_RETURNED[`value`] = (DATA[`managed`] != null) ? DATA[`managed`] : DATA_RETURNED[`value`];
|
||||
};
|
||||
|
||||
return DATA_RETURNED[`value`];
|
||||
break;
|
||||
default:
|
||||
cloud = (cloud > 0) ? 1 : -1;
|
||||
DATA = await pull(cloud);
|
||||
DATA_RETURNED = (NAME) ? find(DATA, NAME) : DATA;
|
||||
|
||||
return(DATA_RETURNED);
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
/* More enhanced searching.
|
||||
|
||||
@param {Array} SOURCE the source of the data
|
||||
@param {string} TERM the term to search
|
||||
@param {Array} ADDITIONAL_PLACES additional places to search
|
||||
@param {object} OPTIONS the options
|
||||
@return {Array} the results
|
||||
*/
|
||||
static async search(SOURCE, TERM, ADDITIONAL_PLACES, STRICT = 0, OPTIONS = {}) {
|
||||
let DATA = await global.read(SOURCE, (OPTIONS[`cloud`] != null) ? OPTIONS[`cloud`] : 0);
|
||||
let RESULTS;
|
||||
|
||||
if (DATA) {
|
||||
RESULTS = {};
|
||||
|
||||
if (TERM && (!(typeof ADDITIONAL_PLACES).includes(`str`) || !ADDITIONAL_PLACES)) {
|
||||
// Sequentially search through the data, first by key.
|
||||
(Object.keys(DATA)).forEach((DATA_NAME) => {
|
||||
if (STRICT ? DATA_NAME == TERM : (DATA_NAME.includes(TERM) || TERM.includes(DATA_NAME))) {
|
||||
RESULTS[DATA_NAME] = DATA[DATA_NAME];
|
||||
}
|
||||
});
|
||||
|
||||
// Then, get the additional places.
|
||||
if ((ADDITIONAL_PLACES != null ? Array.isArray(ADDITIONAL_PLACES) : false) ? ADDITIONAL_PLACES.length > 0 : false) {
|
||||
ADDITIONAL_PLACES.forEach((ADDITIONAL_PLACE) => {
|
||||
// Recursively search
|
||||
RESULTS = Object.assign({}, RESULTS, global.search(SOURCE, TERM, ADDITIONAL_PLACE, STRICT));
|
||||
})
|
||||
}
|
||||
} else if (((typeof ADDITIONAL_PLACES).includes(`str`) && (ADDITIONAL_PLACES)) ? ADDITIONAL_PLACES.trim() : false) {
|
||||
// Perform a sequential search on the data.
|
||||
if ((typeof DATA).includes(`obj`) && !Array.isArray(DATA) && SOURCE != null) {
|
||||
let VALUE = {};
|
||||
|
||||
for (let DICTIONARY_INDEX = 0; DICTIONARY_INDEX < (Object.keys(DATA)).length; DICTIONARY_INDEX++) {
|
||||
VALUE[`parent`] = DATA[(Object.keys(DATA))[DICTIONARY_INDEX]];
|
||||
|
||||
/* Test for a valid RegEx.
|
||||
|
||||
@param {string} item the item to test
|
||||
*/
|
||||
function isRegEx(item) {
|
||||
let RESULT = {};
|
||||
RESULT[`state`] = false;
|
||||
try {
|
||||
RESULT[`expression`] = new RegExp(item);
|
||||
RESULT[`state`] = true;
|
||||
} catch(err) {};
|
||||
|
||||
return (RESULT[`state`]);
|
||||
};
|
||||
|
||||
if (((typeof VALUE[`parent`]).includes(`obj`) && !Array.isArray(VALUE[`parent`]) && VALUE[`parent`] != null) ? (Object.keys(VALUE[`parent`])).length > 0 : false) {
|
||||
VALUE[`current`] = VALUE[`parent`][ADDITIONAL_PLACES];
|
||||
}
|
||||
|
||||
if (VALUE[`current`] ? ((STRICT >= 1) ? VALUE[`current`] == TERM : (((STRICT < 0.5) ? (VALUE[`current`].includes(TERM)) : false) || TERM.includes(VALUE[`current`]) || (isRegEx(VALUE[`current`]) ? (new RegExp(VALUE[`current`])).test(TERM) : false))) : false) {
|
||||
// Add the data.
|
||||
RESULTS[(Object.keys(DATA))[DICTIONARY_INDEX]] = (Object.entries(DATA))[DICTIONARY_INDEX][1];
|
||||
};
|
||||
};
|
||||
} else {
|
||||
for (let ELEMENT_INDEX = 0; ELEMENT_INDEX < DATA.length; ELEMENT_INDEX++) {
|
||||
if (
|
||||
((STRICT || (typeof DATA[ELEMENT_INDEX]).includes(`num`)) && DATA[ELEMENT_INDEX] == TERM) ||
|
||||
((!STRICT && !((typeof DATA[ELEMENT_INDEX]).includes(`num`)))
|
||||
? (TERM.includes(DATA[ELEMENT_INDEX]) || DATA[ELEMENT_INDEX].includes(TERM) ||
|
||||
(typeof(DATA[ELEMENT_INDEX])).includes(`str`)
|
||||
? new RegExp(DATA[ELEMENT_INDEX]).test(TERM)
|
||||
: false
|
||||
) : false
|
||||
)
|
||||
) {
|
||||
RESULTS[SOURCE] = DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RESULTS;
|
||||
}
|
||||
|
||||
/* Write the data on the selected prefname.
|
||||
|
||||
@param {string} PATH the preference name
|
||||
@param {object} DATA the new data to be written
|
||||
@param {int} CLOUD store in the cloud; otherwise set to automatic
|
||||
@param {object} OPTIONS the options
|
||||
*/
|
||||
export async function write(PATH, DATA, CLOUD = -1, OPTIONS = {}) {
|
||||
let DATA_INJECTED = {};
|
||||
|
||||
// Inform the user that saving is in progress.
|
||||
if (((typeof OPTIONS).includes(`obj`) && OPTIONS != null) ? (!(!!OPTIONS[`silent`])) : true) {
|
||||
new logging ((new texts(`saving_current`)).localized, (new texts(`saving_current_message`)).localized, false)
|
||||
return RESULTS;
|
||||
};
|
||||
|
||||
/* Forcibly write the data to chrome database
|
||||
/* Write the data on the selected prefname.
|
||||
|
||||
@param {object} DATA the data
|
||||
@param {number} CLOUD the storage
|
||||
@param {string} path the preference name
|
||||
@param {object} data the new data to be written
|
||||
@param {int} CLOUD store in the cloud; otherwise set to automatic
|
||||
@param {object} OPTIONS the options
|
||||
*/
|
||||
const store = async (DATA, CLOUD = 0) => {
|
||||
// If CLOUD is set to 0, it should automatically determine where the previous source of data was taken from.
|
||||
return((CLOUD > 0) ? chrome.storage.sync.set(DATA) : chrome.storage.local.set(DATA));
|
||||
}
|
||||
static async write(path, data, CLOUD = -1, OPTIONS = {}) {
|
||||
let DATA_INJECTED = {};
|
||||
|
||||
/* Appropriately nest and merge the data.
|
||||
/* Appropriately nest and merge the data.
|
||||
|
||||
@param {object} EXISTING the original data
|
||||
@param {object} PATH the subpath
|
||||
@param {object} VALUE the value
|
||||
@return {object} the updated data
|
||||
*/
|
||||
function nest(EXISTING, SUBPATH, VALUE) {
|
||||
let DATABASE = EXISTING;
|
||||
@param {object} EXISTING the original data
|
||||
@param {object} PATH the subpath
|
||||
@param {object} VALUE the value
|
||||
@param {boolean} STRICT determines whether data is to be overridden or merged
|
||||
@return {object} the updated data
|
||||
*/
|
||||
function nest(existing, path, value, strict = false) {
|
||||
let DATABASE = existing, SUBPATH = path;
|
||||
|
||||
// Get the current path.
|
||||
let PATH = {};
|
||||
PATH[`current`] = (SUBPATH.length) ? String(SUBPATH.shift()).trim() : null;
|
||||
PATH[`target`] = SUBPATH;
|
||||
|
||||
// Get the current path.
|
||||
let PATH = {};
|
||||
PATH[`current`] = String(SUBPATH.shift()).trim();
|
||||
PATH[`target`] = SUBPATH;
|
||||
if (PATH[`target`].length > 0 && PATH[`current`] != undefined && PATH[`current`] != null) {
|
||||
DATABASE[PATH[`current`]] = (DATABASE[PATH[`current`]] == null)
|
||||
? {}
|
||||
: DATABASE[PATH[`current`]];
|
||||
DATABASE[PATH[`current`]] = nest(DATABASE[PATH[`current`]], PATH[`target`], value, strict);
|
||||
} else if (PATH[`current`] != undefined && PATH[`current`] != null) {
|
||||
// If not strict and the data selected is a dictionary, then merge them.
|
||||
DATABASE[PATH[`current`]] = (((DATABASE[PATH[`current`]]) ? ((typeof DATABASE[PATH[`current`]]).includes(`obj`) && !Array.isArray(DATABASE[PATH[`current`]])) : false) && !strict)
|
||||
? Object.assign(DATABASE[PATH[`current`]], value)
|
||||
: value;
|
||||
} else {
|
||||
DATABASE = (DATABASE == null || DATABASE == undefined) ? {} : DATABASE;
|
||||
|
||||
if (PATH[`target`].length > 0) {
|
||||
if (DATABASE[PATH[`current`]] == null) {
|
||||
DATABASE[PATH[`current`]] = {};
|
||||
DATABASE = (((typeof DATABASE).includes(`obj`) && !Array.isArray(DATABASE) && !strict) ? Object.assign(DATABASE, value) : value);
|
||||
}
|
||||
DATABASE[PATH[`current`]] = nest(DATABASE[PATH[`current`]], PATH[`target`], VALUE);
|
||||
} else {
|
||||
DATABASE[PATH[`current`]] = VALUE;
|
||||
|
||||
// Return the value.
|
||||
return DATABASE;
|
||||
}
|
||||
// Return the value.
|
||||
return DATABASE;
|
||||
|
||||
async function verify (NAME, DATA) {
|
||||
let DATA_CHECK = {};
|
||||
|
||||
// Verify the presence of the data.
|
||||
DATA_CHECK[`state`] = await compare(NAME, DATA);
|
||||
|
||||
(!DATA_CHECK[`state`])
|
||||
? logging.error((new texts(`error_msg_save_failed`)).localized, String(path), JSON.stringify(DATA))
|
||||
: ((((typeof OPTIONS).includes(`obj`) && OPTIONS != null) ? (!(!!OPTIONS[`silent`])) : true)
|
||||
? new logging (new texts(`saving_done`).localized)
|
||||
: false);
|
||||
|
||||
return (DATA_CHECK[`state`]);
|
||||
}
|
||||
|
||||
let DATA_ALL;
|
||||
|
||||
// Inform the user that saving is in progress.
|
||||
(((typeof OPTIONS).includes(`obj`) && OPTIONS != null) ? (!(!!OPTIONS[`silent`])) : true)
|
||||
? new logging ((new texts(`saving_current`)).localized, (new texts(`saving_current_message`)).localized, false)
|
||||
: false;
|
||||
|
||||
// Get all data and set a blank value if it doesn't exist yet.
|
||||
DATA_ALL = await global.read(null, CLOUD);
|
||||
DATA_ALL = ((DATA_ALL != null && DATA_ALL != undefined && (typeof DATA_ALL).includes(`obj`)) ? Object.keys(DATA_ALL).length <= 0 : true)
|
||||
? {}
|
||||
: DATA_ALL;
|
||||
|
||||
// Set the data name.
|
||||
let DATA_NAME = (!(Array.isArray(path)) && path && path != undefined)
|
||||
? String(path).trim().split(",")
|
||||
: ((path != null) ? path : []) // Ensure that path isn't empty.
|
||||
|
||||
// Merge!
|
||||
DATA_INJECTED = nest(DATA_ALL, (DATA_NAME != null) ? [...DATA_NAME] : DATA_NAME, data, (OPTIONS[`strict`] != null) ? OPTIONS[`strict`] : false);
|
||||
|
||||
// If cloud is not selected, get where the data is already existent.
|
||||
(CLOUD == 0 || CLOUD == null)
|
||||
? (CLOUD = (DATA_ALL[`local`] != null) ? -1 : 1)
|
||||
: false;
|
||||
|
||||
// Write!
|
||||
chrome.storage[(CLOUD > 0) ? `sync` : `local`].set(DATA_INJECTED);
|
||||
return (verify(DATA_NAME, data));
|
||||
}
|
||||
|
||||
async function verify (NAME, DATA) {
|
||||
let DATA_CHECK = {};
|
||||
/*
|
||||
Removes a particular data.
|
||||
|
||||
// Verify the presence of the data.
|
||||
DATA_CHECK[`state`] = await compare(NAME, DATA);
|
||||
@param {string} preference the preference name to delete
|
||||
@param {string} subpreference the subpreference name to delete
|
||||
@param {int} CLOUD the storage of the data
|
||||
@return {boolean} the user's confirmation
|
||||
*/
|
||||
static async forget(preference, cloud = 0, override = false) {
|
||||
// Confirm the action.
|
||||
let CONFIRMATION = override ? override : await logging.confirm();
|
||||
|
||||
(!DATA_CHECK[`state`])
|
||||
? logging.error((new texts(`error_msg_save_failed`)).localized, String(PATH), JSON.stringify(DATA))
|
||||
: ((((typeof OPTIONS).includes(`obj`) && OPTIONS != null) ? (!(!!OPTIONS[`silent`])) : true)
|
||||
? new logging (new texts(`saving_done`).localized)
|
||||
: false);
|
||||
|
||||
return (DATA_CHECK[`state`]);
|
||||
if (CONFIRMATION) {
|
||||
if (preference) {
|
||||
/*
|
||||
Erase applicable storage from a provider.
|
||||
|
||||
@param {string} name the name of the data
|
||||
@param {int} cloud the usage of cloud storage
|
||||
*/
|
||||
async function erase(path, cloud) {
|
||||
/*
|
||||
Securely erase by replacing any existing value with null.
|
||||
|
||||
@param {string} name the name of the data
|
||||
@param {int} cloud the usage of cloud storage
|
||||
*/
|
||||
function secure(name, cloud) {
|
||||
let PATH = name;
|
||||
// Check if the value already exists.
|
||||
return(global.read([...PATH], cloud).then(async (DATA) => {
|
||||
return((DATA != null)
|
||||
// Then erase the data.
|
||||
? await global.write(PATH, null, cloud, {"strict": true})
|
||||
: true);
|
||||
}));
|
||||
};
|
||||
|
||||
/*
|
||||
Remove the key from existence.
|
||||
|
||||
@param {string} name the name of the data
|
||||
@param {int} cloud the usage of cloud storage
|
||||
*/
|
||||
async function eliminate(name, cloud) {
|
||||
// Store the variable seperately to avoid overwriting.
|
||||
let PATH = name;
|
||||
|
||||
// There are two methods to erase the data.
|
||||
// The first only occurs when the root is selected and the path is just a direct descendant.
|
||||
if (PATH.length == 1) {
|
||||
chrome.storage[(cloud > 0) ? `sync` : `local`].remove(PATH[0]);
|
||||
} else {
|
||||
(global.read(((PATH.length > 1) ? [...PATH.slice(0,-1)] : null), cloud)).then((DATA) => {
|
||||
// Move the existing data into a new object to help in identifying.
|
||||
DATA = {"all": DATA};
|
||||
|
||||
if ((((typeof (DATA[`all`])).includes(`obj`) && !Array.isArray(DATA[`all`])) ? Object.keys(DATA[`all`]) : false) ? Object.hasOwn(DATA[`all`], PATH[PATH.length - 1]) : false) {
|
||||
DATA[`modified`] = DATA[`all`];
|
||||
|
||||
delete DATA[`modified`][PATH[PATH.length - 1]];
|
||||
|
||||
return(global.write(((PATH && Array.isArray(PATH)) ? (PATH.slice(0,-1)) : null), DATA[`modified`], cloud, {"strict": true}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// Set the data path.
|
||||
let DATA_NAME = (!(Array.isArray(path)) && path && path != undefined)
|
||||
? String(path).trim().split(",")
|
||||
: ((path != null) ? path : []) // Ensure that path isn't empty.
|
||||
|
||||
await secure([...DATA_NAME], cloud);
|
||||
eliminate([...DATA_NAME], cloud);
|
||||
}
|
||||
|
||||
(cloud >= 0) ? erase(preference, 1) : false;
|
||||
(cloud <= 0) ? erase(preference, -1) : false;
|
||||
} else {
|
||||
// Clear the data storage.
|
||||
(cloud >= 0) ? chrome.storage.sync.clear() : false;
|
||||
(cloud <= 0) ? chrome.storage.local.clear() : false;
|
||||
}
|
||||
}
|
||||
|
||||
return CONFIRMATION;
|
||||
}
|
||||
|
||||
let DATA_ALL = await read(null, CLOUD);
|
||||
if ((DATA_ALL != null && (typeof DATA_ALL).includes(`obj`)) ? Object.keys(DATA_ALL).length <= 0 : true) {
|
||||
DATA_ALL = {};
|
||||
};
|
||||
|
||||
let DATA_NAME = PATH;
|
||||
|
||||
// Convert the entered prefname to an array if it is not one.
|
||||
if (!(typeof SUBPATH).includes(`object`)) {
|
||||
// Split what is not an object.
|
||||
DATA_NAME = String(PATH).trim().split(",");
|
||||
}
|
||||
|
||||
// Merge!
|
||||
DATA_INJECTED = nest(DATA_ALL, [...DATA_NAME], DATA);
|
||||
|
||||
// Write!
|
||||
store(DATA_INJECTED, CLOUD);
|
||||
return (verify(DATA_NAME, DATA));
|
||||
}
|
||||
|
||||
class session {
|
||||
|
@ -363,10 +437,10 @@ class session {
|
|||
}
|
||||
|
||||
DATA = {"write": DATA};
|
||||
DATA[`all`] = await session.read(null, CLOUD);
|
||||
if ((DATA[`all`] != null && (typeof DATA[`all`]).includes(`obj`)) ? Object.keys(DATA[`all`]).length <= 0 : true) {
|
||||
DATA[`all`] = {};
|
||||
};
|
||||
DATA[`all`] = await session.read(null);
|
||||
((DATA[`all`] != null && (typeof DATA[`all`]).includes(`obj`)) ? Object.keys(DATA[`all`]).length <= 0 : true)
|
||||
? DATA[`all`] = {}
|
||||
: false;
|
||||
|
||||
let TARGET = (!(typeof PATH).includes(`obj`)) ? String(PATH).trim().split(",") : PATH;
|
||||
|
||||
|
@ -378,13 +452,21 @@ class session {
|
|||
}
|
||||
}
|
||||
|
||||
/* Compare a data against the stored data. Useful when comparing dictionaries.
|
||||
/*
|
||||
Compare a data against the stored data. Useful when comparing dictionaries.
|
||||
|
||||
@param {string} PATH the name
|
||||
@param {object} DATA the data to compare to
|
||||
@return {boolean} the result: true is when the data is the same, false otherwise
|
||||
*/
|
||||
export async function compare(PATH, DATA) {
|
||||
/* The actual comparison of data. */
|
||||
/*
|
||||
Compare the data.
|
||||
|
||||
@param {object} DATA_ONE the first data
|
||||
@param {object} DATA_TWO the second data
|
||||
@return {boolean} the result
|
||||
*/
|
||||
async function comparison(DATA_ONE, DATA_TWO) {
|
||||
let RESULT = true;
|
||||
|
||||
|
@ -397,129 +479,131 @@ export async function compare(PATH, DATA) {
|
|||
|
||||
let COMPARISON = {};
|
||||
COMPARISON[`test`] = (PATH) ? DATA : DATA[1];
|
||||
COMPARISON[`against`] = (PATH) ? (await read((Array.isArray(PATH)) ? [...PATH] : PATH)) : DATA[0];
|
||||
COMPARISON[`against`] = (PATH) ? (await global.read((Array.isArray(PATH)) ? [...PATH] : PATH)) : DATA[0];
|
||||
COMPARISON[`result`] = comparison(COMPARISON[`against`], COMPARISON[`test`]);
|
||||
|
||||
// Return the result.
|
||||
return (COMPARISON[`result`]);
|
||||
}
|
||||
|
||||
/* Dangerous: Resets all data or a domain's data.
|
||||
class template {
|
||||
/* Initialize the storage.
|
||||
|
||||
@param {dictionary} data this build's managed data
|
||||
*/
|
||||
static set(data) {
|
||||
let PREFERENCES = {};
|
||||
PREFERENCES[`all`] = {};
|
||||
|
||||
@param {string} preference the preference name to delete
|
||||
@param {string} subpreference the subpreference name to delete
|
||||
@param {int} CLOUD the storage of the data
|
||||
@return {boolean} the user's confirmation
|
||||
*/
|
||||
export async function forget(preference, CLOUD = 0, override = false) {
|
||||
// Confirm the action.
|
||||
let forget_action = override ? override : await logging.confirm();
|
||||
((typeof data).includes(`obj`) && data != null) ? PREFERENCES[`all`][`build`] = data : false;
|
||||
|
||||
if (forget_action) {
|
||||
if (preference) {
|
||||
let erase = async (CLOUD) => {
|
||||
if (!(Array.isArray(preference))) {
|
||||
preference = String(preference).trim().split(",");
|
||||
};
|
||||
// Read all data.
|
||||
[`managed`, `local`, `sync`].forEach((SOURCE) => {
|
||||
chrome.storage[SOURCE].get(null, (DATA) => {
|
||||
PREFERENCES[`all`][SOURCE] = DATA;
|
||||
})
|
||||
});
|
||||
|
||||
let DATA = await read((preference.length > 1) ? [...preference.slice(0,-1)] : null, CLOUD);
|
||||
// Merge the data.
|
||||
// Managed > Synchronized > Imported > Local
|
||||
managed.reinforce();
|
||||
|
||||
if (((((typeof (DATA)).includes(`obj`) && !Array.isArray(DATA) && DATA != null) ? Object.keys(DATA) : false) ? Object.keys(DATA).includes((preference.slice(-1))[0]) : false)) {
|
||||
delete DATA[preference.slice(-1)];
|
||||
};
|
||||
// Set the managed preferences.
|
||||
if ((PREFERENCES[`all`][`managed`] && (typeof PREFERENCES[`all`][`managed`]).includes(`obj`) && !Array.isArray(PREFERENCES[`all`][`managed`])) ? Object.keys(PREFERENCES[`all`][`managed`]).length > 0 : false) {
|
||||
Object.keys(PREFERENCES[`all`][`managed`]).forEach((item) => {
|
||||
let PREFERENCE = {};
|
||||
PREFERENCE[`name`] = item;
|
||||
|
||||
await write(preference.slice(0,-1), DATA, CLOUD);
|
||||
};
|
||||
// Get if the data already exists.
|
||||
PREFERENCE[`existing`] = (PREFERENCES[`all`][`sync`] && (typeof PREFERENCES[`all`][`sync`]).includes(`obj`))
|
||||
? PREFERENCES[`all`][`sync`].hasOwnProperty(PREFERENCE[`name`])
|
||||
: false;
|
||||
|
||||
if (CLOUD >= 0) {
|
||||
erase(1);
|
||||
};
|
||||
if (CLOUD <= 0) {
|
||||
erase(-1);
|
||||
};
|
||||
} else {
|
||||
// Clear the data storage.
|
||||
if (CLOUD >= 0) {
|
||||
chrome.storage.sync.clear();
|
||||
}
|
||||
if (CLOUD <= 0) {
|
||||
chrome.storage.local.clear();
|
||||
}
|
||||
if (!PREFERENCE[`existing`]) {
|
||||
// Do not allow synchronized data to interfere with managed data.
|
||||
global.forget(PREFERENCE[`name`], 0, true);
|
||||
global.write(PREFERENCE[`name`], PREFERENCES_ALL[`managed`][PREFERENCE[`name`]]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Import build data
|
||||
if (PREFERENCES[`all`][`build`]) {
|
||||
Object.keys(PREFERENCES[`all`][`build`]).forEach((item) => {
|
||||
let PREFERENCE = { name: item, existing: false };
|
||||
|
||||
PREFERENCE[`existing`] =
|
||||
(PREFERENCES[`all`][`sync`]
|
||||
? PREFERENCES[`all`][`sync`].hasOwnProperty(PREFERENCE[`name`])
|
||||
: false) ||
|
||||
(PREFERENCES[`all`][`managed`]
|
||||
? PREFERENCES[`all`][`managed`].hasOwnProperty(PREFERENCE[`name`])
|
||||
: false) ||
|
||||
(PREFERENCES[`all`][`local`]
|
||||
? PREFERENCES[`all`][`local`].hasOwnProperty(PREFERENCE[`local`])
|
||||
: false);
|
||||
|
||||
(!PREFERENCE[`existing`])
|
||||
? global.write(PREFERENCE[`name`], PREFERENCES[`all`][`build`][PREFERENCE[`name`]], -1)
|
||||
: false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return forget_action;
|
||||
};
|
||||
}
|
||||
|
||||
/* Initialize the storage.
|
||||
|
||||
@param {dictionary} data this build's managed data
|
||||
/*
|
||||
managed data functions
|
||||
*/
|
||||
export function init(data) {
|
||||
let PREFERENCES_ALL = {};
|
||||
PREFERENCES_ALL[`build`] = data;
|
||||
|
||||
// Read all data.
|
||||
chrome.storage.managed.get(null, function (DATA_MANAGED) {
|
||||
PREFERENCES_ALL[`managed`] = DATA_MANAGED;
|
||||
});
|
||||
|
||||
chrome.storage.local.get(null, function (DATA_LOCAL) {
|
||||
PREFERENCES_ALL[`local`] = DATA_LOCAL;
|
||||
});
|
||||
|
||||
chrome.storage.sync.get(null, function (DATA_SYNC) {
|
||||
PREFERENCES_ALL[`sync`] = DATA_SYNC;
|
||||
});
|
||||
|
||||
// Merge data.
|
||||
// Managed > Synchronized > Imported > Local
|
||||
|
||||
if (PREFERENCES_ALL[`managed`]) {
|
||||
Object.keys(PREFERENCES_ALL[`managed`]).forEach((item) => {
|
||||
let PREFERENCE = { name: item, existing: false };
|
||||
|
||||
if (PREFERENCES_ALL[`sync`]) {
|
||||
PREFERENCE[`existing`] = PREFERENCES_ALL[`sync`].hasOwnProperty(
|
||||
PREFERENCE[`name`],
|
||||
);
|
||||
}
|
||||
|
||||
if (!PREFERENCE[`existing`]) {
|
||||
// Do not allow synchronized data to interfere with managed data.
|
||||
forget(PREFERENCE[`name`]);
|
||||
write(
|
||||
PREFERENCE[`name`],
|
||||
PREFERENCES_ALL[`managed`][PREFERENCE[`name`]],
|
||||
);
|
||||
}
|
||||
class managed {
|
||||
/*
|
||||
Reinforce managed data.
|
||||
*/
|
||||
static reinforce() {
|
||||
chrome.storage.managed.get(null, (DATA_MANAGED) => {
|
||||
// Saving the data asynchronously
|
||||
(Object.keys(DATA_MANAGED)).forEach(async (SOURCE) => {
|
||||
await write(SOURCE, DATA_MANAGED[SOURCE], -1, {"strict": false});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Import build data
|
||||
if (PREFERENCES_ALL[`build`]) {
|
||||
Object.keys(PREFERENCES_ALL[`build`]).forEach((item) => {
|
||||
let PREFERENCE = { name: item, existing: false };
|
||||
/*
|
||||
Read for any applicable managed data.
|
||||
|
||||
PREFERENCE[`existing`] =
|
||||
(PREFERENCES_ALL[`sync`]
|
||||
? PREFERENCES_ALL[`sync`].hasOwnProperty(PREFERENCE[`name`])
|
||||
: false) ||
|
||||
(PREFERENCES_ALL[`managed`]
|
||||
? PREFERENCES_ALL[`managed`].hasOwnProperty(PREFERENCE[`name`])
|
||||
: false) ||
|
||||
(PREFERENCES_ALL[`local`]
|
||||
? PREFERENCES_ALL[`local`].hasOwnProperty(PREFERENCE[`local`])
|
||||
: false);
|
||||
@param {string} name the name of the data
|
||||
@return {boolean} the result
|
||||
*/
|
||||
static async read(name) {
|
||||
function find(DATA_ALL, DATA_PATH) {
|
||||
let DATA = DATA_ALL;
|
||||
|
||||
if (!PREFERENCE[`existing`]) {
|
||||
write(
|
||||
PREFERENCE[`name`],
|
||||
PREFERENCES_ALL[`build`][PREFERENCE[`name`]],
|
||||
-1,
|
||||
);
|
||||
// Pull the data out.
|
||||
if (DATA_ALL != null && (Array.isArray(DATA_PATH) && DATA_PATH != null) ? DATA_PATH.length > 0 : false) {
|
||||
let DATA_PATH_SELECTED = String(DATA_PATH.shift()).trim();
|
||||
|
||||
// Get the selected data.
|
||||
DATA = DATA_ALL[DATA_PATH_SELECTED];
|
||||
|
||||
// must run if there is actually a parameter to test
|
||||
if (DATA_PATH.length > 0) {
|
||||
// Recursively run to make use of the existing data.
|
||||
DATA = find(DATA, DATA_PATH);
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Now return the data.
|
||||
return DATA;
|
||||
}
|
||||
|
||||
let DATA = {};
|
||||
DATA[`all`] = await chrome.storage.managed.get(null);
|
||||
DATA[`selected`] = ((DATA[`all`] && (typeof DATA[`all`]).includes(`obj`) && !Array.isArray(DATA[`all`])) ? Object.keys(DATA[`all`]).length : false)
|
||||
? find(DATA[`all`], name)
|
||||
: null;
|
||||
|
||||
return (DATA[`selected`]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,4 +618,4 @@ export function observe(reaction) {
|
|||
});
|
||||
}
|
||||
|
||||
export {session}
|
||||
export {global, session, template, managed};
|
||||
|
|
5
scripts/utils/browserside.js
Normal file
5
scripts/utils/browserside.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
/*
|
||||
browserside.js
|
||||
|
||||
Easily switch between Chrome and Firefox APIs.
|
||||
*/
|
|
@ -81,7 +81,7 @@ nav ul:not(.dropdown-content) > li > a {
|
|||
|
||||
iframe.viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: fit-content;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
#score {
|
||||
background-color: #88888888;
|
||||
height: 2px;
|
||||
|
@ -11,8 +10,4 @@
|
|||
|
||||
#score::-webkit-progress-value {
|
||||
background-color: #ffF;
|
||||
}
|
||||
|
||||
body[role="window"] {
|
||||
width: 250pt;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue