mora-scripts
Version:
Some collection scripts by myself
141 lines (129 loc) • 4.11 kB
JavaScript
/**
*
* @module libs/lang/Events
* @createdAt 2017-03-01
*
* @copyright Copyright (c) 2017 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
/**
* @class
* @example 直接使用
* var e = new Events()
* e.on('click', function () { ... })
* e.emit('click', 1, 2, 3)
*
* @example 嫁接到函数或类上
* function Foo() { ... }
* Events.mixin(Foo)
* var f = new Foo()
* f.on('click', ...)
* f.emit('click', ...)
*
* @example 嫁接到一个对象上
* var obj = {a: 'aa'}
* Events.mixin(obj)
* obj.on('click', ...)
* obj.emit('click', ...)
*/
function Events() {
this._events = {}
}
/**
* @param {string} events 事件名称,类似于 jQuery,如果有多个要用空格隔开,也可以添加 namespace
* @param {*} [data]
* @param {function} handler
* @param {boolean} [once]
*/
Events.prototype.on = function(events, data, handler, once) {
var store = this._events
if (typeof data === 'function') {
once = handler
handler = data
data = void 0
}
parseEventsString(events).forEach(function(event) {
store[event.type] = store[event.type] || []
event.handler = handler
event.data = data
event.once = !!once
store[event.type].push(event)
})
}
/**
* 和 Events.on 类似,只不过它执行完马上就会被销毁
* @borrows Events.on
*/
Events.prototype.once = function(events, data, handler) {
this.on.apply(this, Array.prototype.slice.call(arguments).concat(true))
}
/**
* @param {string} [events] 事件名称,类似于 jQuery,如果有多个要用空格隔开,也可以添加 namespace;
* 如果不指定 namespace,则会删除所有 namespace 下的相关的 handler;
* 如果不指定 events,则会删除所有事件
* @param {function} [handler] 如果指定了,则删除对应的事件,否则删除所有匹配 events 的事件
*/
Events.prototype.off = function(events, handler) {
var store = this._events
if (events == null) {
this._events = {}
return
}
parseEventsString(events).forEach(function(event) {
store[event.type] = (store[event.type] || []).filter(function(storeEvent) {
var isHandlerMatch = !handler || handler === storeEvent.handler
var isNamespaceMatch = !event.namespace || event.namespace === storeEvent.namespace
return !isHandlerMatch || !isNamespaceMatch
})
})
}
/**
* @param {string} events 事件名称,类似于 jQuery,如果有多个要用空格隔开,也可以添加 namespace
* @param {...*} args 函数列表,将会传给 handler
*/
Events.prototype.emit = function(events, args) {
var store = this._events
args = Array.prototype.slice.call(arguments, 1)
setTimeout(function() {
parseEventsString(events).forEach(function(event) {
store[event.type] = (store[event.type] || []).filter(function(storeEvent) {
if (!event.namespace || event.namespace === storeEvent.namespace) {
storeEvent.handler.apply(storeEvent, args)
return !storeEvent.once
}
return true
})
})
}, 0)
}
/**
* 使 target 拥有事件相关功能
*
* @param {object} target 要将事件相关的函数放置到的对象
*/
Events.mixin = function(target) {
var props = ['on', 'once', 'off', 'emit']
var ref = typeof target === 'function' ? target.prototype : target
var prop = '_events'
if (prop in ref) warnPropExists(target, prop) // 避免 _events 名称被占用了
Events.call(ref)
for (var i = 0; i < props.length; i++) {
prop = props[i]
if (prop in ref) warnPropExists(target, prop)
ref[prop] = Events.prototype[prop]
}
}
function parseEventsString(events) {
var i, parts
var result = []
events = events.split(/\s+/)
for (i = 0; i < events.length; i++) {
parts = events[i].split('.')
result[i] = {type: parts[0], namespace: parts.slice(1).join('.')}
}
return result
}
function warnPropExists(target, prop) {
console.warn('Target %o has already contains property %o, it will be overwrited!', target, prop)
}
module.exports = Events