energy
Version:
Simple cross-platform event emitter
178 lines (159 loc) • 4.54 kB
JavaScript
/** @preserve npm.im/energy */
!function(root, name, make) {
if (typeof module != 'undefined' && module.exports) module.exports = make()
else root[name] = make()
}(this, 'energy', function() {
var emitter = energy.prototype = Energy.prototype
, proceed = {}
, array = []
, slice = array.slice
, owns = proceed.hasOwnProperty
, events = '_events'
, origin = '_origin'
, emit = 'emit'
, init = 'init'
, listeners = 'listeners'
, ifArray = function(o) {
return o instanceof Array && o
}
function defaults(ops, defs) {
for (var k in defs) if (void 0 === ops[k]) ops[k] = defs[k]
return ops
}
/**
* @constructor
*/
function Energy() {
this[events] = {}
}
/**
* @return {Energy} emitter instance
*/
function energy(o) {
return o instanceof Energy ? o[init]() : new Energy
}
/**
* @this {Function} wrapper that constructs the source instance
* @param {Object|Function} target to convert into emitter
* @return {Object|Function} target converted into emitter
*/
energy['to'] = function(target) {
return defaults(target, this.call())
}
/**
* @this {Object|Energy|Function} source emitter or emitter-like
* @param {Object|Energy|Function} target to convert into emitter
* @return {Object|Energy|Function} source for chaining
*/
emitter['to'] = function(target) {
defaults(target, this)
return this
}
/**
* @param {{length:number}} fns
* @param {*} scope
* @param {Array|Arguments} args
* @param {*=} stop breaker
* @return {number} fired
*/
function applies(fns, scope, args, stop) {
var l = fns && fns.length, i = 0
stop = void 0 === stop ? proceed : stop
while (i < l) if (stop === fns[i++].apply(scope, args)) break
return i
}
/**
* @param {Object} o
* @param {string} k
* @param {*} def
*/
function ensure(o, k, def) {
o[k] = owns.call(this, k) && this[k] || def
}
/**
* @param {*} listener
* @param {*} fn
* @return {boolean} true if they're the same listener
*/
function is(listener, fn) {
return listener === fn || !!listener && listener[origin] === fn
}
/**
* @param {Array} a array to mutate
* @param {*=} v value to remove
* @param {number=} quota occurrences to remove
*/
function pull(a, v, quota) {
quota >>= 0
// Loop down so that splices don't interfere with subsequent iterations
for (var i = a.length; i--;) if (is(a[i], v) && a.splice(i, 1) && !--quota) break
}
/**
* @this {Energy|Object}
*/
emitter[init] = function() {
ensure(this, events, {})
return this
}
/**
* @param {string|number} id
* @return {number} fired
*/
emitter[emit] = function(id) {
var rest = slice.call(arguments, 1), fired = applies(this[listeners](id), this, rest)
return fired
}
/**
* @param {string|number} id
* @return {Array}
*/
emitter[listeners] = function(id) {
if (null == id) throw new TypeError('@listeners')
return this[events][id] = ifArray(this[events][id]) || []
}
/**
* @return {Energy}
*/
emitter['clone'] = function() {
var clone = new Energy(this)
defaults(clone[events], this[events])
return clone
}
/**
* @param {string|number} id
* @param {Function} fn listener to add
* @return {Energy|Object}
*/
emitter['on'] = function on(id, fn) {
if (null == id || null == fn) throw new TypeError('@on')
this[listeners](id).push(fn)
return this
}
/**
* @param {(string|number)=} id
* @param {Function=} fn listener to remove
* @param {number=} quota occurrences to remove (the default 0 removes all)
* @return {Energy|Object}
*/
emitter['off'] = function(id, fn, quota) {
if (null != fn) pull(this[listeners](id), fn, quota)
else if (null != id) this[events][id] = void 0
else if (!arguments.length) this[events] = {}
return this
}
/**
* @param {string|number} id
* @param {Function} fn one-time listener to add
* @return {Energy|Object}
*/
emitter['once'] = function(id, fn) {
var that = this, handler = function() {
that['off'](id, handler)
return fn.apply(this, arguments)
}
handler[origin] = fn
return this['on'](id, handler)
}
energy['applies'] = applies
return energy
});