nxkit
Version:
This is a collection of tools, independent of any other libraries
671 lines (670 loc) • 20.4 kB
JavaScript
"use strict";
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2015, xuewen.chu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of xuewen.chu nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL xuewen.chu BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
Object.defineProperty(exports, "__esModule", { value: true });
var _id = 0;
class ListItem {
constructor(host, prev, next, value) {
this._host = host;
this._prev = prev;
this._next = next;
this._value = value;
}
get host() { return this._host; }
get prev() { return this._prev; }
get next() { return this._next; }
get value() { return this._value; }
set value(value) { this._value = value; }
}
exports.ListItem = ListItem;
/**
* @class List linked
*/
class List {
constructor() {
this._first = null;
this._last = null;
this._length = 0;
}
get first() {
return this._first;
}
get last() {
return this._last;
}
get length() {
return this._length;
}
del(item) {
if (item.host === this) {
var prev = item.prev;
var next = item.next;
if (prev) {
prev._next = next;
}
else {
this._first = next;
}
if (next) {
next._prev = prev;
}
else {
this._last = prev;
}
item._host = null;
item._prev = null;
item._next = null;
this._length--;
return next;
}
return null;
}
unshift(value) {
var item;
if (this._first) {
item = new ListItem(this, null, this._first, value);
this._first._prev = item;
this._first = item;
}
else {
item = new ListItem(this, null, null, value);
this._last = item;
this._first = item;
}
this._length++;
return item;
}
push(value) {
var item;
if (this._last) {
item = new ListItem(this, this._last, null, value);
this._last._next = item;
this._last = item;
}
else {
item = new ListItem(this, null, null, value);
this._last = item;
this._first = item;
}
this._length++;
return item;
}
pop() {
if (this._length) {
var r = this._last;
if (this._length > 1) {
r.prev._next = null;
this._last = r.prev;
}
else {
this._first = null;
this._last = null;
}
this._length--;
r._host = null;
r._prev = null;
r._next = null;
return r.value;
}
return null;
}
shift() {
if (this._length) {
var r = this._first;
if (this._length > 1) {
r.next._prev = null;
this._first = r.next;
}
else {
this._first = null;
this._last = null;
}
this._length--;
r._host = null;
r._prev = null;
r._next = null;
return r.value;
}
return null;
}
insert(prev, value) {
if (prev.host !== this)
throw 'Bad argument.';
var _prev = prev;
var item;
if (_prev._next) {
item = new ListItem(this, _prev, _prev._next, value);
_prev._next._prev = item;
_prev._next = item;
}
else {
item = new ListItem(this, _prev, null, value);
_prev._next = item;
this._last = item;
}
this._length++;
return item;
}
clear() {
this._first = null;
this._last = null;
this._length = 0;
}
}
exports.List = List;
/**
* @class Event
*/
class Event {
constructor(data, returnValue) {
this.m_data = data;
if (returnValue !== undefined)
this.m_return_value = returnValue;
}
get name() {
return this.m_noticer.name;
}
get data() {
return this.m_data;
}
get sender() {
return this.m_noticer.sender;
}
get origin() {
return this.m_origin;
}
set origin(value) {
this.m_origin = value;
}
get noticer() {
return this.m_noticer;
}
get returnValue() {
return this.m_return_value;
}
set returnValue(value) {
this.m_return_value = value;
}
}
exports.Event = Event;
Event.prototype.m_return_value = 0;
Event.prototype.m_noticer = null;
Event.prototype.m_origin = null;
function check_noticer(noticer) {
if (!noticer)
throw new Error('Event listener function type is incorrect ');
}
function check_fun(origin) {
if (typeof origin != 'function') {
throw new Error('Event listener function type is incorrect ');
}
}
function forwardNoticeNoticer(forward_noticer, evt) {
try {
var noticer = evt.m_noticer;
forward_noticer.triggerWithEvent(evt);
}
finally {
evt.m_noticer = noticer;
}
}
class EventNoticer {
/**
* @constructor
* @arg name {String} # 事件名称
* @arg sender {Object} # 事件发起者
*/
constructor(name, sender) {
this.m_listens = null;
this.m_listens_map = null;
this.m_length = 0;
this.m_enable = true;
this.m_name = name;
this.m_sender = sender;
}
/* @fun add # Add event listen */
_add(origin_listen, listen, scope, id) {
var self = this;
var listens_map = self.m_listens_map;
if (!listens_map) {
self.m_listens = new List();
self.m_listens_map = listens_map = new Map();
}
if (typeof scope != 'object') {
id = String(scope || ++_id);
scope = self.m_sender;
}
else {
scope = scope || self.m_sender;
id = String(id || ++_id);
}
id = String(id);
var value = {
origin: origin_listen,
listen: listen,
scope: scope,
id: id,
};
var item = listens_map.get(id);
if (item) { // replace
item.value = value;
}
else { // add
listens_map.set(id, self.m_listens.push(value));
self.m_length++;
}
return id;
}
/**
* @get enable {bool} # 获取是否已经启用
*/
get enable() {
return this.m_enable;
}
/**
* @set enable {bool} # 设置, 启用/禁用
*/
set enable(value) {
this.m_enable = value;
}
/**
* @get name {String} # 事件名称
*/
get name() {
return this.m_name;
}
/**
* @get {Object} # 事件发送者
*/
get sender() {
return this.m_sender;
}
/**
*
* @get {int} # 添加的事件侦听数量
*/
get length() {
return this.m_length;
}
/**
* @fun on # 绑定一个事件侦听器(函数)
* @arg listen {Function} # 侦听函数
* @arg [scope] {Object} # 重新指定侦听函数this
* @arg [id] {String} # 侦听器别名,可通过id删除
*/
on(listen, scopeOrId, id) {
check_fun(listen);
return this._add(listen, listen, scopeOrId, id);
}
/**
* @fun once # 绑定一个侦听器(函数),且只侦听一次就立即删除
* @arg listen {Function} # 侦听函数
* @arg [scope] {Object} # 重新指定侦听函数this
* @arg [id] {String} # 侦听器别名,可通过id删除
*/
once(listen, scopeOrId, id) {
check_fun(listen);
var self = this;
var _id = this._add(listen, {
call: function (scope, evt) {
self.off(_id);
listen.call(scope, evt);
}
}, scopeOrId, id);
return _id;
}
/**
* Bind an event listener (function),
* and "on" the same processor of the method to add the event trigger to receive two parameters
* @fun on2
* @arg listen {Function} # 侦听函数
* @arg [scope] {Object} # 重新指定侦听函数this
* @arg [id] {String} # 侦听器别名,可通过id删除
*/
on2(listen, scopeOrId, id) {
check_fun(listen);
return this._add(listen, { call: listen }, scopeOrId, id);
}
/**
* Bind an event listener (function), And to listen only once and immediately remove
* and "on" the same processor of the method to add the event trigger to receive two parameters
* @fun once2
* @arg listen {Function} # 侦听函数
* @arg [scope] {Object} # 重新指定侦听函数this
* @arg [id] {String} # 侦听器id,可通过id删除
*/
once2(listen, scopeOrId, id) {
check_fun(listen);
var self = this;
var _id = this._add(listen, {
call: function (scope, evt) {
self.off(_id);
listen(scope, evt);
}
}, scopeOrId, id);
return _id;
}
forward(noticer, id) {
check_noticer(noticer);
return this._add(noticer, { call: forwardNoticeNoticer }, noticer, id);
}
forwardOnce(noticer, id) {
check_noticer(noticer);
var self = this;
var _id = this._add(noticer, function (evt) {
self.off(_id);
forwardNoticeNoticer(noticer, evt);
}, noticer, id);
return _id;
}
/**
* @fun trigger # 通知所有观察者
* @arg data {Object} # 要发送的数据
* @ret {Object}
*/
trigger(data) {
return this.triggerWithEvent(new Event(data));
}
/**
* @fun triggerWithEvent # 通知所有观察者
* @arg data {Object} 要发送的event
* @ret {Object}
*/
triggerWithEvent(evt) {
if (this.m_enable && this.m_length) {
evt.m_noticer = this;
var listens = this.m_listens;
var item = listens.first;
while (item) {
var value = item.value;
if (value.listen) {
value.listen.call(value.scope, evt);
item = item.next;
}
else {
item = listens.del(item);
}
}
evt.m_noticer = null;
}
return evt.returnValue;
}
/**
* @fun off # 卸载侦听器(函数)
* @arg [func] {Object} # 可以是侦听函数,id,如果不传入参数卸载所有侦听器
* @arg [scope] {Object} # scope
*/
off(listen, scope) {
if (!this.m_length) {
return 0;
}
var r = 0;
if (listen) {
if (typeof listen == 'string') { // by id delete
var name = String(listen);
let listens_map = this.m_listens_map;
let item = listens_map.get(name);
if (item) {
this.m_length--;
listens_map.delete(name);
item.value.listen = null; // clear
r++;
}
}
else if (listen instanceof Function) { // 要卸载是一个函数
let listens = this.m_listens;
let listens_map = this.m_listens_map;
let item = listens.first;
if (scope) { // 需比较范围
while (item) {
let value = item.value;
if (value.listen) {
if (value.origin === listen && value.scope === scope) {
this.m_length--;
listens_map.delete(value.id);
item.value.listen = null;
r++;
break; // clear
}
}
item = item.next;
}
}
else { // 与这个函数有关系的
let listens_map = this.m_listens_map;
while (item) {
let value = item.value;
if (value.listen) {
if (value.origin === listen) {
this.m_length--;
listens_map.delete(value.id);
item.value.listen = null;
r++;
break; // clear
}
}
item = item.next;
}
}
}
else if (listen instanceof Object) { //
let listens = this.m_listens;
let listens_map = this.m_listens_map;
let item = listens.first;
// 要卸载这个范围上相关的侦听器,包括`EventNoticer`代理
while (item) {
var value = item.value;
if (value.listen) {
if (value.scope === listen) {
this.m_length--;
listens_map.delete(value.id);
item.value.listen = null; // break; // clear
r++;
}
}
item = item.next;
}
}
else { //
throw new Error('Bad argument.');
}
}
else { // 全部删除
let listens = this.m_listens;
let item = listens.first;
while (item) {
item.value.listen = null; // clear
item = item.next;
r++;
}
this.m_length = 0;
this.m_listens_map = new Map();
}
return r;
}
}
exports.EventNoticer = EventNoticer;
const PREFIX = 'm_on';
const FIND_REG = new RegExp('^' + PREFIX);
/**
* @class Notification
*/
class Notification {
/**
* @func getNoticer
*/
getNoticer(name) {
var noticer = this[PREFIX + name];
if (!noticer) {
noticer = new EventNoticer(name, this);
this[PREFIX + name] = noticer;
}
return noticer;
}
/**
* @func hasNoticer
*/
hasNoticer(name) {
return (PREFIX + name) in this;
}
/**
* @func addDefaultListener
*/
addDefaultListener(name, listen) {
if (listen) {
this.addEventListener(name, listen, '0'); // default id 0
}
else { // delete default listener
this.removeEventListener(name, '0');
}
}
/**
* @func addEventListener(name, listen[,scope[,id]])
*/
addEventListener(name, listen, scopeOrId, id) {
var del = this.getNoticer(name);
var r = del.on(listen, scopeOrId, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
/**
* @func addEventListenerOnce(name, listen[,scope[,id]])
*/
addEventListenerOnce(name, listen, scopeOrId, id) {
var del = this.getNoticer(name);
var r = del.once(listen, scopeOrId, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
/**
* @func addEventListener2(name, listen[,scope[,id]])
*/
addEventListener2(name, listen, scopeOrId, id) {
var del = this.getNoticer(name);
var r = del.on2(listen, scopeOrId, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
/**
* @func addEventListenerOnce2(name, listen[,scope[,id]])
*/
addEventListenerOnce2(name, listen, scopeOrId, id) {
var del = this.getNoticer(name);
var r = del.once2(listen, scopeOrId, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
addEventForward(name, noticer, id) {
var del = this.getNoticer(name);
var r = del.forward(noticer, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
addEventForwardOnce(noticer, id) {
var del = this.getNoticer(name);
var r = del.forwardOnce(noticer, id);
this.triggerListenerChange(name, del.length, 1);
return r;
}
/**
* @func trigger 通知事监听器
* @arg name {String} 事件名称
* @arg data {Object} 要发送的消数据
*/
trigger(name, data) {
return this.triggerWithEvent(name, new Event(data));
}
/**
* @func triggerWithEvent 通知事监听器
* @arg name {String} 事件名称
* @arg event {Event} Event
*/
triggerWithEvent(name, event) {
var noticer = this[PREFIX + name];
if (noticer) {
return noticer.triggerWithEvent(event);
}
return event.returnValue;
}
/**
* @func removeEventListener(name,[func[,scope]])
*/
removeEventListener(name, listen, scope) {
var noticer = this[PREFIX + name];
if (noticer) {
noticer.off(listen, scope);
this.triggerListenerChange(name, noticer.length, -1);
}
}
/**
* @func removeEventListenerWithScope(scope) 卸载notification上所有与scope相关的侦听器
* @arg scope {Object}
*/
removeEventListenerWithScope(scope) {
for (let noticer of this.allNoticers()) {
noticer.off(scope);
this.triggerListenerChange(name, noticer.length, -1);
}
}
/**
* @func allNoticers() # Get all event noticer
* @ret {Array}
*/
allNoticers() {
var result = [];
for (var i in this) {
if (FIND_REG.test(i)) {
var noticer = this[i];
if (noticer instanceof EventNoticer) {
result.push(noticer);
}
}
}
return result;
}
/**
* @func triggerListenerChange
*/
triggerListenerChange(name, count, change) { }
}
exports.Notification = Notification;
function event(target, name) {
if (name.substr(0, 2) !== 'on') {
throw new Error(`event name incorrect format`);
}
var event = name.substr(2);
Object.defineProperty(target, name, {
configurable: false,
enumerable: true,
get() { return this.getNoticer(event); },
set(listen) { this.addDefaultListener(event, listen); },
});
}
exports.event = event;