Skip to content

Instantly share code, notes, and snippets.

@xphere
Created December 16, 2012 19:56
Show Gist options
  • Select an option

  • Save xphere/4312163 to your computer and use it in GitHub Desktop.

Select an option

Save xphere/4312163 to your computer and use it in GitHub Desktop.
// Based on idea of http://millermedeiros.github.com/js-signals/
function Signal(context, remember) {
this._bindings = [];
this._sorted = true;
this._erased = false;
this._remember = !!remember;
this._params = null;
this._propagate = false;
this._context = context || this;
this.dispatch = Signal.prototype.dispatch.bind(this);
}
Signal.prototype = {
active: true,
then: function(listener, priority, context) {
var binding, method = 'push';
if (typeof priority !== 'number') {
context = priority;
priority = 0;
}
binding = new SignalBinding(this, listener, context || this.context, priority);
if (this._bindings.length && this._sorted) {
if (binding._priority <= this._bindings[0]._priority) {
method = 'unshift';
} else if (binding._priority < this._bindings[this._bindings.length - 1]._priority) {
this._sorted = false;
}
}
this._bindings[method](binding);
if (this._remember && this._params) {
binding.execute(this._params);
}
return binding;
},
thenOnce: function(listener, priority, context) {
var bind;
return bind = this.then(function() {
bind.remove();
listener.apply(context, arguments);
}, priority, context);
},
remove: function(binding) {
var index = this._bindings.indexOf(binding);
if (index >= 0) {
this._bindings[index] = null;
this._erased = true;
}
},
call: function(params) {
if (!this.active) {
return;
}
params = Array.prototype.slice.call(arguments);
if (this._remember) {
this._params = params;
}
if (!this._bindings.length) {
return;
}
if (this._erased) {
console.log('Some bindings erased, filter');
this._erased = false;
this._bindings = this._bindings.filter(function(value) {
return value;
})
}
if (!this._sorted) {
console.log('Some bindings added, sort');
this._sorted = true;
this._bindings.sort(function(left, right) {
return left._priority - right._priority;
});
}
var bindings = this._bindings.slice(), n = bindings.length;
this._propagate = true;
do { --n } while (bindings[n] && this._propagate && bindings[n].execute(params) !== false)
},
clear: function() {
this._bindings = [];
this._params = null;
},
remember: function() {
this._remember = true;
},
forget: function() {
this._params = null;
this._remember = false;
},
halt: function() {
this._propagate = false;
}
};
function SignalBinding(signal, listener, context, priority) {
this.active = true;
this.context = context;
this._signal = signal;
this._listener = listener;
this._priority = priority || 0;
}
SignalBinding.prototype = {
execute: function(params) {
if (this.active && this._listener) {
return this._listener.apply(this.context, params);
}
return void 0;
},
detach: function() {
if (this._signal) {
this._signal.remove(this);
this._signal = null;
}
},
repriorize: function(priority) {
this._signal._sorted = false;
this._priority = priority;
}
};
function SignalEmitter() {
this._signals = {};
}
SignalEmitter.prototype = {
add: function(eventName, listener, priority, context) {
if (!this._signals[eventName]) {
this._signals[eventName] = new Signal();
}
return this._signals[eventName].then(listener, priority, context);
},
remove: function(eventName, binding) {
var signal = this._signals[eventName];
signal && signal.remove(binding);
},
getSignal: function(eventName) {
return this._signals[eventName];
},
dispatch: function(eventName) {
var signal = this._signals[eventName];
if (signal) {
if (arguments.length > 1) {
signal.dispatch.apply(signal, Array.prototype.slice.call(arguments,1));
} else {
signal.dispatch();
}
}
}
};
SignalEmitter.applyTo = function(target) {
SignalEmitter.call(target);
for (var key in SignalEmitter.prototype) {
if (SignalEmitter.prototype.hasOwnProperty(key)) {
target[key] = SignalEmitter.prototype[key];
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment