text: (function(factory) {
  	module.exports = function(_root) {
  		var root = _root || {};
  		return factory(root);
  	};
  })(function(root) {
  	'use strict';
  
  	/** @namespace {devoir} **/
  	/** @namespace {events} Devoir event functionality **/
  
  	function generateID() {
  		return 'E' + (idCounter++);
  	}
  
  	function getEventName(eventName) {
  		if (eventName instanceof root.Event)
  			return eventName.type;
  
  		var p = ('' + eventName).match(/^([^.]+)/);
  		return (p) ? p[1] : undefined;
  	}
  
  	function getNamespace(eventName) {
  		if (eventName instanceof root.Event)
  			return eventName.namespace;
  
  		var ns = (this && this._events) ? this._events._defaultEventNamespace : undefined;
  
  		if (eventName) {
  			var p = ('' + eventName).match(/\.(.*)$/);
  			if (p) ns = p[1];
  		}
  		
  		return ns;
  	}
  
  	function getListeners(_eventName, skipCreate) {
  		var eventName = getEventName.call(this, _eventName),
  				events = this._events;
  
  		if (!events)
  			return;
  
  		var bindings = events._eventBindings,
  				listeners = bindings[eventName];
  
  		if (!listeners) {
  			if (skipCreate)
  				return;
  
  			listeners = bindings[eventName] = [];
  		}
  
  		return listeners;
  	}
  
  	function getAllListeners(_eventName) {
  		var eventName = getEventName.call(this, _eventName),
  				namespace = getNamespace.call(this, _eventName),
  				events = this._events;
  
  		if (!events)
  			return;
  
  		var bindings = events._eventBindings,
  				keys = Object.keys(bindings),
  				allListeners = [];
  
  		for (var i = 0, il = keys.length; i < il; i++) {
  			var key = keys[i],
  					listeners = bindings[eventName];
  
  			if (!listeners)
  				continue;
  
  			for (var j = 0, jl = listeners.length; j < jl; j++) {
  				var listener = listeners[j];
  				if (!listener)
  					continue;
  
  				if (namespace && namespace !== listener.namespace)
  					continue;
  
  				allListeners.push(listener);
  			}
  		}
  
  		return allListeners;
  	}
  
  	function emitToAllListeners(event, listeners, args) {
  		var namespace = event.namespace, 
  				ret = [];
  
  		for (var i = 0, il = listeners.length; i < il; i++) {
  			var listenerObj = listeners[i];
  
  			//Should we halt event chain?
  			if (event.halt === true)
  				break;
  
  			//If propagate is false, don't propagate to other namespaces
  			if (event.propagate === false && listenerObj.namespace !== namespace)
  				continue;
  
  			//Call listeners
  			ret.push(listenerObj.listener.bind(this, event).apply(this, args));
  		}
  
  		return ret;
  	}
  
  	function doEmit(thisEvent, listeners, args) {
  		//Call listeners
  		emitToAllListeners.call(this, thisEvent, listeners, args);
  
  		//Remove all "call-once" listeners
  		var onceListeners = [];
  		for (var i = 0, il = listeners.length; i < il; i++) {
  			var listenerObj = listeners[i];
  			if (listenerObj.once)
  				onceListeners.push(listenerObj.listener);
  		}
  
  		if (onceListeners.length > 0)
  			this.removeListener(thisEvent, onceListeners);
  
  		//Call proxies
  		var events = this._events,
  				proxies = events._eventProxies;
  
  		if (thisEvent.proxy || proxies.length === 0)
  			return true;
  
  		for (var i = 0, il = proxies.length; i < il; i++) {
  			var proxyObj = proxies[i],
  					context = proxyObj.dst,
  					filter = proxyObj.filter;
  
  			if (!context)
  				continue;
  
  			if (filter) {
  				if (typeof filter === 'string' || filter instanceof String) {
  					var filterEventName = getEventName.call(this, filter),
  							filterNamespace = getNamespace.call(this, filter);
  
  					if (filterEventName && filterEventName !== event.type)
  						continue;
  
  					if (filterNamespace && event.namespace !== event.namespace)
  						continue;
  				} else if ((filter instanceof Function) && filter.call(this, event) !== true)
  					continue;
  			}
  
  			if (proxyObj.namespace && proxyObj.namespace !== thisEvent.namespace)
  				continue;
  
  			context.emit.bind(context, thisEvent).apply(context, args);
  		}	
  
  		return true;
  	}
  
  	/**
  	* @class {Event} Event class to hold event information. An instance of this will be passed as "event" to all bound event listeners
  	* @constructor
  	* @param {event}
  	*		@type {String} Event name
  	*		@type {Event} Skip event creation and use this event instance instead
  	* @param {[options]}
  	* 	@type {Object} Options object for event
  	* 		@property {Boolean} {bubbles=true} The event bubbles across namespaces
  	*			@property {Object} {origin} Object that emitted the event @end
  	*			@property {Boolean} {async=false} This event when emitted is asynchronous
  	*		@end
  	**/
  
  	function Event(event, _opts) {
  		if (event instanceof root.Event)
  			return event;
  
  		var opts = _opts || {},
  				bubbles = (opts.bubbles !== false),
  				origin = opts.origin;
  
  		/** @property {String} {id} Unique id of this event **/
  		this.id = generateID();
  
  		/** @property {String} {namespace} Namespace this event was emitted on **/
  		this.namespace = getNamespace.call(origin || this, event);
  
  		/** @property {String} {type} Type name of event **/
  		this.type = getEventName.call(origin || this, event);
  
  		/** @property {Boolean} {bubbles} Does this event bubble? **/
  		this.bubbles = !!bubbles;
  
  		/** @property {Boolean} {propagate} If true then this event will be propagated to all namespaces **/
  		this.propagate = this.bubbles;
  
  		this.preventDefault = false;
  		this.halt = false;
  
  		/** @property {Boolean} {async} If true then this event will be emitted on nextTick **/
  		if (opts.async !== undefined && opts.async !== null)
  			this.async = !!opts.async;
  		
  		/** @property {Object} {origin} The originating object **/
  		if (origin)
  			this.origin = origin;
  	}
  
  	Event.prototype = {
  		constructor: Event,
  		/** @function {stopImmediatePropagation} Stop all propagation immediately **/
  		stopImmediatePropagation: function() {
  			this.halt = true;
  		},
  		/** @function {stopPropagation} Stop propagation. Bound event callbacks in the current namespace will still be called **/
  		stopPropagation: function() {
  			this.propagate = false;
  		},
  		/** @function {preventDefault} Set event property "preventDefault" to **true**. This does nothing internally; it is intended to be used with any callbacks that have default actions. **/
  		preventDefault: function() {
  			this.preventDefault = true;
  		}
  	};
  	root.Event = Event;
  
  	/**
  	* @@class {EventEmitter} Base class for all event emitting functionality
  	**/
  
  	/**
  	* @constructor
  	* @param {[options]} Options object
  	*		@type {Object}
  				@property {Boolean} {async=false} If *true* events will be asynchronous by default
  				@property {Object} {eventsObj={}} Object used to hold bound event listeners. Only define this is you need fine grained control over the events
  				@@property {Object} {proxyObj=[] Array used to hold proxies. Only define this is you need fine grained control over proxies
  			@end
  	**/
  
  	function EventEmitter(_opts) {
  		var opts = _opts || {},
  				eventsObj = opts.eventsObj || {},
  				proxyObj = opts.proxyObj || [];
  		
  		if (!this.hasOwnProperty('_events')) {
  			Object.defineProperty(this, '_events', {
  				writable: false, enumerable: false, configurable: false,
  				value: {}
  			});
  		}
  
  		var events = this._events;
  
  		Object.defineProperty(events, '_async', {
  			writable: false, enumerable: false, configurable: false,
  			value: !!opts.async
  		});
  		
  		if (opts.defaultNamespace || typeof events._eventBindings === 'undefined') {
  			Object.defineProperty(events, '_defaultEventNamespace', {
  				writable: true, enumerable: false, configurable: false,
  				value: opts.defaultNamespace
  			});
  		}
  
  		if (opts.eventsObj || typeof events._eventBindings === 'undefined') {
  			Object.defineProperty(events, '_eventBindings', {
  				writable: true, enumerable: false, configurable: false,
  				value: eventsObj
  			});	
  		}
  
  		if (opts.proxyObj || typeof events._eventProxies === 'undefined') {
  			Object.defineProperty(events, '_eventProxies', {
  				writable: true, enumerable: false, configurable: false,
  				value: proxyObj
  			});	
  		}
  	}
  
  	EventEmitter.prototype = {
  		constructor: EventEmitter,
  		createEvent: function(eventName, _opts) {
  			if (eventName instanceof root.Event)
  				return eventName;
  
  			var opts = Object.create(_opts || {});
  			if (!opts.origin)
  				opts.origin = this;
  
  			return new root.Event(eventName, opts);
  		},
  		addListener: function(_eventName, listener, _opts) {
  			var eventName = getEventName.call(this, _eventName),
  					opts = _opts || {},
  					namespace = getNamespace.call(this, _eventName),
  					listeners = getListeners.call(this, eventName);
  
  			if (!eventName)
  				return this;
  
  			var e = this.createEvent("newListener", {proxy: false, async: false});
  			this.emit(e, listener);
  			if (e.preventDefault)	
  				return this;
  
  			listeners.push({
  				event: eventName,
  				listener: listener,
  				namespace: namespace,
  				order: listeners.length,
  				once: opts.once
  			});
  
  			return this;
  		},
  		removeListener: function(_eventName, _removeListeners) {
  			var removeListeners = _removeListeners || [],
  					eventName = getEventName.call(this, _eventName),
  					listeners = getListeners.call(this, eventName, true),
  					namespace = getNamespace.call(this, _eventName),
  					events = this._events;
  
  			if (!listeners || !events)
  				return this;
  
  			if (!(removeListeners instanceof Array))
  				removeListeners = [removeListeners];
  
  			var e = this.createEvent("removeListener", {proxy: false, async: false}),
  					newListeners = [];
  
  			for (var i = 0, il = listeners.length; i < il; i++) {
  				var listenerObj = listeners[i];
  
  				if (removeListeners.length > 0 && removeListeners.indexOf(listenerObj.listener) < 0) {
  					newListeners.push(listenerObj);
  					continue;
  				}
  
  				if (namespace && listenerObj.namespace !== namespace) {
  					newListeners.push(listenerObj);
  					continue;
  				}
  
  				e.halt = false;
  				e.preventDefault = false;
  				e.propagate = true;
  
  				this.emit(e, listenerObj.listener);
  				if (e.preventDefault === true) {
  					newListeners.push(listenerObj);
  					continue;
  				}
  			}
  
  			if (newListeners.length !== listeners.length) 
  				events._eventBindings[eventName] = newListeners;
  
  			return this;
  		},
  		removeAllListeners: function(event) {
  			var events = this._events;
  			if (!events)
  				return this;
  
  			if (arguments.length === 0) {
  				var bindings = events._eventBindings,
  						keys = Object.keys(bindings);
  
  				for (var i = 0, il = keys.length; i < il; i++)
  					this.removeListener(keys[i]);
  
  				events._eventBindings = {};
  				events._eventProxies = {};
  
  				return this;
  			}
  
  			return this.removeListener(event);
  		},
  		listeners: function(event) {
  			return getAllListeners.call(this, event, true);
  		},
  		on: function(_events, listener) {
  			var events = _events;
  			if (!events)
  				return this;
  
  			var isString = (typeof events === 'string' || events instanceof String);
  			if (isString || events instanceof Array) {
  				if (isString)
  					events = events.split(/\s+/g);
  
  				for (var i = 0, il = events.length; i < il; i++) {
  					var thisEvent = events[i];
  					this.addListener(thisEvent, listener);
  				}
  			} else if ((events instanceof Object) && !(events instanceof String)) {
  				var events = event,
  						keys = Object.keys(events);
  
  				for (var i = 0, il = keys.length; i < il; i++) {
  					var thisEvent = keys[i],
  							thisListener = events[key];
  
  					this.addListener(thisEvent, thisListener);
  				}
  			}
  
  			return this;
  		},
  		off: function(event, listener) {
  			return this.removeListener(event, listener);
  		},
  		once: function(event, listener) {
  			return this.addListener(event, listener, {once: true});
  		},
  		emit: function(event) {
  			if (arguments.length === 0)
  				return false;
  
  			var events = this._events;
  			if (!events)
  				return false;
  
  			var thisEvent = this.createEvent(event),
  					allListeners = getAllListeners.call(this, event, true);
  					
  			if (allListeners.length === 0)
  				return false;
  
  			var args = new Array(arguments.length - 1);
  			for (var i = 1, il = arguments.length; i < il; i++)
  				args[i - 1] = arguments[i];
  
  			var async = (thisEvent.async === undefined) ? events._async : thisEvent.async;
  			if (async) {
  				setImmediate(doEmit.bind(this, thisEvent, allListeners, args));
  			} else {
  				doEmit.call(this, thisEvent, allListeners, args);
  			}
  
  			return true;
  		},
  		proxy: function(dst, filter) {
  			var events = this._events;
  			if (!events)
  				return this;
  
  			if (!dst || !(dst['emit'] instanceof Function))
  				return this;
  
  			events._eventProxies.push({
  				dst: dst,
  				filter: filter
  			});
  
  			return this;
  		},
  		removeProxy: function(dst) {
  			var events = this._events;
  			if (!events)
  				return this;
  
  			var ep = events._eventProxies;
  			if (!ep)
  				return this;
  
  			var newProxies = [];
  			for (var i = 0, il = ep.length; i < il; i++) {
  				var thisEP = ep[i];
  				if (thisEP.dst === dst)
  					continue;
  
  				newProxies.push(thisEP);
  			}
  
  			if (newProxies.length !== ep.length)
  				events._eventProxies = newProxies;
  
  			return this;
  		}
  	};
  
  	var idCounter = 0;
  	root.EventEmitter = EventEmitter;
  
  	return root;
  });
  