1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @language=en 9 * @class EventMixin is a mixin on event related functions. Use Class.mix(target, EventMixin) to add event function onto target. 10 * @mixin 11 * @static 12 * @module hilo/event/EventMixin 13 * @requires hilo/core/Class 14 */ 15 var EventMixin = { 16 _listeners: null, 17 18 /** 19 * @language=en 20 * Add an event listenser. 21 * @param {String} type Event type to listen. 22 * @param {Function} listener Callback function of event listening. 23 * @param {Boolean} once Listen on event only once and no more response after the first response? 24 * @returns {Object} The Event itself. Functions chain call supported. 25 */ 26 on: function(type, listener, once){ 27 var listeners = (this._listeners = this._listeners || {}); 28 var eventListeners = (listeners[type] = listeners[type] || []); 29 for(var i = 0, len = eventListeners.length; i < len; i++){ 30 var el = eventListeners[i]; 31 if(el.listener === listener) return; 32 } 33 eventListeners.push({listener:listener, once:once}); 34 return this; 35 }, 36 37 /** 38 * @language=en 39 * Remove one event listener. Remove all event listeners if no parameter provided, and remove all event listeners on one type which is provided as the only parameter. 40 * @param {String} type The type of event listener that want to remove. 41 * @param {Function} listener Event listener callback function to be removed. 42 * @returns {Object} The Event itself. Functions chain call supported. 43 */ 44 off: function(type, listener){ 45 //remove all event listeners 46 if(arguments.length == 0){ 47 this._listeners = null; 48 return this; 49 } 50 51 var eventListeners = this._listeners && this._listeners[type]; 52 if(eventListeners){ 53 //remove event listeners by specified type 54 if(arguments.length == 1){ 55 delete this._listeners[type]; 56 return this; 57 } 58 59 for(var i = 0, len = eventListeners.length; i < len; i++){ 60 var el = eventListeners[i]; 61 if(el.listener === listener){ 62 eventListeners.splice(i, 1); 63 if(eventListeners.length === 0) delete this._listeners[type]; 64 break; 65 } 66 } 67 } 68 return this; 69 }, 70 71 /** 72 * @language=en 73 * Send events. If the first parameter is an Object, take it as an Event Object. 74 * @param {String} type Event type to send. 75 * @param {Object} detail The detail (parameters go with the event) of Event to send. 76 * @returns {Boolean} Whether Event call successfully. 77 */ 78 fire: function(type, detail){ 79 var event, eventType; 80 if(typeof type === 'string'){ 81 eventType = type; 82 }else{ 83 event = type; 84 eventType = type.type; 85 } 86 87 var listeners = this._listeners; 88 if(!listeners) return false; 89 90 var eventListeners = listeners[eventType]; 91 if(eventListeners){ 92 var eventListenersCopy = eventListeners.slice(0); 93 event = event || new EventObject(eventType, this, detail); 94 if(event._stopped) return false; 95 96 for(var i = 0; i < eventListenersCopy.length; i++){ 97 var el = eventListenersCopy[i]; 98 el.listener.call(this, event); 99 if(el.once) { 100 var index = eventListeners.indexOf(el); 101 if(index > -1){ 102 eventListeners.splice(index, 1); 103 } 104 } 105 } 106 107 if(eventListeners.length == 0) delete listeners[eventType]; 108 return true; 109 } 110 return false; 111 } 112 }; 113 114 /** 115 * @language=en 116 * Event Object class. It's an private class now, but maybe will become a public class if needed. 117 */ 118 var EventObject = Class.create({ 119 constructor: function EventObject(type, target, detail){ 120 this.type = type; 121 this.target = target; 122 this.detail = detail; 123 this.timeStamp = +new Date(); 124 }, 125 126 type: null, 127 target: null, 128 detail: null, 129 timeStamp: 0, 130 131 stopImmediatePropagation: function(){ 132 this._stopped = true; 133 } 134 }); 135 136 //Trick: `stopImmediatePropagation` compatibility 137 var RawEvent = window.Event; 138 if(RawEvent){ 139 var proto = RawEvent.prototype, 140 stop = proto.stopImmediatePropagation; 141 proto.stopImmediatePropagation = function(){ 142 stop && stop.call(this); 143 this._stopped = true; 144 } 145 } 146