Skip to content

Instantly share code, notes, and snippets.

@millermedeiros
Created March 10, 2011 01:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save millermedeiros/863421 to your computer and use it in GitHub Desktop.
Save millermedeiros/863421 to your computer and use it in GitHub Desktop.
JS-Signals 0.5.3
/*jslint onevar:true, undef:true, newcap:true, regexp:true, bitwise:true, maxerr:50, indent:4, white:false, nomen:false, plusplus:false */
/*!!
* JS Signals <http://millermedeiros.github.com/js-signals/>
* Released under the MIT license <http://www.opensource.org/licenses/mit-license.php>
* @author Miller Medeiros <http://millermedeiros.com/>
* @version 0.5.3
* @build 143 (02/21/2011 07:18 PM)
*/
var signals = (function(){
var signals = {
VERSION : '0.5.3'
};
// SignalBinding -------------------------------------------------
//================================================================
function SignalBinding(signal, listener, isOnce, listenerContext, priority){
this._listener = listener;
this._isOnce = isOnce;
this.context = listenerContext;
this._signal = signal;
this._priority = priority || 0;
}
SignalBinding.prototype = {
_isEnabled : true,
execute : function(paramsArr){
var r;
if(this._isEnabled){
r = this._listener.apply(this.context, paramsArr);
if(this._isOnce){
this.detach();
}
}
return r;
},
detach : function(){
return this._signal.remove(this._listener);
},
getListener : function(){
return this._listener;
},
dispose : function(){
this.detach();
this._destroy();
},
_destroy : function(){
delete this._signal;
delete this._isOnce;
delete this._listener;
delete this.context;
},
disable : function(){
this._isEnabled = false;
},
enable : function(){
this._isEnabled = true;
},
isEnabled : function(){
return this._isEnabled;
},
isOnce : function(){
return this._isOnce;
},
toString : function(){
return '[SignalBinding isOnce: '+ this._isOnce +', isEnabled: '+ this._isEnabled +']';
}
};
// Signal --------------------------------------------------------
//================================================================
signals.Signal = function(){
this._bindings = [];
};
signals.Signal.prototype = {
_shouldPropagate : true,
_isEnabled : true,
_registerListener : function(listener, isOnce, scope, priority){
if(typeof listener !== 'function'){
throw new Error('listener is a required param of add() and addOnce() and should be a Function.');
}
var prevIndex = this._indexOfListener(listener),
binding;
if(prevIndex !== -1){ //avoid creating a new Binding for same listener if already added to list
binding = this._bindings[prevIndex];
if(binding.isOnce() !== isOnce){
throw new Error('You cannot add'+ (isOnce? '' : 'Once') +'() then add'+ (!isOnce? '' : 'Once') +'() the same listener without removing the relationship first.');
}
} else {
binding = new SignalBinding(this, listener, isOnce, scope, priority);
this._addBinding(binding);
}
return binding;
},
_addBinding : function(binding){
//simplified insertion sort
var n = this._bindings.length;
do { --n; } while (this._bindings[n] && binding._priority <= this._bindings[n]._priority);
this._bindings.splice(n+1, 0, binding);
},
_indexOfListener : function(listener){
var n = this._bindings.length;
while(n--){
if(this._bindings[n]._listener === listener){
return n;
}
}
return -1;
},
add : function(listener, scope, priority){
return this._registerListener(listener, false, scope, priority);
},
addOnce : function(listener, scope, priority){
return this._registerListener(listener, true, scope, priority);
},
remove : function(listener){
if(typeof listener !== 'function'){
throw new Error('listener is a required param of remove() and should be a Function.');
}
var i = this._indexOfListener(listener);
if(i !== -1){
this._bindings[i]._destroy(); //no reason to a SignalBinding exist if it isn't attached to a signal
this._bindings.splice(i, 1);
}
return listener;
},
removeAll : function(){
var n = this._bindings.length;
while(n--){
this._bindings[n]._destroy();
}
this._bindings.length = 0;
},
getNumListeners : function(){
return this._bindings.length;
},
disable : function(){
this._isEnabled = false;
},
enable : function(){
this._isEnabled = true;
},
isEnabled : function(){
return this._isEnabled;
},
halt : function(){
this._shouldPropagate = false;
},
dispatch : function(params){
if(! this._isEnabled){
return;
}
var paramsArr = Array.prototype.slice.call(arguments),
bindings = this._bindings.slice(), //clone array in case add/remove items during dispatch
n = this._bindings.length;
this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch.
//execute all callbacks until end of the list or until a callback returns `false` or stops propagation
//reverse loop since listeners with higher priority will be added at the end of the list
do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false);
},
dispose : function(){
this.removeAll();
delete this._bindings;
},
toString : function(){
return '[Signal isEnabled: '+ this._isEnabled +' numListeners: '+ this.getNumListeners() +']';
}
};
return signals;
}());
@millermedeiros
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment