/** * This file is part of Haketilo. * * Function: Storage manager, lighter than the previous one. * * Copyright (C) 2021 Wojtek Kosior * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * As additional permission under GNU GPL version 3 section 7, you * may distribute forms of that code without the copy of the GNU * GPL normally required by section 4, provided you include this * license notice and, in case of non-source distribution, a URL * through which recipients can access the Corresponding Source. * If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * As a special exception to the GPL, any HTML file which merely * makes function calls to this code, and for that purpose * includes it by reference shall be deemed a separate work for * copyright law purposes. If you modify this code, you may extend * this exception to your version of the code, but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * I, Wojtek Kosior, thereby promise not to sue for violation of this file's * license. Although I request that you do not make use this code in a * proprietary program, I am not going to enforce this in court. */ /* * IMPORTS_START * IMPORT TYPE_PREFIX * IMPORT raw_storage * IMPORT is_mozilla * IMPORT observables * IMPORTS_END */ const reg_spec = new Set(["\\", "[", "]", "(", ")", "{", "}", ".", "*", "+"]); const escape_reg_special = c => reg_spec.has(c) ? "\\" + c : c; function make_regex(name) { return new RegExp(`^${name.split("").map(escape_reg_special).join("")}\$`); } const listeners_by_callback = new Map(); function listen(callback, prefix, name) { let by_prefix = listeners_by_callback.get(callback); if (!by_prefix) { by_prefix = new Map(); listeners_by_callback.set(callback, by_prefix); } let by_name = by_prefix.get(prefix); if (!by_name) { by_name = new Map(); by_prefix.set(prefix, by_name); } let name_reg = by_name.get(name); if (!name_reg) { name_reg = name.test ? name : make_regex(name); by_name.set(name, name_reg); } } function no_listen(callback, prefix, name) { const by_prefix = listeners_by_callback.get(callback); if (!by_prefix) return; const by_name = by_prefix.get(prefix); if (!by_name) return; const name_reg = by_name.get(name); if (!name_reg) return; by_name.delete(name); if (by_name.size === 0) by_prefix.delete(prefix); if (by_prefix.size === 0) listeners_by_callback.delete(callback); } function storage_change_callback(changes, area) { if (is_mozilla && area !== "local") {console.log("area", area);return;} for (const item of Object.keys(changes)) { for (const [callback, by_prefix] of listeners_by_callback.entries()) { const by_name = by_prefix.get(item[0]); if (!by_name) continue; for (const reg of by_name.values()) { if (!reg.test(item.substring(1))) continue; try { callback(item, changes[item]); } catch(e) { console.error(e); } } } } } raw_storage.listen(storage_change_callback); const created_observables = new Map(); async function observe(prefix, name) { const observable = observables.make(); const callback = (it, ch) => observables.set(observable, ch.newValue); listen(callback, prefix, name); created_observables.set(observable, [callback, prefix, name]); observables.silent_set(observable, await raw_storage.get(prefix + name)); return observable; } const observe_var = name => observe(TYPE_PREFIX.VAR, name); function no_observe(observable) { no_listen(...created_observables.get(observable) || []); created_observables.delete(observable); } const light_storage = {}; Object.assign(light_storage, raw_storage); Object.assign(light_storage, {listen, no_listen, observe, observe_var, no_observe}); /* * EXPORTS_START * EXPORT light_storage * EXPORTS_END */