1 /**
  2  * Event
  3  *
  4  * Event api wrapper
  5  * @todo Add method for triggering events
  6  */
  7 (function (){
  8 
  9 	"use strict";
 10 
 11 	// Property name for expandos on DOM objects
 12 	var kis_expando = "KIS_0_6_0";
 13 
 14 	var _attach, _remove, _add_remove, e, _attach_delegate;
 15 
 16 	// Define the proper _attach and _remove functions
 17 	// based on browser support
 18 	if(typeof document.addEventListener !== "undefined")
 19 	{
 20 		/**
 21 		 * @private
 22 		 */
 23 		_attach = function (sel, event, callback)
 24 		{
 25 			if(typeof sel.addEventListener !== "undefined")
 26 			{
 27 				// Duplicated events are dropped, per the specification
 28 				sel.addEventListener(event, callback, false);
 29 			}
 30 		};
 31 		/**
 32 		 * @private
 33 		 */
 34 		_remove = function (sel, event, callback)
 35 		{
 36 			if(typeof sel.removeEventListener !== "undefined")
 37 			{
 38 				sel.removeEventListener(event, callback, false);
 39 			}
 40 		};
 41 	}
 42 	// typeof function doesn't work in IE where attachEvent is available: brute force it
 43 	else if(typeof document.attachEvent !== "undefined")
 44 	{
 45 		/**
 46 		 * @private
 47 		 */
 48 		_attach = function (sel, event, callback)
 49 		{
 50 			function _listener () {
 51 				// Internet Explorer fails to correctly set the 'this' object
 52 				// for event listeners, so we need to set it ourselves.
 53 				callback.apply(arguments[0]);
 54 			}
 55 
 56 			if (typeof sel.attachEvent !== "undefined")
 57 			{
 58 				_remove(event, callback); // Make sure we don't have duplicate listeners
 59 
 60 				sel.attachEvent("on" + event, _listener);
 61 				// Store our listener so we can remove it later
 62 				var expando = sel[kis_expando] = sel[kis_expando] || {};
 63 				expando.listeners = expando.listeners || {};
 64 				expando.listeners[event] = expando.listeners[event] || [];
 65 				expando.listeners[event].push({
 66 					callback: callback,
 67 					_listener: _listener
 68 				});
 69 			}
 70 			else
 71 			{
 72 				console.log("Failed to _attach event:"+event+" on "+sel);
 73 			}
 74 		};
 75 		/**
 76 		 * @private
 77 		 */
 78 		_remove = function (sel, event, callback)
 79 		{
 80 			if(typeof sel.detachEvent !== "undefined")
 81 			{
 82 				var expando = sel[kis_expando];
 83 				if (expando && expando.listeners
 84 						&& expando.listeners[event])
 85 				{
 86 					var listeners = expando.listeners[event];
 87 					var len = listeners.length;
 88 					for (var i=0; i<len; i++)
 89 					{
 90 						if (listeners[i].callback === callback)
 91 						{
 92 							sel.detachEvent("on" + event, listeners[i]._listener);
 93 							listeners.splice(i, 1);
 94 							if(listeners.length === 0)
 95 							{
 96 								delete expando.listeners[event];
 97 							}
 98 							return;
 99 						}
100 					}
101 				}
102 			}
103 		};
104 	}
105 
106 	_add_remove = function (sel, event, callback, add)
107 	{
108 		var i, len;
109 
110 		if(typeof sel === "undefined")
111 		{
112 			console.log(arguments);
113 			console.log(event);
114 			return false;
115 		}
116 
117 		// Multiple events? Run recursively!
118 		if ( ! event.match(/^([\w\-]+)$/))
119 		{
120 			event = event.split(" ");
121 
122 			len = event.length;
123 
124 			for (i = 0; i < len; i++)
125 			{
126 				_add_remove(sel, event[i], callback, add);
127 			}
128 
129 			return;
130 		}
131 
132 
133 		if(add === true)
134 		{
135 			_attach(sel, event, callback);
136 		}
137 		else
138 		{
139 			_remove(sel, event, callback);
140 		}
141 	};
142 
143 	_attach_delegate = function(sel, target, event, callback)
144 	{
145 		// attach the listener to the parent object
146 		_add_remove(sel, event, function(e){
147 
148 			var elem, t, tar;
149 
150 			// IE 8 doesn't have event bound to element
151 			e = e || window.event;
152 
153 			// Get the live version of the target selector
154 			t = $_.$(target, sel);
155 
156 			// Check each element to see if it matches the target
157 			for(elem in t)
158 			{
159 				// IE 8 doesn't have target in the event object
160 				tar = e.target || e.srcElement;
161 
162 				// Fire target callback when event bubbles from target
163 				if(tar == t[elem])
164 				{
165 					// Trigger the event callback
166 					callback.call(t[elem], e);
167 
168 					// Stop event propegation
169 					e.stopPropagation();
170 				}
171 			}
172 
173 		}, true);
174 	};
175 
176 	// --------------------------------------------------------------------------
177 
178 	/**
179 	 * Event Listener module
180 	 *
181 	 * @namespace
182 	 * @name event
183 	 * @memberOf $_
184 	 */
185 	e = {
186 		/**
187 		 * Adds an event that returns a callback when triggered on the selected
188 		 * event and selector
189 		 *
190 		 * @memberOf $_.event
191 		 * @name add
192 		 * @function
193 		 * @example Eg. $_("#selector").event.add("click", do_something());
194 		 * @param string event
195 		 * @param function callback
196 		 */
197 		add: function (event, callback)
198 		{
199 			$_.each(function(e){
200 				_add_remove(e, event, callback, true);
201 			});
202 		},
203 		/**
204 		 * Removes an event bound the the specified selector, event type, and callback
205 		 *
206 		 * @memberOf $_.event
207 		 * @name remove
208 		 * @function
209 		 * @example Eg. $_("#selector").event.remove("click", do_something());
210 		 * @param string event
211 		 * @param string callback
212 		 */
213 		remove: function (event, callback)
214 		{
215 			$_.each(function(e){
216 				_add_remove(e, event, callback, false);
217 			});
218 		},
219 		/**
220 		 * Binds a persistent event to the document
221 		 *
222 		 * @memberOf $_.event
223 		 * @name live
224 		 * @function
225 		 * @example Eg. $_.event.live(".button", "click", do_something());
226 		 * @param string target
227 		 * @param string event
228 		 * @param function callback
229 		 */
230 		live: function (target, event, callback)
231 		{
232 			_attach_delegate(document.documentElement, target, event, callback);
233 		},
234 		/**
235 		 * Binds an event to a parent object
236 		 *
237 		 * @memberOf $_.event
238 		 * @name delegate
239 		 * @function
240 		 * @example Eg. $_("#parent").delegate(".button", "click", do_something());
241 		 * @param string target
242 		 * @param string event_type
243 		 * @param function callback
244 		 */
245 		delegate: function (target, event, callback)
246 		{
247 			$_.each(function(e){
248 				_attach_delegate(e, target, event, callback);
249 			});
250 		}
251 	};
252 
253 	$_.ext('event', e);
254 
255 }());