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 **/