1 /**
  2  * @fileOverview 提供事件发布和订阅机制
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add('event/target', function (S, Event, EventObject, Utils, handle, undefined) {
  6     var KS_PUBLISH = "__~ks_publish",
  7         trim = S.trim,
  8         splitAndRun = Utils.splitAndRun,
  9         KS_BUBBLE_TARGETS = "__~ks_bubble_targets",
 10         ALL_EVENT = "*";
 11 
 12     function getCustomEvent(self, type, eventData) {
 13         if (eventData instanceof EventObject) {
 14             // set currentTarget in the process of bubbling
 15             eventData.currentTarget = self;
 16             return eventData;
 17         }
 18         var customEvent = new EventObject(self, undefined, type);
 19         S.mix(customEvent, eventData);
 20         return customEvent
 21     }
 22 
 23     function getEventPublishObj(self) {
 24         self[KS_PUBLISH] = self[KS_PUBLISH] || {};
 25         return self[KS_PUBLISH];
 26     }
 27 
 28     function getBubbleTargetsObj(self) {
 29         self[KS_BUBBLE_TARGETS] = self[KS_BUBBLE_TARGETS] || {};
 30         return self[KS_BUBBLE_TARGETS];
 31     }
 32 
 33     function isBubblable(self, eventType) {
 34         var publish = getEventPublishObj(self);
 35         return publish[eventType] && publish[eventType].bubbles || publish[ALL_EVENT] && publish[ALL_EVENT].bubbles
 36     }
 37 
 38     function attach(method) {
 39         return function (type, fn, scope) {
 40             var self = this;
 41             type = trim(type);
 42             splitAndRun(type, function (t) {
 43                 Event["__" + method](false, self, t, fn, scope);
 44             });
 45             return self; // chain
 46         };
 47     }
 48 
 49     /**
 50      * @namespace
 51      * EventTarget provides the implementation for any object to publish, subscribe and fire to custom events,
 52      * and also allows other EventTargets to target the object with events sourced from the other object.
 53      * EventTarget is designed to be used with S.augment to allow events to be listened to and fired by name.
 54      * This makes it possible for implementing code to subscribe to an event that either has not been created yet,
 55      * or will not be created at all.
 56      * @name Target
 57      * @memberOf Event
 58      */
 59     var Target =
 60     /**
 61      * @lends Event.Target
 62      */
 63     {
 64         /**
 65          * Fire a custom event by name.
 66          * The callback functions will be executed from the context specified when the event was created,
 67          * and the {@link Event.Object} created will be mixed with eventData
 68          * @param {String} type The type of the event
 69          * @param {Object} [eventData] The data will be mixed with {@link Event.Object} created
 70          * @returns {Boolean|*} If any listen returns false, then the returned value is false. else return the last listener's returned value
 71          */
 72         fire:function (type, eventData) {
 73             var self = this,
 74                 ret = undefined,
 75                 r2,
 76                 customEvent;
 77             eventData = eventData || {};
 78             type = trim(type);
 79             if (type.indexOf(" ") > 0) {
 80                 splitAndRun(type, function (t) {
 81                     r2 = self.fire(t, eventData);
 82                     if (ret !== false) {
 83                         ret = r2;
 84                     }
 85                 });
 86                 return ret;
 87             }
 88             var typedGroups = Utils.getTypedGroups(type), _ks_groups = typedGroups[1];
 89             type = typedGroups[0];
 90             if (_ks_groups) {
 91                 _ks_groups = Utils.getGroupsRe(_ks_groups);
 92             }
 93             S.mix(eventData, {
 94                 // protect type
 95                 type:type,
 96                 _ks_groups:_ks_groups
 97             });
 98             customEvent = getCustomEvent(self, type, eventData);
 99             ret = handle(self, customEvent);
100             if (!customEvent.isPropagationStopped &&
101                 isBubblable(self, type)) {
102                 r2 = self.bubble(type, customEvent);
103                 // false 优先返回
104                 if (ret !== false) {
105                     ret = r2;
106                 }
107             }
108             return ret
109         },
110 
111         /**
112          * Creates a new custom event of the specified type
113          * @param {String} type The type of the event
114          * @param {Object} cfg Config params
115          * @param {Boolean} [cfg.bubbles=false] whether or not this event bubbles
116          */
117         publish:function (type, cfg) {
118             var self = this,
119                 publish = getEventPublishObj(self);
120             type = trim(type);
121             if (type) {
122                 splitAndRun(type, function (t) {
123                     publish[t] = cfg;
124                 });
125             }
126         },
127 
128         /**
129          * bubble event to its targets
130          * @param type
131          * @param eventData
132          * @private
133          */
134         bubble:function (type, eventData) {
135             var self = this,
136                 ret = undefined,
137                 targets = getBubbleTargetsObj(self);
138             S.each(targets, function (t) {
139                 var r2 = t.fire(type, eventData);
140                 if (ret !== false) {
141                     ret = r2;
142                 }
143             });
144             return ret;
145         },
146 
147         /**
148          * Registers another EventTarget as a bubble target.
149          * @param {Event.Target} target Another EventTarget instance to add
150          */
151         addTarget:function (target) {
152             var self = this,
153                 targets = getBubbleTargetsObj(self);
154             targets[S.stamp(target)] = target;
155         },
156 
157         /**
158          * Removes a bubble target
159          * @param {Event.Target} target Another EventTarget instance to remove
160          */
161         removeTarget:function (target) {
162             var self = this,
163                 targets = getBubbleTargetsObj(self);
164             delete targets[S.stamp(target)];
165         },
166 
167         /**
168          * Subscribe a callback function to a custom event fired by this object or from an object that bubbles its events to this object.
169          * @function
170          * @param {String} type The name of the event
171          * @param {Function} fn The callback to execute in response to the event
172          * @param {Object} [scope] this object in callback
173          */
174         on:attach("add"),
175         /**
176          * Detach one or more listeners the from the specified event
177          * @function
178          * @param {String} type The name of the event
179          * @param {Function} [fn] The subscribed function to unsubscribe. if not supplied, all subscribers will be removed.
180          * @param {Object} [scope] The custom object passed to subscribe.
181          */
182         detach:attach("remove")
183     };
184 
185     return Target;
186 }, {
187     requires:["./base", './object', './utils', './handle']
188 });
189 /**
190  *  yiminghe:2011-10-17
191  *   - implement bubble for custom event
192  **/