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