var SS = { // slots are stored as: // signalid: { // slotid: [ connection1, connection2, ... ] // } _slots: {} // from 140byt.es: https://gist.github.com/982883 ,_uuid: function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)} ,_makeSignal: function(sigCtx, signal, slotCtx, slot){ var oSignal = sigCtx[signal] ,signalid = SS._uuid() ,slotid = slotCtx[slot].ssid // replace the original func with a wrapper that will trigger slots var signalWrap = function(){ var slots; console.log('told to fire signal: ', arguments); oSignal.apply(sigCtx, arguments); if(SS._slots[signalid] && SS._slots[signalid][slotid]){ slots = SS._slots[signalid][slotid]; for(i = 0; i < slots.length; i++){ console.log(slots[i]); slots[i].execute.apply(slots[i], arguments); } } } sigCtx[signal] = signalWrap; sigCtx[signal].ssid = signalid; } ,_connect: function(sigCtx, signal, slotCtx, slot, once){ var signalid ,slotid ,con = new SS.Connection(sigCtx, signal, slotCtx, slot, once); if(!slotCtx[slot].ssid){ slotCtx[slot].ssid = SS._uuid(); } signalid = sigCtx[signal].ssid; slotid = slotCtx[slot].ssid; // no signalid means this is a fresh func if(!signalid){ SS._makeSignal(sigCtx, signal, slotCtx, slot); signalid = sigCtx[signal].ssid; } if(!SS._slots[signalid]) { SS._slots[signalid] = {}; } if(!SS._slots[signalid][slotid]) { SS._slots[signalid][slotid] = []; } SS._slots[signalid][slotid].push( con ); } ,connect: function(sigCtx, signal, slotCtx, slot){ SS._connect(sigCtx, signal, slotCtx, slot, false); } ,connectOnce: function(sigCtx, signal, slotCtx, slot){ SS._connect(sigCtx, signal, slotCtx, slot, true); } // this removes ALL listeners matching signal/slot pair ,disconnect: function(sigCtx, signal, slotCtx, slot){ var signalid = sigCtx[signal].ssid ,slotid = slotCtx[slot].ssid ,i ,slots ,positions = [] if(!slotid){ throw new Error('Method is not a connected slot'); } delete SS._slots[signalid][slotid]; console.log('slots after disconnect', SS._slots[signalid]); } // empty a signal of all connections ,emptySignal: function(sigCtx, signal){ var signalid = sigCtx[signal].ssid; delete SS._slots[signalid]; } }; SS.Connection = function(sigCtx, signal, slotCtx, slot, once){ this.sigCtx = sigCtx; this.signal = signal; this.slotCtx = slotCtx; this.slot = slot; this.once = once; if(typeof slotCtx[slot] !== 'function'){ throw new Error('Slot must be a function'); } if(typeof sigCtx[signal] !== 'function'){ throw new Error('Signal must be a function'); } } SS.Connection.prototype = { execute: function(){ this.slotCtx[this.slot].apply(this.slotCtx, arguments); if(this.once === true){ SS.disconnect(this.sigCtx, this.signal, this.slotCtx, this.slot); } } }; (function(exports){ function MyObj(){} MyObj.prototype = { // a slot refresh: function(data){ console.log('told to refresh', arguments); } // a signal ,ready: function(){ console.log('ready called', arguments); } ,doSomethingAsync: function(){ var self = this; setTimeout(function(){ self.ready('behold! i am data!', 'so am i'); SS.disconnect(my, 'ready', myToo, 'refresh'); self.ready('i should not trigger handlers'); SS.connectOnce(my, 'ready', myToo, 'refresh'); self.ready('i trigger once'); self.ready('i trigger none'); }, 1000); } }; window.MyObj = MyObj; })(window); var my = new MyObj(); var myToo = new MyObj(); my.doSomethingAsync(); SS.connect(my, 'ready', myToo, 'refresh'); SS.connect(my, 'ready', myToo, 'refresh'); console.log('end of sync...');