|
import * as util from './util'; |
|
import * as is from './is'; |
|
import Event from './event'; |
|
|
|
const eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; |
|
const universalNamespace = '.*'; |
|
|
|
const defaults = { |
|
qualifierCompare: function( q1, q2 ){ |
|
return q1 === q2; |
|
}, |
|
eventMatches: function( ){ |
|
return true; |
|
}, |
|
addEventFields: function( ){ |
|
}, |
|
callbackContext: function( context ){ |
|
return context; |
|
}, |
|
beforeEmit: function(){ |
|
}, |
|
afterEmit: function(){ |
|
}, |
|
bubble: function( ){ |
|
return false; |
|
}, |
|
parent: function( ){ |
|
return null; |
|
}, |
|
context: null |
|
}; |
|
|
|
let defaultsKeys = Object.keys( defaults ); |
|
let emptyOpts = {}; |
|
|
|
function Emitter( opts = emptyOpts, context ){ |
|
|
|
for( let i = 0; i < defaultsKeys.length; i++ ){ |
|
let key = defaultsKeys[i]; |
|
|
|
this[key] = opts[key] || defaults[key]; |
|
} |
|
|
|
this.context = context || this.context; |
|
this.listeners = []; |
|
this.emitting = 0; |
|
} |
|
|
|
let p = Emitter.prototype; |
|
|
|
let forEachEvent = function( self, handler, events, qualifier, callback, conf, confOverrides ){ |
|
if( is.fn( qualifier ) ){ |
|
callback = qualifier; |
|
qualifier = null; |
|
} |
|
|
|
if( confOverrides ){ |
|
if( conf == null ){ |
|
conf = confOverrides; |
|
} else { |
|
conf = util.assign( {}, conf, confOverrides ); |
|
} |
|
} |
|
|
|
let eventList = is.array(events) ? events : events.split(/\s+/); |
|
|
|
for( let i = 0; i < eventList.length; i++ ){ |
|
let evt = eventList[i]; |
|
|
|
if( is.emptyString( evt ) ){ continue; } |
|
|
|
let match = evt.match( eventRegex ); |
|
|
|
if( match ){ |
|
let type = match[1]; |
|
let namespace = match[2] ? match[2] : null; |
|
let ret = handler( self, evt, type, namespace, qualifier, callback, conf ); |
|
|
|
if( ret === false ){ break; } |
|
} |
|
} |
|
}; |
|
|
|
let makeEventObj = function( self, obj ){ |
|
self.addEventFields( self.context, obj ); |
|
|
|
return new Event( obj.type, obj ); |
|
}; |
|
|
|
let forEachEventObj = function( self, handler, events ){ |
|
if( is.event( events ) ){ |
|
handler( self, events ); |
|
|
|
return; |
|
} else if( is.plainObject( events ) ){ |
|
handler( self, makeEventObj( self, events ) ); |
|
|
|
return; |
|
} |
|
|
|
let eventList = is.array(events) ? events : events.split(/\s+/); |
|
|
|
for( let i = 0; i < eventList.length; i++ ){ |
|
let evt = eventList[i]; |
|
|
|
if( is.emptyString( evt ) ){ continue; } |
|
|
|
let match = evt.match( eventRegex ); |
|
|
|
if( match ){ |
|
let type = match[1]; |
|
let namespace = match[2] ? match[2] : null; |
|
let eventObj = makeEventObj( self, { |
|
type: type, |
|
namespace: namespace, |
|
target: self.context |
|
} ); |
|
|
|
handler( self, eventObj ); |
|
} |
|
} |
|
}; |
|
|
|
p.on = p.addListener = function( events, qualifier, callback, conf, confOverrides ){ |
|
forEachEvent( this, function( self, event, type, namespace, qualifier, callback, conf ){ |
|
if( is.fn( callback ) ){ |
|
self.listeners.push( { |
|
event: event, |
|
callback: callback, |
|
type: type, |
|
namespace: namespace, |
|
qualifier: qualifier, |
|
conf: conf |
|
} ); |
|
} |
|
}, events, qualifier, callback, conf, confOverrides ); |
|
|
|
return this; |
|
}; |
|
|
|
p.one = function( events, qualifier, callback, conf ){ |
|
return this.on( events, qualifier, callback, conf, { one: true } ); |
|
}; |
|
|
|
p.removeListener = p.off = function( events, qualifier, callback, conf ){ |
|
if( this.emitting !== 0 ){ |
|
this.listeners = util.copyArray( this.listeners ); |
|
} |
|
|
|
let listeners = this.listeners; |
|
|
|
for( let i = listeners.length - 1; i >= 0; i-- ){ |
|
let listener = listeners[i]; |
|
|
|
forEachEvent( this, function( self, event, type, namespace, qualifier, callback ){ |
|
if( |
|
( listener.type === type || events === '*' ) && |
|
( (!namespace && listener.namespace !== '.*') || listener.namespace === namespace ) && |
|
( !qualifier || self.qualifierCompare( listener.qualifier, qualifier ) ) && |
|
( !callback || listener.callback === callback ) |
|
){ |
|
listeners.splice( i, 1 ); |
|
|
|
return false; |
|
} |
|
}, events, qualifier, callback, conf ); |
|
} |
|
|
|
return this; |
|
}; |
|
|
|
p.removeAllListeners = function(){ |
|
return this.removeListener('*'); |
|
}; |
|
|
|
p.emit = p.trigger = function( events, extraParams, manualCallback ){ |
|
let listeners = this.listeners; |
|
let numListenersBeforeEmit = listeners.length; |
|
|
|
this.emitting++; |
|
|
|
if( !is.array( extraParams ) ){ |
|
extraParams = [ extraParams ]; |
|
} |
|
|
|
forEachEventObj( this, function( self, eventObj ){ |
|
if( manualCallback != null ){ |
|
listeners = [{ |
|
event: eventObj.event, |
|
type: eventObj.type, |
|
namespace: eventObj.namespace, |
|
callback: manualCallback |
|
}]; |
|
|
|
numListenersBeforeEmit = listeners.length; |
|
} |
|
|
|
for( let i = 0; i < numListenersBeforeEmit; i++ ){ |
|
let listener = listeners[i]; |
|
|
|
if( |
|
( listener.type === eventObj.type ) && |
|
( !listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace ) && |
|
( self.eventMatches( self.context, listener, eventObj ) ) |
|
){ |
|
let args = [ eventObj ]; |
|
|
|
if( extraParams != null ){ |
|
util.push( args, extraParams ); |
|
} |
|
|
|
self.beforeEmit( self.context, listener, eventObj ); |
|
|
|
if( listener.conf && listener.conf.one ){ |
|
self.listeners = self.listeners.filter( l => l !== listener ); |
|
} |
|
|
|
let context = self.callbackContext( self.context, listener, eventObj ); |
|
let ret = listener.callback.apply( context, args ); |
|
|
|
self.afterEmit( self.context, listener, eventObj ); |
|
|
|
if( ret === false ){ |
|
eventObj.stopPropagation(); |
|
eventObj.preventDefault(); |
|
} |
|
} |
|
} |
|
|
|
if( self.bubble( self.context ) && !eventObj.isPropagationStopped() ){ |
|
self.parent( self.context ).emit( eventObj, extraParams ); |
|
} |
|
}, events ); |
|
|
|
this.emitting--; |
|
|
|
return this; |
|
}; |
|
|
|
export default Emitter; |
|
|