1 (function($) {
  2 	/** @exports $ as puredom */
  3 
  4 	/**	Creates a new EventEmitter instance.
  5 	 *	@class Fire events and listen for fired events.
  6 	 */
  7 	$.EventEmitter = function EventEmitter() {
  8 		this._eventRegistry = [];
  9 	};
 10 
 11 	var proto = $.EventEmitter.prototype;
 12 
 13 	function multi(inst, func, type, handler, collector) {
 14 		var o = typeof type,
 15 			t, i, ret;
 16 		if (o==='object' && type) {
 17 			for (i in type) {
 18 				if (type.hasOwnProperty(i)) {
 19 					ret = inst[func](i, type[i]);
 20 					if (collector) {
 21 						collector.push(ret);
 22 					}
 23 				}
 24 			}
 25 			return true;
 26 		}
 27 		if (o==='string' && type.indexOf(',')>-1) {
 28 			t = type.split(',');
 29 			for (i=0; i<t.length; i++) {
 30 				ret = inst[func](t[i], handler);
 31 				if (collector) {
 32 					collector.push(ret);
 33 				}
 34 			}
 35 			return true;
 36 		}
 37 		return false;
 38 	}
 39 
 40 	function normalizeType(type) {
 41 		return String(type).toLowerCase().replace(/(^on|\s+)/gim,'');
 42 	}
 43 
 44 	$.extend(proto, /** @lends puredom.EventEmitter# */ {
 45 
 46 		/** Register an event listener on the instance.
 47 		 *	@param {String} type		An event type, or a comma-seprated list of event types.
 48 		 *	@param {Function} handler	A function to call in response to events of the given type.
 49 		 *	@returns {this}
 50 		 */
 51 		on : function(type, handler) {
 52 			type = normalizeType(type);
 53 			if (!multi(this, 'on', type, handler)) {
 54 				this._eventRegistry.push({
 55 					type : type,
 56 					handler : handler
 57 				});
 58 			}
 59 			return this;
 60 		},
 61 
 62 
 63 		/**	A version of {@link puredom.EventEmitter#on .on()} that removes handlers once they are called.
 64 		 *	@see puredom.EventEmitter#on
 65 		 *	@param {String} type		An event type, or a comma-seprated list of event types.
 66 		 *	@param {Function} handler	A function to call in response to events of the given type.  Will only be called once.
 67 		 *	@returns {this}
 68 		 */
 69 		once : function(type, handler) {
 70 			type = normalizeType(type);
 71 			if (!multi(this, 'once', type, handler)) {
 72 				this.on(type, function onceProxy() {
 73 					this.removeListener(type, onceProxy);
 74 					return handler.apply(this, arguments);
 75 				});
 76 			}
 77 			return this;
 78 		},
 79 
 80 
 81 		/** Remove an event listener from the instance.
 82 		 *	@param {String} type		An event type, or a comma-seprated list of event types.
 83 		 *	@param {Function} handler	A reference to the handler, as was originally passed to {puredom.EventEmitter#addEventListener}.
 84 		 *	@returns {this}
 85 		 */
 86 		removeListener : function(type, handler) {
 87 			var x, r;
 88 			type = normalizeType(type);
 89 			if (!multi(this, 'removeListener', type, handler)) {
 90 				for (x=this._eventRegistry.length; x--; ) {
 91 					r = this._eventRegistry[x];
 92 					if (r.type===type && r.handler===handler) {
 93 						this._eventRegistry.splice(x, 1);
 94 						break;
 95 					}
 96 				}
 97 			}
 98 			return this;
 99 		},
100 
101 
102 		/** Fire an event of a given type. <br />
103 		 *	Pass a comma-separated list for <code>type</code> to fire multiple events at once.
104 		 *	@param {String} type	Event type, or a comma-seprated list of event types.
105 		 *	@param {Array} [args]	Arguments to pass to each handler. Non-Array values get auto-boxed into an Array.
106 		 *	@returns {Array} an Array of handler return values. The Array also has "truthy" and "falsey" properties indicating if any handlers returned <code>true</code> or <code>false</code>, respectively.
107 		 */
108 		emit : function(type, args) {
109 			var returns = [],
110 				x, r, rval;
111 			type = normalizeType(type);
112 			args = Array.prototype.slice.call(arguments, 1);
113 			if (multi(this, 'emit', type, args, returns)) {
114 				return Array.prototype.concat.apply([], returns);
115 			}
116 			for (x=this._eventRegistry.length; x--; ) {
117 				r = this._eventRegistry[x];
118 				if (r.type===type) {
119 					if (returns.length===0) {
120 						returns.falsy = returns.falsey = returns.truthy = true;
121 					}
122 					rval = r.handler.apply(this, args);
123 					returns.push(rval);
124 					if (rval===true) {
125 						returns.falsy = returns.falsey = false;
126 					}
127 					else if (rval===false) {
128 						returns.truthy = false;
129 					}
130 					if (rval===false) {
131 						break;
132 					}
133 				}
134 			}
135 			return returns;
136 		},
137 
138 		/**	Deprecated alternative version of {@link puredom.EventEmitter#emit emit()} that
139 		 *	accepts an Array of event parameters as the second argument.
140 		 *	@function
141 		 *	@private
142 		 *	@deprecated
143 		 */
144 		fireEvent : function(type, args) {
145 			return this.emit.apply(this, ([type]).concat(args));
146 		}
147 
148 	});
149 
150 
151 	$.forEach(/** @lends puredom.EventEmitter# */{
152 
153 		/**	Alias of {@link puredom.EventEmitter#on on()}
154 		 *	@function
155 		 *	@private
156 		 */
157 		addListener : 'on',
158 
159 		/**	Alias of {@link puredom.EventEmitter#on on()}
160 		 *	@function
161 		 *	@private
162 		 */
163 		addEventListener : 'on',
164 
165 		/**	Alias of {@link puredom.EventEmitter#removeListener removeListener()}
166 		 *	@function
167 		 *	@private
168 		 */
169 		removeEventListener : 'removeListener',
170 
171 		/**	Alias of {@link puredom.EventEmitter#emit emit()}
172 		 *	@function
173 		 *	@private
174 		 */
175 		trigger : 'emit'
176 
177 	}, function(alias, key) {
178 		proto[key] = proto[alias];
179 	});
180 
181 }(puredom));
182