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;
148 			
149 			//Get the live version of the target selector
150 			t = $_.$(target);
151 			
152 			//Check each element to see if it matches the target
153 			for(elem in t)
154 			{
155 				//Fire target callback when event bubbles from target
156 				if(e.target == t[elem])
157 				{
158 					//Trigger the event callback
159 					callback.call(t[elem], e);
160 					
161 					//Stop event propegation
162 					e.stopPropagation();
163 				}
164 			}
165 			
166 			
167 		}, true);
168 	};
169 	
170 	
171 	
172 	// --------------------------------------------------------------------------
173 
174 	/**
175 	 * Event Listener module
176 	 *
177 	 * @namespace
178 	 * @name event
179 	 * @memberOf $_
180 	 */
181 	e = {
182 		/**
183 		 * Adds an event that returns a callback when triggered on the selected
184 		 * event and selector
185 		 * 
186 		 * @memberOf $_.event
187 		 * @name add
188 		 * @function
189 		 * @example Eg. $_("#selector").event.add("click", do_something());
190 		 * @param string event
191 		 * @param function callback
192 		 */
193 		add: function (event, callback)
194 		{
195 			$_.each(function(e){
196 				_add_remove(e, event, callback, true);
197 			});
198 		},
199 		/**
200 		 * Removes an event bound the the specified selector, event type, and callback
201 		 *
202 		 * @memberOf $_.event
203 		 * @name remove
204 		 * @function
205 		 * @example Eg. $_("#selector").event.remove("click", do_something());
206 		 * @param string event
207 		 * @param string callback
208 		 */
209 		remove: function (event, callback)
210 		{
211 			$_.each(function(e){
212 				_add_remove(e, event, callback, false);
213 			});
214 		},
215 		/** 
216 		 * Binds a persistent event to the document
217 		 *
218 		 * @memberOf $_.event
219 		 * @name live
220 		 * @function
221 		 * @example Eg. $_.event.live(".button", "click", do_something());
222 		 * @param string target
223 		 * @param string event
224 		 * @param function callback
225 		 */
226 		live: function (target, event, callback)
227 		{
228 			_attach_delegate(document.documentElement, target, event, callback);
229 		},
230 		/** 
231 		 * Binds an event to a parent object
232 		 *
233 		 * @memberOf $_.event
234 		 * @name delegate
235 		 * @function
236 		 * @example Eg. $_("#parent").delegate(".button", "click", do_something());
237 		 * @param string target
238 		 * @param string event_type
239 		 * @param function callback
240 		 */
241 		delegate: function(target, event, callback)
242 		{
243 			$_.each(function(e){
244 				_attach_delegate(e, target, event, callback);
245 			});
246 		}
247 	};
248 
249 	$_.ext('event', e);
250 
251 }());