1 /**
  2  * @fileOverview responsible for registering event
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("event/add", function (S, Event, DOM, Utils, EventObject, handle, _data, specials) {
  6     var simpleAdd = Utils.simpleAdd,
  7         isValidTarget = Utils.isValidTarget,
  8         isIdenticalHandler = Utils.isIdenticalHandler;
  9 
 10     /**
 11      * dom node need eventHandler attached to dom node
 12      */
 13     function addDomEvent(target, type, eventHandler, handlers, handleObj) {
 14         var special = specials[type] || {};
 15         // 第一次注册该事件,dom 节点才需要注册 dom 事件
 16         if (!handlers.length &&
 17             (!special.setup || special.setup.call(target) === false)) {
 18             simpleAdd(target, type, eventHandler)
 19         }
 20         if (special.add) {
 21             special.add.call(target, handleObj);
 22         }
 23     }
 24 
 25     /**
 26      * @exports Event as Event
 27      */
 28 
 29     S.mix(Event,
 30         /**
 31          * @lends Event
 32          */
 33         {
 34             // single type , single target , fixed native
 35             __add:function (isNativeTarget, target, type, fn, scope) {
 36                 var typedGroups = Utils.getTypedGroups(type);
 37                 type = typedGroups[0];
 38                 var groups = typedGroups[1],
 39                     eventDesc,
 40                     data,
 41                     s = specials[type],
 42                     // in case overwrite by delegateFix/onFix in specials events
 43                     // (mouseenter/leave,focusin/out)
 44                     originalType,
 45                     last,
 46                     selector;
 47                 if (S.isObject(fn)) {
 48                     last = fn.last;
 49                     scope = fn.scope;
 50                     data = fn.data;
 51                     selector = fn.selector;
 52                     // in case provided by clone
 53                     originalType = fn.originalType;
 54                     fn = fn.fn;
 55                     if (selector && !originalType) {
 56                         if (s && s['delegateFix']) {
 57                             originalType = type;
 58                             type = s['delegateFix'];
 59                         }
 60                     }
 61                 }
 62                 if (!selector && !originalType) {
 63                     // when on mouseenter , it's actually on mouseover , and handlers is saved with mouseover!
 64                     // TODO need evaluate!
 65                     if (s && s['onFix']) {
 66                         originalType = type;
 67                         type = s['onFix'];
 68                     }
 69                 }
 70                 // 不是有效的 target 或 参数不对
 71                 if (!type ||
 72                     !target ||
 73                     !S.isFunction(fn) ||
 74                     (isNativeTarget && !isValidTarget(target))) {
 75                     return;
 76                 }
 77                 // 获取事件描述
 78                 eventDesc = Event._data(target);
 79                 if (!eventDesc) {
 80                     _data._data(target, eventDesc = {});
 81                 }
 82                 //事件 listeners , similar to eventListeners in DOM3 Events
 83                 var events = eventDesc.events = eventDesc.events || {},
 84                     handlers = events[type] = events[type] || [],
 85                     handleObj = {
 86                         fn:fn,
 87                         scope:scope,
 88                         selector:selector,
 89                         last:last,
 90                         data:data,
 91                         groups:groups,
 92                         originalType:originalType
 93                     },
 94                     eventHandler = eventDesc.handler;
 95                 // 该元素没有 handler ,并且该元素是 dom 节点时才需要注册 dom 事件
 96                 if (!eventHandler) {
 97                     eventHandler = eventDesc.handler = function (event, data) {
 98                         // 是经过 fire 手动调用而浏览器同步触发导致的,就不要再次触发了,
 99                         // 已经在 fire 中 bubble 过一次了
100                         // incase after page has unloaded
101                         if (typeof KISSY == "undefined" ||
102                             event && event.type == Utils.Event_Triggered) {
103                             return;
104                         }
105                         var currentTarget = eventHandler.target, type;
106                         if (!event || !event.fixed) {
107                             event = new EventObject(currentTarget, event);
108                         }
109                         type = event.type;
110                         if (S.isPlainObject(data)) {
111                             S.mix(event, data);
112                         }
113                         // protect type
114                         if (type) {
115                             event.type = type;
116                         }
117                         return handle(currentTarget, event);
118                     };
119                     // as for native dom event , this represents currentTarget !
120                     eventHandler.target = target;
121                 }
122 
123                 for (var i = handlers.length - 1; i >= 0; --i) {
124                     /**
125                      * If multiple identical EventListeners are registered on the same EventTarget
126                      * with the same parameters the duplicate instances are discarded.
127                      * They do not cause the EventListener to be called twice
128                      * and since they are discarded
129                      * they do not need to be removed with the removeEventListener method.
130                      */
131                     if (isIdenticalHandler(handlers[i], handleObj, target)) {
132                         return;
133                     }
134                 }
135 
136                 if (isNativeTarget) {
137                     addDomEvent(target, type, eventHandler, handlers, handleObj);
138                     //nullify to prevent memory leak in ie ?
139                     target = null;
140                 }
141 
142                 // 增加 listener
143                 if (selector) {
144                     var delegateIndex = handlers.delegateCount
145                         = handlers.delegateCount || 0;
146                     handlers.splice(delegateIndex, 0, handleObj);
147                     handlers.delegateCount++;
148                 } else {
149                     handlers.lastCount = handlers.lastCount || 0;
150                     if (last) {
151                         handlers.push(handleObj);
152                         handlers.lastCount++;
153                     } else {
154                         handlers.splice(handlers.length - handlers.lastCount,
155                             0, handleObj);
156                     }
157                 }
158             },
159 
160             /**
161              * Adds an event listener.similar to addEventListener in DOM3 Events
162              * @param targets KISSY selector
163              * @param type {String} The type of event to append.use space to separate multiple event types.
164              * @param fn {Function} The event handler/listener.
165              * @param {Object} [scope] The scope (this reference) in which the handler function is executed.
166              */
167             add:function (targets, type, fn, scope) {
168                 type = S.trim(type);
169                 // data : 附加在回调后面的数据,delegate 检查使用
170                 // remove 时 data 相等(指向同一对象或者定义了 equals 比较函数)
171                 if (Utils.batchForType(Event.add, targets, type, fn, scope)) {
172                     return targets;
173                 }
174                 targets = DOM.query(targets);
175                 for (var i = targets.length - 1; i >= 0; i--) {
176                     Event.__add(true, targets[i], type, fn, scope);
177                 }
178                 return targets;
179             }
180         });
181 }, {
182     requires:['./base', 'dom', './utils', './object', './handle', './data', './special']
183 });