1 /** 2 * @fileOverview EventObject 3 * @author lifesinger@gmail.com,yiminghe@gmail.com 4 */ 5 KISSY.add('event/object', function (S, undefined) { 6 7 var doc = S.Env.host.document, 8 TRUE = true, 9 FALSE = false, 10 props = ('altKey attrChange attrName bubbles button cancelable ' + 11 'charCode clientX clientY ctrlKey currentTarget data detail ' + 12 'eventPhase fromElement handler keyCode metaKey ' + 13 'newValue offsetX offsetY originalTarget pageX pageY prevValue ' + 14 'relatedNode relatedTarget screenX screenY shiftKey srcElement ' + 15 'target toElement view wheelDelta which axis').split(' '); 16 17 /** 18 * @class KISSY's event system normalizes the event object according to 19 * W3C standards. The event object is guaranteed to be passed to 20 * the event handler. Most properties from the original event are 21 * copied over and normalized to the new event object. 22 * @name Object 23 * @memberOf Event 24 */ 25 function EventObject(currentTarget, domEvent, type) { 26 var self = this; 27 self.originalEvent = domEvent || { }; 28 self.currentTarget = currentTarget; 29 if (domEvent) { // html element 30 self.type = domEvent.type; 31 // incase dom event has been mark as default prevented by lower dom node 32 self.isDefaultPrevented = ( domEvent['defaultPrevented'] || domEvent.returnValue === FALSE || 33 domEvent['getPreventDefault'] && domEvent['getPreventDefault']() ) ? TRUE : FALSE; 34 self._fix(); 35 } 36 else { // custom 37 self.type = type; 38 self.target = currentTarget; 39 } 40 // bug fix: in _fix() method, ie maybe reset currentTarget to undefined. 41 self.currentTarget = currentTarget; 42 self.fixed = TRUE; 43 } 44 45 S.augment(EventObject, 46 /** 47 * @lends Event.Object# 48 */ 49 { 50 51 /** 52 * Flag for preventDefault that is modified during fire event. if it is true, the default behavior for this event will be executed. 53 * @type Boolean 54 */ 55 isDefaultPrevented:FALSE, 56 /** 57 * Flag for stopPropagation that is modified during fire event. true means to stop propagation to bubble targets. 58 * @type Boolean 59 */ 60 isPropagationStopped:FALSE, 61 /** 62 * Flag for stopImmediatePropagation that is modified during fire event. true means to stop propagation to bubble targets and other listener. 63 * @type Boolean 64 */ 65 isImmediatePropagationStopped:FALSE, 66 67 _fix:function () { 68 var self = this, 69 originalEvent = self.originalEvent, 70 l = props.length, prop, 71 ct = self.currentTarget, 72 ownerDoc = (ct.nodeType === 9) ? ct : (ct.ownerDocument || doc); // support iframe 73 74 // clone properties of the original event object 75 while (l) { 76 prop = props[--l]; 77 self[prop] = originalEvent[prop]; 78 } 79 80 // fix target property, if necessary 81 if (!self.target) { 82 self.target = self.srcElement || ownerDoc; // srcElement might not be defined either 83 } 84 85 // check if target is a textnode (safari) 86 if (self.target.nodeType === 3) { 87 self.target = self.target.parentNode; 88 } 89 90 // add relatedTarget, if necessary 91 if (!self.relatedTarget && self.fromElement) { 92 self.relatedTarget = (self.fromElement === self.target) ? self.toElement : self.fromElement; 93 } 94 95 // calculate pageX/Y if missing and clientX/Y available 96 if (self.pageX === undefined && self.clientX !== undefined) { 97 var docEl = ownerDoc.documentElement, bd = ownerDoc.body; 98 self.pageX = self.clientX + (docEl && docEl.scrollLeft || bd && bd.scrollLeft || 0) - (docEl && docEl.clientLeft || bd && bd.clientLeft || 0); 99 self.pageY = self.clientY + (docEl && docEl.scrollTop || bd && bd.scrollTop || 0) - (docEl && docEl.clientTop || bd && bd.clientTop || 0); 100 } 101 102 // add which for key events 103 if (self.which === undefined) { 104 self.which = (self.charCode === undefined) ? self.keyCode : self.charCode; 105 } 106 107 // add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) 108 if (self.metaKey === undefined) { 109 self.metaKey = self.ctrlKey; 110 } 111 112 // add which for click: 1 === left; 2 === middle; 3 === right 113 // Note: button is not normalized, so don't use it 114 if (!self.which && self.button !== undefined) { 115 self.which = (self.button & 1 ? 1 : (self.button & 2 ? 3 : ( self.button & 4 ? 2 : 0))); 116 } 117 }, 118 119 /** 120 * Prevents the event's default behavior 121 */ 122 preventDefault:function () { 123 var e = this.originalEvent; 124 125 // if preventDefault exists run it on the original event 126 if (e.preventDefault) { 127 e.preventDefault(); 128 } 129 // otherwise set the returnValue property of the original event to FALSE (IE) 130 else { 131 e.returnValue = FALSE; 132 } 133 134 this.isDefaultPrevented = TRUE; 135 }, 136 137 /** 138 * Stops the propagation to the next bubble target 139 */ 140 stopPropagation:function () { 141 var e = this.originalEvent; 142 143 // if stopPropagation exists run it on the original event 144 if (e.stopPropagation) { 145 e.stopPropagation(); 146 } 147 // otherwise set the cancelBubble property of the original event to TRUE (IE) 148 else { 149 e.cancelBubble = TRUE; 150 } 151 152 this.isPropagationStopped = TRUE; 153 }, 154 155 156 /** 157 * Stops the propagation to the next bubble target and 158 * prevents any additional listeners from being exectued 159 * on the current target. 160 */ 161 stopImmediatePropagation:function () { 162 var self = this; 163 self.isImmediatePropagationStopped = TRUE; 164 // fixed 1.2 165 // call stopPropagation implicitly 166 self.stopPropagation(); 167 }, 168 169 /** 170 * Stops the event propagation and prevents the default 171 * event behavior. 172 * @param {Boolean} [immediate] if true additional listeners on the current target will not be executed 173 */ 174 halt:function (immediate) { 175 var self = this; 176 if (immediate) { 177 self.stopImmediatePropagation(); 178 } else { 179 self.stopPropagation(); 180 } 181 self.preventDefault(); 182 } 183 }); 184 185 return EventObject; 186 187 }); 188 189 /** 190 * NOTES: 191 * 192 * 2010.04 193 * - http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html 194 * 195 * TODO: 196 * - pageX, clientX, scrollLeft, clientLeft 的详细测试 197 */ 198