blob: 8a841a10187c0ec46e6329c3788b9d591b0e604b [file] [log] [blame]
/**
* 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
*/
export default Eventful;