board-game
Version:
an online board game engine
171 lines (169 loc) • 5.58 kB
JavaScript
/**
* Created by wm123 on 2017/5/23.
*/
class Queue {
constructor() {
this.queue = [];
this.timer = false;
}
exec() {
if (this.queue.length) {
clearTimeout(this.timer);
this.timer = process.nextTick(() => {
this.queue.shift().call();
this.exec();
});
}
}
execute(executor) {
this.queue.push(executor);
this.exec();
}
}
class Listener {
constructor(dispatcher) {
this.dispatcher = dispatcher;
this.listeners = [];
this.callbacks = [];
this.queue = new Queue();
}
add(listener, id) {
let index = this.listeners.findIndex(fun => fun === listener || typeof fun.id !== 'undefined' && fun.id === id);
if (index >= 0) this.listeners.splice(index, 1);
listener.id = id;
this.listeners.push(listener);
}
remove(listener) {
let index = this.listeners.findIndex(fun => fun === listener || typeof fun.id !== 'undefined' && fun.id === listener);
if (index >= 0) this.listeners.splice(index, 1);
}
removeAll() {
this.listeners = [];
}
dispatch(callback) {
this.callbacks.push(callback);
if (this.callbacks.length === 1) {
this.queue.execute(() => {
this.dispatcher.dispatching = true;
this.listeners.forEach(cb => cb && cb.call(this.dispatcher, this.dispatcher));
this.callbacks.forEach(cb => cb.call());
this.callbacks = [];
this.dispatcher.dispatching = false;
});
}
}
get size() {
return this.listeners.length;
}
}
class ListenerMap {
constructor() {
this.listeners = new WeakMap();
}
add(dispatcher, listener, id) {
this.get(dispatcher).add(listener, id);
}
remove(dispatcher, listener) {
this.get(dispatcher).remove(listener);
}
removeAll(dispatcher) {
this.get(dispatcher).removeAll();
}
get(dispatcher) {
if (this.listeners.has(dispatcher)) {
return this.listeners.get(dispatcher);
} else {
let listener = new Listener(dispatcher);
this.listeners.set(dispatcher, listener);
return listener;
}
}
dispatch(dispatcher, callback) {
this.get(dispatcher).dispatch(callback);
}
size(dispatcher) {
return this.get(dispatcher).size;
}
}
class Dispatcher {
constructor() {
this.listeners = new ListenerMap();
this.dispatching = false;
let dispatcher = new Proxy(this, {
set(target, property, value, receiver) {
Reflect.set(target, property, value, receiver);
if (!property.startsWith('~') && property !== 'dispatching') {
if (!dispatcher.dispatching) dispatcher.dispatch();
}
return true;
},
get(target, property, receiver) {
let value = Reflect.get(target, property, receiver);
if (!['on', 'off', 'clear', 'dispatch', 'constructor', 'dispatching'].includes(property) && !(property in { }) && (value instanceof Function || typeof value === 'function')) {
return function() {
let result = value.apply(this, arguments);
if (!dispatcher.dispatching) dispatcher.dispatch();
return result;
};
} else {
return value;
}
},
deleteProperty(target, property) {
if (Reflect.get(target, property, dispatcher) instanceof Dispatcher) Reflect.get(target, property, dispatcher).off(dispatcher);
Reflect.deleteProperty(target, property);
if (!property.startsWith('~')) {
Reflect.set(target, '~' + property, true);
if (!dispatcher.dispatching) dispatcher.dispatch();
}
return true;
}
});
//dispatcher.dispatch();
return dispatcher;
}
on(listener, id, append = false) {
this.listeners.add(this, listener, id);
if(!append) this.dispatch();
}
off(listener) {
this.listeners.remove(this, listener);
}
clear() {
this.listeners.removeAll(this);
}
refresh() {
//do nothing
}
dispatch() {
this.listeners.dispatch(this, () => {
for (let name in this) {
if (this.hasOwnProperty(name)) {
if (name.startsWith('~')) {
delete this[name];
}
}
}
});
}
dispose() {
this.clear();
}
get data() {
let result = { };
for (let name in this) {
if (this.hasOwnProperty(name)) {
if (name !== 'data' && name !== 'listeners') {
let value = this[name];
if (value instanceof Dispatcher) {
result[name] = value.data;
} else {
result[name] = value;
}
}
}
}
return result;
}
}
module.exports = Dispatcher;