/** * Событие изменения конфигурации. */ class ConfigChangeEvent extends Event { constructor(type, name, value) { super(type); this.name = name; this.value = value; } } /** * Конфигурация. * * При любом изменении конфигурации вызывается событие {@link ConfigChangeEvent} * с типом changed, а так же changed.путь. */ class Config extends EventEmitter { #data; constructor() { super(); this.#data = {}; } /** * Получить значение из конфигурации. * * @example config.get('group', 'subgroup', 'name') */ get(...name) { const lastName = name.pop(); let obj = this.#data; for (const i of name) { obj = (i in obj) ? obj[i] : {}; if (typeof obj !== 'object' || !obj) return; } return obj[lastName]; } /** * Записать значение в конфигурацию. Последний аргумент - значение. * * @example config.set('group', 'subgroup', 'name', value) */ set(...name) { const value = name.pop(); const lastName = name.pop(); let obj = this.#data; for (const i of name) { if (!(i in obj) || obj[i] === null) obj[i] = {} obj = obj[i]; if (typeof obj !== 'object') return; } obj[lastName] = value; name.push(lastName); let path = 'changed'; this.dispatchEvent(new ConfigChangeEvent(path, name, value)); for (const i of name) { path += '.' + i; this.dispatchEvent(new ConfigChangeEvent(path, name, value)); } } /** * Удалить значение из конфигурации */ delete(...name) { const lastName = name.pop(); let obj = this.#data; for (const i of name) { if (!(i in obj) || obj[i] === null) obj[i] = {} obj = obj[i]; if (typeof obj !== 'object') return; } delete obj[lastName]; let path = 'changed'; this.dispatchEvent(new ConfigChangeEvent(path, name, undefined)); for (const i of name) { path += '.' + i; this.dispatchEvent(new ConfigChangeEvent(path, name, undefined)); } } /** * Зкспорт конфигурации в json строку. * @returns {string} */ toJson() { return JSON.stringify(this.#data); } /** * Импорт конфигурации из json строки * @param {string} data */ fromJson(data) { this.#data = JSON.parse(data); this.#dispatchRecurse(this.#data, 'changed'); } #dispatchRecurse(obj, path) { this.dispatchEvent(new ConfigChangeEvent(path, [], this.#data)); if (typeof obj !== 'object' || !obj) return; for (const i in obj) if (obj.hasOwnProperty(i)) { this.#dispatchRecurse(obj[i], path + '.' + i); } } /** * Создает прокси-объект для указанной группы. * * @example const proxy = config.createProxy('group', 'subgroup'); * proxy.name = value; // то же, что и config.set('group', 'subgroup', 'name', value); */ createProxy(...names) { const self = this; return new Proxy(Object.create(null), { get(t, name, r) { return self.get(...names, name); }, set(t, name, value, r) { self.set(...names, name, value); return true; } }); } /** * Создает прокси для настроек соединения. * @see {@link Config.createProxy} * @param {string} connName Имя соединения */ getConnection(connName) { return this.createProxy('connections', connName); } /** * Создает прокси для настроек устройства. * @see {@link Config.createProxy} * @param {string} devId ID устройства */ getDevice(devId) { return this.createProxy('devices', devId); } /** * Глобальные настройки хаба. * @see {@link Config.createProxy} */ get global() { return this.createProxy('hub'); } }