1 | import * as util from './util';
|
2 | import * as is from './is';
|
3 | import Event from './event';
|
4 |
|
5 | const eventRegex = /^([^.]+)(\.(?:[^.]+))?$/;
|
6 | const universalNamespace = '.*';
|
7 |
|
8 | const defaults = {
|
9 | qualifierCompare: function( q1, q2 ){
|
10 | return q1 === q2;
|
11 | },
|
12 | eventMatches: function( /*context, listener, eventObj*/ ){
|
13 | return true;
|
14 | },
|
15 | addEventFields: function( /*context, evt*/ ){
|
16 | },
|
17 | callbackContext: function( context/*, listener, eventObj*/ ){
|
18 | return context;
|
19 | },
|
20 | beforeEmit: function(/* context, listener, eventObj */){
|
21 | },
|
22 | afterEmit: function(/* context, listener, eventObj */){
|
23 | },
|
24 | bubble: function( /*context*/ ){
|
25 | return false;
|
26 | },
|
27 | parent: function( /*context*/ ){
|
28 | return null;
|
29 | },
|
30 | context: null
|
31 | };
|
32 |
|
33 | let defaultsKeys = Object.keys( defaults );
|
34 | let emptyOpts = {};
|
35 |
|
36 | function Emitter( opts = emptyOpts, context ){
|
37 |
|
38 | for( let i = 0; i < defaultsKeys.length; i++ ){
|
39 | let key = defaultsKeys[i];
|
40 |
|
41 | this[key] = opts[key] || defaults[key];
|
42 | }
|
43 |
|
44 | this.context = context || this.context;
|
45 | this.listeners = [];
|
46 | this.emitting = 0;
|
47 | }
|
48 |
|
49 | let p = Emitter.prototype;
|
50 |
|
51 | let forEachEvent = function( self, handler, events, qualifier, callback, conf, confOverrides ){
|
52 | if( is.fn( qualifier ) ){
|
53 | callback = qualifier;
|
54 | qualifier = null;
|
55 | }
|
56 |
|
57 | if( confOverrides ){
|
58 | if( conf == null ){
|
59 | conf = confOverrides;
|
60 | } else {
|
61 | conf = util.assign( {}, conf, confOverrides );
|
62 | }
|
63 | }
|
64 |
|
65 | let eventList = is.array(events) ? events : events.split(/\s+/);
|
66 |
|
67 | for( let i = 0; i < eventList.length; i++ ){
|
68 | let evt = eventList[i];
|
69 |
|
70 | if( is.emptyString( evt ) ){ continue; }
|
71 |
|
72 | let match = evt.match( eventRegex );
|
73 |
|
74 | if( match ){
|
75 | let type = match[1];
|
76 | let namespace = match[2] ? match[2] : null;
|
77 | let ret = handler( self, evt, type, namespace, qualifier, callback, conf );
|
78 |
|
79 | if( ret === false ){ break; }
|
80 | }
|
81 | }
|
82 | };
|
83 |
|
84 | let makeEventObj = function( self, obj ){
|
85 | self.addEventFields( self.context, obj );
|
86 |
|
87 | return new Event( obj.type, obj );
|
88 | };
|
89 |
|
90 | let forEachEventObj = function( self, handler, events ){
|
91 | if( is.event( events ) ){
|
92 | handler( self, events );
|
93 |
|
94 | return;
|
95 | } else if( is.plainObject( events ) ){
|
96 | handler( self, makeEventObj( self, events ) );
|
97 |
|
98 | return;
|
99 | }
|
100 |
|
101 | let eventList = is.array(events) ? events : events.split(/\s+/);
|
102 |
|
103 | for( let i = 0; i < eventList.length; i++ ){
|
104 | let evt = eventList[i];
|
105 |
|
106 | if( is.emptyString( evt ) ){ continue; }
|
107 |
|
108 | let match = evt.match( eventRegex );
|
109 |
|
110 | if( match ){
|
111 | let type = match[1];
|
112 | let namespace = match[2] ? match[2] : null;
|
113 | let eventObj = makeEventObj( self, {
|
114 | type: type,
|
115 | namespace: namespace,
|
116 | target: self.context
|
117 | } );
|
118 |
|
119 | handler( self, eventObj );
|
120 | }
|
121 | }
|
122 | };
|
123 |
|
124 | p.on = p.addListener = function( events, qualifier, callback, conf, confOverrides ){
|
125 | forEachEvent( this, function( self, event, type, namespace, qualifier, callback, conf ){
|
126 | if( is.fn( callback ) ){
|
127 | self.listeners.push( {
|
128 | event: event,
|
129 | callback: callback,
|
130 | type: type,
|
131 | namespace: namespace,
|
132 | qualifier: qualifier,
|
133 | conf: conf
|
134 | } );
|
135 | }
|
136 | }, events, qualifier, callback, conf, confOverrides );
|
137 |
|
138 | return this;
|
139 | };
|
140 |
|
141 | p.one = function( events, qualifier, callback, conf ){
|
142 | return this.on( events, qualifier, callback, conf, { one: true } );
|
143 | };
|
144 |
|
145 | p.removeListener = p.off = function( events, qualifier, callback, conf ){
|
146 | if( this.emitting !== 0 ){
|
147 | this.listeners = util.copyArray( this.listeners );
|
148 | }
|
149 |
|
150 | let listeners = this.listeners;
|
151 |
|
152 | for( let i = listeners.length - 1; i >= 0; i-- ){
|
153 | let listener = listeners[i];
|
154 |
|
155 | forEachEvent( this, function( self, event, type, namespace, qualifier, callback/*, conf*/ ){
|
156 | if(
|
157 | ( listener.type === type || events === '*' ) &&
|
158 | ( (!namespace && listener.namespace !== '.*') || listener.namespace === namespace ) &&
|
159 | ( !qualifier || self.qualifierCompare( listener.qualifier, qualifier ) ) &&
|
160 | ( !callback || listener.callback === callback )
|
161 | ){
|
162 | listeners.splice( i, 1 );
|
163 |
|
164 | return false;
|
165 | }
|
166 | }, events, qualifier, callback, conf );
|
167 | }
|
168 |
|
169 | return this;
|
170 | };
|
171 |
|
172 | p.removeAllListeners = function(){
|
173 | return this.removeListener('*');
|
174 | };
|
175 |
|
176 | p.emit = p.trigger = function( events, extraParams, manualCallback ){
|
177 | let listeners = this.listeners;
|
178 | let numListenersBeforeEmit = listeners.length;
|
179 |
|
180 | this.emitting++;
|
181 |
|
182 | if( !is.array( extraParams ) ){
|
183 | extraParams = [ extraParams ];
|
184 | }
|
185 |
|
186 | forEachEventObj( this, function( self, eventObj ){
|
187 | if( manualCallback != null ){
|
188 | listeners = [{
|
189 | event: eventObj.event,
|
190 | type: eventObj.type,
|
191 | namespace: eventObj.namespace,
|
192 | callback: manualCallback
|
193 | }];
|
194 |
|
195 | numListenersBeforeEmit = listeners.length;
|
196 | }
|
197 |
|
198 | for( let i = 0; i < numListenersBeforeEmit; i++ ){
|
199 | let listener = listeners[i];
|
200 |
|
201 | if(
|
202 | ( listener.type === eventObj.type ) &&
|
203 | ( !listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace ) &&
|
204 | ( self.eventMatches( self.context, listener, eventObj ) )
|
205 | ){
|
206 | let args = [ eventObj ];
|
207 |
|
208 | if( extraParams != null ){
|
209 | util.push( args, extraParams );
|
210 | }
|
211 |
|
212 | self.beforeEmit( self.context, listener, eventObj );
|
213 |
|
214 | if( listener.conf && listener.conf.one ){
|
215 | self.listeners = self.listeners.filter( l => l !== listener );
|
216 | }
|
217 |
|
218 | let context = self.callbackContext( self.context, listener, eventObj );
|
219 | let ret = listener.callback.apply( context, args );
|
220 |
|
221 | self.afterEmit( self.context, listener, eventObj );
|
222 |
|
223 | if( ret === false ){
|
224 | eventObj.stopPropagation();
|
225 | eventObj.preventDefault();
|
226 | }
|
227 | }
|
228 | }
|
229 |
|
230 | if( self.bubble( self.context ) && !eventObj.isPropagationStopped() ){
|
231 | self.parent( self.context ).emit( eventObj, extraParams );
|
232 | }
|
233 | }, events );
|
234 |
|
235 | this.emitting--;
|
236 |
|
237 | return this;
|
238 | };
|
239 |
|
240 | export default Emitter;
|