nativeloop
Version:
⭐ Axway Amplify module for using nativeloop with Appcelerator Titanium SDK Framework
709 lines (611 loc) • 19.3 kB
JavaScript
/*!
* EventEmitter2
* https://github.com/hij1nx/EventEmitter2
*
* Copyright (c) 2013 hij1nx
* Licensed under the MIT license.
*/
;
! function( undefined ) {
var isArray = Array.isArray ? Array.isArray : function _isArray( obj ) {
return Object.prototype.toString.call( obj ) === "[object Array]";
};
var defaultMaxListeners = 10;
function init() {
this._events = {};
if( this._conf ) {
configure.call( this, this._conf );
}
}
function configure( conf ) {
if( conf ) {
this._conf = conf;
conf.delimiter && ( this.delimiter = conf.delimiter );
this._events.maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners;
conf.wildcard && ( this.wildcard = conf.wildcard );
conf.newListener && ( this.newListener = conf.newListener );
conf.verboseMemoryLeak && ( this.verboseMemoryLeak = conf.verboseMemoryLeak );
if( this.wildcard ) {
this.listenerTree = {};
}
} else {
this._events.maxListeners = defaultMaxListeners;
}
}
function logPossibleMemoryLeak( count, eventName ) {
var errorMsg = '(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.';
if( this.verboseMemoryLeak ) {
errorMsg += ' Event name: %s.';
console.error( errorMsg, count, eventName );
} else {
console.error( errorMsg, count );
}
if( console.trace ) {
console.trace();
}
}
function EventEmitter( conf ) {
this._events = {};
this.newListener = false;
this.verboseMemoryLeak = false;
configure.call( this, conf );
}
EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property
//
// Attention, function return type now is array, always !
// It has zero elements if no any matches found and one or more
// elements (leafs) if there are matches
//
function searchListenerTree( handlers, type, tree, i ) {
if( !tree ) {
return [];
}
var listeners = [],
leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
typeLength = type.length,
currentType = type[ i ],
nextType = type[ i + 1 ];
if( i === typeLength && tree._listeners ) {
//
// If at the end of the event(s) list and the tree has listeners
// invoke those listeners.
//
if( typeof tree._listeners === 'function' ) {
handlers && handlers.push( tree._listeners );
return [ tree ];
} else {
for( leaf = 0, len = tree._listeners.length; leaf < len; leaf++ ) {
handlers && handlers.push( tree._listeners[ leaf ] );
}
return [ tree ];
}
}
if( ( currentType === '*' || currentType === '**' ) || tree[ currentType ] ) {
//
// If the event emitted is '*' at this part
// or there is a concrete match at this patch
//
if( currentType === '*' ) {
for( branch in tree ) {
if( branch !== '_listeners' && tree.hasOwnProperty( branch ) ) {
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ branch ], i + 1 ) );
}
}
return listeners;
} else if( currentType === '**' ) {
endReached = ( i + 1 === typeLength || ( i + 2 === typeLength && nextType === '*' ) );
if( endReached && tree._listeners ) {
// The next element has a _listeners, add it to the handlers.
listeners = listeners.concat( searchListenerTree( handlers, type, tree, typeLength ) );
}
for( branch in tree ) {
if( branch !== '_listeners' && tree.hasOwnProperty( branch ) ) {
if( branch === '*' || branch === '**' ) {
if( tree[ branch ]._listeners && !endReached ) {
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ branch ], typeLength ) );
}
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ branch ], i ) );
} else if( branch === nextType ) {
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ branch ], i + 2 ) );
} else {
// No match on this one, shift into the tree but not in the type array.
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ branch ], i ) );
}
}
}
return listeners;
}
listeners = listeners.concat( searchListenerTree( handlers, type, tree[ currentType ], i + 1 ) );
}
xTree = tree[ '*' ];
if( xTree ) {
//
// If the listener tree will allow any match for this part,
// then recursively explore all branches of the tree
//
searchListenerTree( handlers, type, xTree, i + 1 );
}
xxTree = tree[ '**' ];
if( xxTree ) {
if( i < typeLength ) {
if( xxTree._listeners ) {
// If we have a listener on a '**', it will catch all, so add its handler.
searchListenerTree( handlers, type, xxTree, typeLength );
}
// Build arrays of matching next branches and others.
for( branch in xxTree ) {
if( branch !== '_listeners' && xxTree.hasOwnProperty( branch ) ) {
if( branch === nextType ) {
// We know the next element will match, so jump twice.
searchListenerTree( handlers, type, xxTree[ branch ], i + 2 );
} else if( branch === currentType ) {
// Current node matches, move into the tree.
searchListenerTree( handlers, type, xxTree[ branch ], i + 1 );
} else {
isolatedBranch = {};
isolatedBranch[ branch ] = xxTree[ branch ];
searchListenerTree( handlers, type, {
'**': isolatedBranch
}, i + 1 );
}
}
}
} else if( xxTree._listeners ) {
// We have reached the end and still on a '**'
searchListenerTree( handlers, type, xxTree, typeLength );
} else if( xxTree[ '*' ] && xxTree[ '*' ]._listeners ) {
searchListenerTree( handlers, type, xxTree[ '*' ], typeLength );
}
}
return listeners;
}
function growListenerTree( type, listener ) {
type = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
//
// Looks for two consecutive '**', if so, don't add the event at all.
//
for( var i = 0, len = type.length; i + 1 < len; i++ ) {
if( type[ i ] === '**' && type[ i + 1 ] === '**' ) {
return;
}
}
var tree = this.listenerTree;
var name = type.shift();
while( name !== undefined ) {
if( !tree[ name ] ) {
tree[ name ] = {};
}
tree = tree[ name ];
if( type.length === 0 ) {
if( !tree._listeners ) {
tree._listeners = listener;
} else {
if( typeof tree._listeners === 'function' ) {
tree._listeners = [ tree._listeners ];
}
tree._listeners.push( listener );
if( !tree._listeners.warned && this._events.maxListeners > 0 && tree._listeners.length > this._events.maxListeners ) {
tree._listeners.warned = true;
logPossibleMemoryLeak.call( this, tree._listeners.length, name );
}
}
return true;
}
name = type.shift();
}
return true;
}
// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.delimiter = '.';
EventEmitter.prototype.setMaxListeners = function( n ) {
if( n !== undefined ) {
this._events || init.call( this );
this._events.maxListeners = n;
if( !this._conf ) this._conf = {};
this._conf.maxListeners = n;
}
};
EventEmitter.prototype.event = '';
EventEmitter.prototype.once = function( event, fn ) {
this.many( event, 1, fn );
return this;
};
EventEmitter.prototype.many = function( event, ttl, fn ) {
var self = this;
if( typeof fn !== 'function' ) {
throw new Error( 'many only accepts instances of Function' );
}
function listener() {
if( --ttl === 0 ) {
self.off( event, listener );
}
fn.apply( this, arguments );
}
listener._origin = fn;
this.on( event, listener );
return self;
};
EventEmitter.prototype.emit = function() {
this._events || init.call( this );
var type = arguments[ 0 ];
if( type === 'newListener' && !this.newListener ) {
if( !this._events.newListener ) {
return false;
}
}
var al = arguments.length;
var args, l, i, j;
var handler;
if( this._all && this._all.length ) {
handler = this._all.slice();
if( al > 3 ) {
args = new Array( al );
for( j = 0; j < al; j++ ) args[ j ] = arguments[ j ];
}
for( i = 0, l = handler.length; i < l; i++ ) {
this.event = type;
switch( al ) {
case 1:
handler[ i ].call( this, type );
break;
case 2:
handler[ i ].call( this, type, arguments[ 1 ] );
break;
case 3:
handler[ i ].call( this, type, arguments[ 1 ], arguments[ 2 ] );
break;
default:
handler[ i ].apply( this, args );
}
}
}
if( this.wildcard ) {
handler = [];
var ns = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
searchListenerTree.call( this, handler, ns, this.listenerTree, 0 );
} else {
handler = this._events[ type ];
if( typeof handler === 'function' ) {
this.event = type;
switch( al ) {
case 1:
handler.call( this );
break;
case 2:
handler.call( this, arguments[ 1 ] );
break;
case 3:
handler.call( this, arguments[ 1 ], arguments[ 2 ] );
break;
default:
args = new Array( al - 1 );
for( j = 1; j < al; j++ ) args[ j - 1 ] = arguments[ j ];
handler.apply( this, args );
}
return true;
} else if( handler ) {
// need to make copy of handlers because list can change in the middle
// of emit call
handler = handler.slice();
}
}
if( handler && handler.length ) {
if( al > 3 ) {
args = new Array( al - 1 );
for( j = 1; j < al; j++ ) args[ j - 1 ] = arguments[ j ];
}
for( i = 0, l = handler.length; i < l; i++ ) {
this.event = type;
switch( al ) {
case 1:
handler[ i ].call( this );
break;
case 2:
handler[ i ].call( this, arguments[ 1 ] );
break;
case 3:
handler[ i ].call( this, arguments[ 1 ], arguments[ 2 ] );
break;
default:
handler[ i ].apply( this, args );
}
}
return true;
} else if( !this._all && type === 'error' ) {
if( arguments[ 1 ] instanceof Error ) {
throw arguments[ 1 ]; // Unhandled 'error' event
} else {
throw new Error( "Uncaught, unspecified 'error' event." );
}
return false;
}
return !!this._all;
};
EventEmitter.prototype.emitAsync = function() {
this._events || init.call( this );
var type = arguments[ 0 ];
if( type === 'newListener' && !this.newListener ) {
if( !this._events.newListener ) {
return Promise.resolve( [ false ] );
}
}
var promises = [];
var al = arguments.length;
var args, l, i, j;
var handler;
if( this._all ) {
if( al > 3 ) {
args = new Array( al );
for( j = 1; j < al; j++ ) args[ j ] = arguments[ j ];
}
for( i = 0, l = this._all.length; i < l; i++ ) {
this.event = type;
switch( al ) {
case 1:
promises.push( this._all[ i ].call( this, type ) );
break;
case 2:
promises.push( this._all[ i ].call( this, type, arguments[ 1 ] ) );
break;
case 3:
promises.push( this._all[ i ].call( this, type, arguments[ 1 ], arguments[ 2 ] ) );
break;
default:
promises.push( this._all[ i ].apply( this, args ) );
}
}
}
if( this.wildcard ) {
handler = [];
var ns = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
searchListenerTree.call( this, handler, ns, this.listenerTree, 0 );
} else {
handler = this._events[ type ];
}
if( typeof handler === 'function' ) {
this.event = type;
switch( al ) {
case 1:
promises.push( handler.call( this ) );
break;
case 2:
promises.push( handler.call( this, arguments[ 1 ] ) );
break;
case 3:
promises.push( handler.call( this, arguments[ 1 ], arguments[ 2 ] ) );
break;
default:
args = new Array( al - 1 );
for( j = 1; j < al; j++ ) args[ j - 1 ] = arguments[ j ];
promises.push( handler.apply( this, args ) );
}
} else if( handler && handler.length ) {
if( al > 3 ) {
args = new Array( al - 1 );
for( j = 1; j < al; j++ ) args[ j - 1 ] = arguments[ j ];
}
for( i = 0, l = handler.length; i < l; i++ ) {
this.event = type;
switch( al ) {
case 1:
promises.push( handler[ i ].call( this ) );
break;
case 2:
promises.push( handler[ i ].call( this, arguments[ 1 ] ) );
break;
case 3:
promises.push( handler[ i ].call( this, arguments[ 1 ], arguments[ 2 ] ) );
break;
default:
promises.push( handler[ i ].apply( this, args ) );
}
}
} else if( !this._all && type === 'error' ) {
if( arguments[ 1 ] instanceof Error ) {
return Promise.reject( arguments[ 1 ] ); // Unhandled 'error' event
} else {
return Promise.reject( "Uncaught, unspecified 'error' event." );
}
}
return Promise.all( promises );
};
EventEmitter.prototype.on = function( type, listener ) {
if( typeof type === 'function' ) {
this.onAny( type );
return this;
}
if( typeof listener !== 'function' ) {
throw new Error( 'on only accepts instances of Function' );
}
this._events || init.call( this );
// To avoid recursion in the case that type == "newListeners"! Before
// adding it to the listeners, first emit "newListeners".
this.emit( 'newListener', type, listener );
if( this.wildcard ) {
growListenerTree.call( this, type, listener );
return this;
}
if( !this._events[ type ] ) {
// Optimize the case of one listener. Don't need the extra array object.
this._events[ type ] = listener;
} else {
if( typeof this._events[ type ] === 'function' ) {
// Change to array.
this._events[ type ] = [ this._events[ type ] ];
}
// If we've already got an array, just append.
this._events[ type ].push( listener );
// Check for listener leak
if( !this._events[ type ].warned && this._events.maxListeners > 0 && this._events[ type ].length > this._events.maxListeners ) {
this._events[ type ].warned = true;
logPossibleMemoryLeak.call( this, this._events[ type ].length, type );
}
}
return this;
};
EventEmitter.prototype.onAny = function( fn ) {
if( typeof fn !== 'function' ) {
throw new Error( 'onAny only accepts instances of Function' );
}
if( !this._all ) {
this._all = [];
}
// Add the function to the event listener collection.
this._all.push( fn );
return this;
};
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
EventEmitter.prototype.off = function( type, listener ) {
if( typeof listener !== 'function' ) {
throw new Error( 'removeListener only takes instances of Function' );
}
var handlers, leafs = [];
if( this.wildcard ) {
var ns = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
leafs = searchListenerTree.call( this, null, ns, this.listenerTree, 0 );
} else {
// does not use listeners(), so no side effect of creating _events[type]
if( !this._events[ type ] ) return this;
handlers = this._events[ type ];
leafs.push( {
_listeners: handlers
} );
}
for( var iLeaf = 0; iLeaf < leafs.length; iLeaf++ ) {
var leaf = leafs[ iLeaf ];
handlers = leaf._listeners;
if( isArray( handlers ) ) {
var position = -1;
for( var i = 0, length = handlers.length; i < length; i++ ) {
if( handlers[ i ] === listener || ( handlers[ i ].listener && handlers[ i ].listener === listener ) || ( handlers[ i ]._origin && handlers[ i ]._origin === listener ) ) {
position = i;
break;
}
}
if( position < 0 ) {
continue;
}
if( this.wildcard ) {
leaf._listeners.splice( position, 1 );
} else {
this._events[ type ].splice( position, 1 );
}
if( handlers.length === 0 ) {
if( this.wildcard ) {
delete leaf._listeners;
} else {
delete this._events[ type ];
}
}
this.emit( "removeListener", type, listener );
return this;
} else if( handlers === listener || ( handlers.listener && handlers.listener === listener ) || ( handlers._origin && handlers._origin === listener ) ) {
if( this.wildcard ) {
delete leaf._listeners;
} else {
delete this._events[ type ];
}
this.emit( "removeListener", type, listener );
}
}
function recursivelyGarbageCollect( root ) {
if( root === undefined ) {
return;
}
var keys = Object.keys( root );
for( var i in keys ) {
var key = keys[ i ];
var obj = root[ key ];
if( ( obj instanceof Function ) || ( typeof obj !== "object" ) || ( obj === null ) ) continue;
if( Object.keys( obj ).length > 0 ) {
recursivelyGarbageCollect( root[ key ] );
}
if( Object.keys( obj ).length === 0 ) {
delete root[ key ];
}
}
}
recursivelyGarbageCollect( this.listenerTree );
return this;
};
EventEmitter.prototype.offAny = function( fn ) {
var i = 0,
l = 0,
fns;
if( fn && this._all && this._all.length > 0 ) {
fns = this._all;
for( i = 0, l = fns.length; i < l; i++ ) {
if( fn === fns[ i ] ) {
fns.splice( i, 1 );
this.emit( "removeListenerAny", fn );
return this;
}
}
} else {
fns = this._all;
for( i = 0, l = fns.length; i < l; i++ )
this.emit( "removeListenerAny", fns[ i ] );
this._all = [];
}
return this;
};
EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
EventEmitter.prototype.removeAllListeners = function( type ) {
if( arguments.length === 0 ) {
!this._events || init.call( this );
return this;
}
if( this.wildcard ) {
var ns = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
var leafs = searchListenerTree.call( this, null, ns, this.listenerTree, 0 );
for( var iLeaf = 0; iLeaf < leafs.length; iLeaf++ ) {
var leaf = leafs[ iLeaf ];
leaf._listeners = null;
}
} else if( this._events ) {
this._events[ type ] = null;
}
return this;
};
EventEmitter.prototype.listeners = function( type ) {
if( this.wildcard ) {
var handlers = [];
var ns = typeof type === 'string' ? type.split( this.delimiter ) : type.slice();
searchListenerTree.call( this, handlers, ns, this.listenerTree, 0 );
return handlers;
}
this._events || init.call( this );
if( !this._events[ type ] ) this._events[ type ] = [];
if( !isArray( this._events[ type ] ) ) {
this._events[ type ] = [ this._events[ type ] ];
}
return this._events[ type ];
};
EventEmitter.prototype.listenerCount = function( type ) {
return this.listeners( type ).length;
};
EventEmitter.prototype.listenersAny = function() {
if( this._all ) {
return this._all;
} else {
return [];
}
};
if( typeof define === 'function' && define.amd ) {
// AMD. Register as an anonymous module.
define( function() {
return EventEmitter;
} );
} else if( typeof exports === 'object' ) {
// CommonJS
module.exports = EventEmitter;
} else {
// Browser global.
window.EventEmitter2 = EventEmitter;
}
}();