ConfigManager

ConfigManager: Manage(Get, set and update) your config with config path simply with a ruleset!

Versione datata 26/08/2022. Vedi la nuova versione l'ultima versione.

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greasyfork.org/scripts/449583/1085898/ConfigManager.js

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

/* eslint-disable no-multi-spaces */

// ==UserScript==
// @name               ConfigManager
// @namespace          ConfigManager
// @version            0.5.1
// @description        ConfigManager: Manage(Get, set and update) your config with config path simply with a ruleset!
// @author             PY-DNG
// @license            GPL-v3
// @grant              GM_setValue
// @grant              GM_getValue
// @grant              GM_listValues
// @grant              GM_deleteValue
// ==/UserScript==

function ConfigManager(Ruleset, storage={}) {
	const CM = this;
	const _GM_setValue = storage.GM_setValue || GM_setValue || Err('ConfigManager: could not find GM_setValue');
	const _GM_getValue = storage.GM_getValue || GM_getValue || Err('ConfigManager: could not find GM_getValue');
	const _GM_listValues = storage.GM_listValues || GM_listValues || Err('ConfigManager: could not find GM_listValues');
	const _GM_deleteValue = storage.GM_deleteValue || GM_deleteValue || Err('ConfigManager: could not find GM_deleteValue');
	const ConfigBase = new Proxy({}, {
		get: function(target, property, reciever) {
			return _GM_getValue(property);
		},
		set: function(target, property, value, reciever) {
			return (_GM_setValue(property, value), true);
		},
		has: function(target, property) {
			return _GM_listValues().includes(property);
		}
	});

	CM.getConfig = getConfig;
	CM.setConfig = setConfig;
	CM.updateConfig = updateConfig;
	CM.updateAllConfigs = updateAllConfigs;
	CM.setDefaults = setDefaults;
	CM.readPath = readPath;
	CM.pathExists = pathExists;
	CM.mergePath = mergePath;
	CM.getBaseName = getBaseName;
	CM.Config = new Proxy({}, {
		get: function(target, property, reciever) {
			return makeProxy(getConfig(property), [property]);

			function makeProxy(config, path) {
				return isObject(config) ? new Proxy(config, {
					get: function(target, property, reciever) {
						path.push(property);
						return makeProxy(target[property], path);
					},
					set: function(target, property, value, reciever) {
						path.push(property);
						return (setConfig(path, value), true);
					}
				}) : config;
			}
		},
		set: function(target, property, value, reciever) {
			return (_GM_setValue(property, value), true);
		},
		has: function(target, property) {
			return _GM_listValues().includes(property);
		}
	});
	Object.freeze(CM);

	// Get config value from path (e.g. 'Users/username/' or ['Users', 12345])
	function getConfig(path) {
		// Split path
		path = arrPath(path);

		// Init config if need
		if (!_GM_listValues().includes(path[0])) {
			ConfigBase[path[0]] = Ruleset.defaultValues[path[0]];
		}

		// Get config by path
		const target = path.pop();
		let config = readPath(ConfigBase, path);
		return config[target];
	}

	// Set config value to path
	function setConfig(path, value) {
		path = arrPath(path);
		const target = path.pop();

		if (path.length > 0) {
			const basekey = path.shift();
			const baseobj = ConfigBase[basekey];
			let config = readPath(baseobj, path);
			if (isObject(config)) {
				config[target] = value;
				ConfigBase[basekey] = baseobj;
			} else {
				Err('Attempt to set a property to a non-object value')
			}
		} else {
			ConfigBase[target] = value;
		}
	}

	function updateConfig(basename) {
		let updated = false;

		// Get updaters and config
		const updaters = Ruleset.updaters.hasOwnProperty(basename) ? Ruleset.updaters[basename] : [];
		const verKey = Ruleset['version-key'];
		const config = getConfig(basename);

		// Valid check
		if (Ruleset.ignores.includes(basename)) {
			return false;
		}
		if (!updaters.length) {
			return save();
		}

		// Update
		for (let i = (config[verKey] || 0); i < updaters.length; i++) {
			const updater = updaters[i];
			config = updater(config);
			updated = true;
		}

		// Set version and save
		return save();

		function save() {
			config[verKey] = updaters.length;
			setConfig(basename, config);
			return updated;
		}
	}

	function updateAllConfigs() {
		const keys = _GM_listValues();
		keys.forEach((key) => (updateConfig(key)));
	}

	function setDefaults() {
		for (const [key, val] of Object.entries(Ruleset.defaultValues)) {
			!key in ConfigBase && (ConfigBase[key] = val);
		}
	}

	function readPath(obj, path) {
		path = arrPath(path);
		while (path.length > 0) {
			const key = path.shift();
			if (isObject(obj) && hasProp(obj, key)) {
				obj = obj[key]
			} else {
				Err('Attempt to read a property that is not exist (reading "' + key + '" in path "' + path + '")')
			}
		}
		return obj;
	}

	function pathExists(obj, path) {
		path = arrPath(path);
		while (path.length > 0) {
			const key = path.shift();
			if (isObject(obj) && hasProp(obj, key)) {
				obj = obj[key];
			} else {
				return false;
			}
		}
		return true;
	}

	function mergePath() {
		return Array.from(arguments).join('/');
	}

	function getBaseName(path) {
		return arrPath(path)[0];
	}

	function getPathWithoutBase(path) {
		const p = arrPath(path);
		p.shift();
		return p;
	}

	function arrPath(strpath) {
		return Array.isArray(strpath) ? [...strpath] : strpath.split('/');
	}

	function isObject(obj) {
		return typeof obj === 'object' && obj !== null;
	}

	function hasProp(obj, prop) {
		return obj === ConfigBase ? prop in obj : obj.hasOwnProperty(prop);
	}

	// type: [Error, TypeError]
	function Err(msg, type=0) {
		throw new [Error, TypeError][type](msg);
	}
}