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