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 }; 71 /** 72 * @private 73 */ 74 _remove = function (sel, event, callback) 75 { 76 if(typeof sel.detachEvent !== "undefined") 77 { 78 var expando = sel[kis_expando]; 79 if (expando && expando.listeners 80 && expando.listeners[event]) 81 { 82 var listeners = expando.listeners[event]; 83 var len = listeners.length; 84 for (var i=0; i<len; i++) 85 { 86 if (listeners[i].callback === callback) 87 { 88 sel.detachEvent("on" + event, listeners[i]._listener); 89 listeners.splice(i, 1); 90 if(listeners.length === 0) 91 { 92 delete expando.listeners[event]; 93 } 94 return; 95 } 96 } 97 } 98 } 99 }; 100 } 101 102 _add_remove = function (sel, event, callback, add) 103 { 104 var i, len; 105 106 if(typeof sel === "undefined") 107 { 108 return null; 109 } 110 111 // Multiple events? Run recursively! 112 if ( ! event.match(/^([\w\-]+)$/)) 113 { 114 event = event.split(" "); 115 116 len = event.length; 117 118 for (i = 0; i < len; i++) 119 { 120 _add_remove(sel, event[i], callback, add); 121 } 122 123 return; 124 } 125 126 127 if(add === true) 128 { 129 _attach(sel, event, callback); 130 } 131 else 132 { 133 _remove(sel, event, callback); 134 } 135 }; 136 137 _attach_delegate = function(sel, target, event, callback) 138 { 139 // attach the listener to the parent object 140 _add_remove(sel, event, function(e){ 141 142 var elem, t, tar; 143 144 // IE 8 doesn't have event bound to element 145 e = e || window.event; 146 147 // Get the live version of the target selector 148 t = $_.$(target, sel); 149 150 // Check each element to see if it matches the target 151 for(elem in t) 152 { 153 // IE 8 doesn't have target in the event object 154 tar = e.target || e.srcElement; 155 156 // Fire target callback when event bubbles from target 157 if(tar == t[elem]) 158 { 159 // Trigger the event callback 160 callback.call(t[elem], e); 161 162 // Stop event propegation 163 e.stopPropagation(); 164 } 165 } 166 167 }, true); 168 }; 169 170 // -------------------------------------------------------------------------- 171 172 /** 173 * Event Listener module 174 * 175 * @namespace 176 * @name event 177 * @memberOf $_ 178 */ 179 e = { 180 /** 181 * Adds an event that returns a callback when triggered on the selected 182 * event and selector 183 * 184 * @memberOf $_.event 185 * @name add 186 * @function 187 * @example Eg. $_("#selector").event.add("click", do_something()); 188 * @param string event 189 * @param function callback 190 */ 191 add: function (event, callback) 192 { 193 $_.each(function(e){ 194 _add_remove(e, event, callback, true); 195 }); 196 }, 197 /** 198 * Removes an event bound the the specified selector, event type, and callback 199 * 200 * @memberOf $_.event 201 * @name remove 202 * @function 203 * @example Eg. $_("#selector").event.remove("click", do_something()); 204 * @param string event 205 * @param string callback 206 */ 207 remove: function (event, callback) 208 { 209 $_.each(function(e){ 210 _add_remove(e, event, callback, false); 211 }); 212 }, 213 /** 214 * Binds a persistent event to the document 215 * 216 * @memberOf $_.event 217 * @name live 218 * @function 219 * @example Eg. $_.event.live(".button", "click", do_something()); 220 * @param string target 221 * @param string event 222 * @param function callback 223 */ 224 live: function (target, event, callback) 225 { 226 _attach_delegate(document.documentElement, target, event, callback); 227 }, 228 /** 229 * Binds an event to a parent object 230 * 231 * @memberOf $_.event 232 * @name delegate 233 * @function 234 * @example Eg. $_("#parent").delegate(".button", "click", do_something()); 235 * @param string target 236 * @param string event_type 237 * @param function callback 238 */ 239 delegate: function (target, event, callback) 240 { 241 $_.each(function(e){ 242 _attach_delegate(e, target, event, callback); 243 }); 244 } 245 }; 246 247 $_.ext('event', e); 248 249 }());