const MszWatcher = function() { let watchers = []; return { watch: function(watcher, thisArg, args) { if(typeof watcher !== 'function') throw 'watcher must be a function'; if(watchers.indexOf(watcher) >= 0) return; watchers.push(watcher); if(thisArg !== undefined) { if(!Array.isArray(args)) { if(args !== undefined) args = [args]; else args = []; } // initial call args.push(true); watcher.apply(thisArg, args); } }, unwatch: function(watcher) { $ari(watchers, watcher); }, call: function(thisArg, args) { if(!Array.isArray(args)) { if(args !== undefined) args = [args]; else args = []; } args.push(false); for(const watcher of watchers) watcher.apply(thisArg, args); }, }; }; const MszWatcherCollection = function() { const collection = new Map; const watch = function(name, watcher, thisArg, args) { const watchers = collection.get(name); if(watchers === undefined) throw 'undefined watcher name'; watchers.watch(watcher, thisArg, args); }; const unwatch = function(name, watcher) { const watchers = collection.get(name); if(watchers === undefined) throw 'undefined watcher name'; watchers.unwatch(watcher); }; return { define: function(names) { if(!Array.isArray(names)) names = [names]; for(const name of names) collection.set(name, new MszWatcher); }, call: function(name, thisArg, args) { const watchers = collection.get(name); if(watchers === undefined) throw 'undefined watcher name'; watchers.call(thisArg, args); }, watch: watch, unwatch: unwatch, proxy: function(obj) { obj.watch = function(name, watcher) { watch(name, watcher); }; obj.unwatch = unwatch; }, }; };