/** * This file is part of Haketilo. * * Function: Storage manager, lighter than the previous one. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. */ /* * IMPORTS_START * IMPORT TYPE_PREFIX * IMPORT raw_storage * IMPORT is_mozilla * IMPORT observables */ 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 */