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