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