| /** | 
|  * Event Mixin | 
|  * @module zrender/mixin/Eventful | 
|  * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) | 
|  *         pissang (https://www.github.com/pissang) | 
|  */ | 
| var arrySlice = Array.prototype.slice; | 
| /** | 
|  * Event dispatcher. | 
|  * | 
|  * @alias module:zrender/mixin/Eventful | 
|  * @constructor | 
|  * @param {Object} [eventProcessor] The object eventProcessor is the scope when | 
|  *        `eventProcessor.xxx` called. | 
|  * @param {Function} [eventProcessor.normalizeQuery] | 
|  *        param: {string|Object} Raw query. | 
|  *        return: {string|Object} Normalized query. | 
|  * @param {Function} [eventProcessor.filter] Event will be dispatched only | 
|  *        if it returns `true`. | 
|  *        param: {string} eventType | 
|  *        param: {string|Object} query | 
|  *        return: {boolean} | 
|  * @param {Function} [eventProcessor.afterTrigger] Called after all handlers called. | 
|  *        param: {string} eventType | 
|  */ | 
|   | 
| var Eventful = function (eventProcessor) { | 
|   this._$handlers = {}; | 
|   this._$eventProcessor = eventProcessor; | 
| }; | 
|   | 
| Eventful.prototype = { | 
|   constructor: Eventful, | 
|   | 
|   /** | 
|    * The handler can only be triggered once, then removed. | 
|    * | 
|    * @param {string} event The event name. | 
|    * @param {string|Object} [query] Condition used on event filter. | 
|    * @param {Function} handler The event handler. | 
|    * @param {Object} context | 
|    */ | 
|   one: function (event, query, handler, context) { | 
|     return on(this, event, query, handler, context, true); | 
|   }, | 
|   | 
|   /** | 
|    * Bind a handler. | 
|    * | 
|    * @param {string} event The event name. | 
|    * @param {string|Object} [query] Condition used on event filter. | 
|    * @param {Function} handler The event handler. | 
|    * @param {Object} [context] | 
|    */ | 
|   on: function (event, query, handler, context) { | 
|     return on(this, event, query, handler, context, false); | 
|   }, | 
|   | 
|   /** | 
|    * Whether any handler has bound. | 
|    * | 
|    * @param  {string}  event | 
|    * @return {boolean} | 
|    */ | 
|   isSilent: function (event) { | 
|     var _h = this._$handlers; | 
|     return !_h[event] || !_h[event].length; | 
|   }, | 
|   | 
|   /** | 
|    * Unbind a event. | 
|    * | 
|    * @param {string} [event] The event name. | 
|    *        If no `event` input, "off" all listeners. | 
|    * @param {Function} [handler] The event handler. | 
|    *        If no `handler` input, "off" all listeners of the `event`. | 
|    */ | 
|   off: function (event, handler) { | 
|     var _h = this._$handlers; | 
|   | 
|     if (!event) { | 
|       this._$handlers = {}; | 
|       return this; | 
|     } | 
|   | 
|     if (handler) { | 
|       if (_h[event]) { | 
|         var newList = []; | 
|   | 
|         for (var i = 0, l = _h[event].length; i < l; i++) { | 
|           if (_h[event][i].h !== handler) { | 
|             newList.push(_h[event][i]); | 
|           } | 
|         } | 
|   | 
|         _h[event] = newList; | 
|       } | 
|   | 
|       if (_h[event] && _h[event].length === 0) { | 
|         delete _h[event]; | 
|       } | 
|     } else { | 
|       delete _h[event]; | 
|     } | 
|   | 
|     return this; | 
|   }, | 
|   | 
|   /** | 
|    * Dispatch a event. | 
|    * | 
|    * @param {string} type The event name. | 
|    */ | 
|   trigger: function (type) { | 
|     var _h = this._$handlers[type]; | 
|     var eventProcessor = this._$eventProcessor; | 
|   | 
|     if (_h) { | 
|       var args = arguments; | 
|       var argLen = args.length; | 
|   | 
|       if (argLen > 3) { | 
|         args = arrySlice.call(args, 1); | 
|       } | 
|   | 
|       var len = _h.length; | 
|   | 
|       for (var i = 0; i < len;) { | 
|         var hItem = _h[i]; | 
|   | 
|         if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) { | 
|           i++; | 
|           continue; | 
|         } // Optimize advise from backbone | 
|   | 
|   | 
|         switch (argLen) { | 
|           case 1: | 
|             hItem.h.call(hItem.ctx); | 
|             break; | 
|   | 
|           case 2: | 
|             hItem.h.call(hItem.ctx, args[1]); | 
|             break; | 
|   | 
|           case 3: | 
|             hItem.h.call(hItem.ctx, args[1], args[2]); | 
|             break; | 
|   | 
|           default: | 
|             // have more than 2 given arguments | 
|             hItem.h.apply(hItem.ctx, args); | 
|             break; | 
|         } | 
|   | 
|         if (hItem.one) { | 
|           _h.splice(i, 1); | 
|   | 
|           len--; | 
|         } else { | 
|           i++; | 
|         } | 
|       } | 
|     } | 
|   | 
|     eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type); | 
|     return this; | 
|   }, | 
|   | 
|   /** | 
|    * Dispatch a event with context, which is specified at the last parameter. | 
|    * | 
|    * @param {string} type The event name. | 
|    */ | 
|   triggerWithContext: function (type) { | 
|     var _h = this._$handlers[type]; | 
|     var eventProcessor = this._$eventProcessor; | 
|   | 
|     if (_h) { | 
|       var args = arguments; | 
|       var argLen = args.length; | 
|   | 
|       if (argLen > 4) { | 
|         args = arrySlice.call(args, 1, args.length - 1); | 
|       } | 
|   | 
|       var ctx = args[args.length - 1]; | 
|       var len = _h.length; | 
|   | 
|       for (var i = 0; i < len;) { | 
|         var hItem = _h[i]; | 
|   | 
|         if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) { | 
|           i++; | 
|           continue; | 
|         } // Optimize advise from backbone | 
|   | 
|   | 
|         switch (argLen) { | 
|           case 1: | 
|             hItem.h.call(ctx); | 
|             break; | 
|   | 
|           case 2: | 
|             hItem.h.call(ctx, args[1]); | 
|             break; | 
|   | 
|           case 3: | 
|             hItem.h.call(ctx, args[1], args[2]); | 
|             break; | 
|   | 
|           default: | 
|             // have more than 2 given arguments | 
|             hItem.h.apply(ctx, args); | 
|             break; | 
|         } | 
|   | 
|         if (hItem.one) { | 
|           _h.splice(i, 1); | 
|   | 
|           len--; | 
|         } else { | 
|           i++; | 
|         } | 
|       } | 
|     } | 
|   | 
|     eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type); | 
|     return this; | 
|   } | 
| }; | 
|   | 
| function normalizeQuery(host, query) { | 
|   var eventProcessor = host._$eventProcessor; | 
|   | 
|   if (query != null && eventProcessor && eventProcessor.normalizeQuery) { | 
|     query = eventProcessor.normalizeQuery(query); | 
|   } | 
|   | 
|   return query; | 
| } | 
|   | 
| function on(eventful, event, query, handler, context, isOnce) { | 
|   var _h = eventful._$handlers; | 
|   | 
|   if (typeof query === 'function') { | 
|     context = handler; | 
|     handler = query; | 
|     query = null; | 
|   } | 
|   | 
|   if (!handler || !event) { | 
|     return eventful; | 
|   } | 
|   | 
|   query = normalizeQuery(eventful, query); | 
|   | 
|   if (!_h[event]) { | 
|     _h[event] = []; | 
|   } | 
|   | 
|   for (var i = 0; i < _h[event].length; i++) { | 
|     if (_h[event][i].h === handler) { | 
|       return eventful; | 
|     } | 
|   } | 
|   | 
|   var wrap = { | 
|     h: handler, | 
|     one: isOnce, | 
|     query: query, | 
|     ctx: context || eventful, | 
|     // FIXME | 
|     // Do not publish this feature util it is proved that it makes sense. | 
|     callAtLast: handler.zrEventfulCallAtLast | 
|   }; | 
|   var lastIndex = _h[event].length - 1; | 
|   var lastWrap = _h[event][lastIndex]; | 
|   lastWrap && lastWrap.callAtLast ? _h[event].splice(lastIndex, 0, wrap) : _h[event].push(wrap); | 
|   return eventful; | 
| } // ---------------------- | 
| // The events in zrender | 
| // ---------------------- | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onclick | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmouseover | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmouseout | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmousemove | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmousewheel | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmousedown | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#onmouseup | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondrag | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondragstart | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondragend | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondragenter | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondragleave | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondragover | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
| /** | 
|  * @event module:zrender/mixin/Eventful#ondrop | 
|  * @type {Function} | 
|  * @default null | 
|  */ | 
|   | 
|   | 
| var _default = Eventful; | 
| module.exports = _default; |