1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | const Base = require('./Base');
|
7 |
|
8 | module.exports = class Event extends Base {
|
9 |
|
10 |
|
11 |
|
12 | static once (target, name, handler, data, prepend) {
|
13 | this.on(target, name, handler, data, prepend, 1);
|
14 | }
|
15 |
|
16 | static on (target, name, handler, data, prepend, counter) {
|
17 | const id = target.CLASS_FILE;
|
18 | if (!id) {
|
19 | throw new Error('Invalid target class');
|
20 | }
|
21 | if (typeof handler !== 'function') {
|
22 | throw new Error('Invalid event handler');
|
23 | }
|
24 | if (!Object.prototype.hasOwnProperty.call(this._eventMap, name)) {
|
25 | this._eventMap[name] = {};
|
26 | }
|
27 | const targetMap = this._eventMap[name];
|
28 | if (!Array.isArray(targetMap[id])) {
|
29 | targetMap[id] = [];
|
30 | }
|
31 | handler = [handler, data, counter, targetMap[id]];
|
32 | prepend ? targetMap[id].unshift(handler)
|
33 | : targetMap[id].push(handler);
|
34 | }
|
35 |
|
36 | static off (target, name, handler) {
|
37 | if (target === undefined) {
|
38 | this._eventMap = {};
|
39 | return true;
|
40 | }
|
41 | const id = target.CLASS_FILE;
|
42 | if (!id) {
|
43 | throw new Error('Invalid target class');
|
44 | }
|
45 | if (!name) {
|
46 | return this.detachByTarget(id);
|
47 | }
|
48 | const targetMap = this._eventMap[name];
|
49 | if (!targetMap || !Array.isArray(targetMap[id])) {
|
50 | return false;
|
51 | }
|
52 | if (handler) {
|
53 | return this.detachByHandler(handler, targetMap[id]);
|
54 | }
|
55 | delete targetMap[id];
|
56 | return true;
|
57 | }
|
58 |
|
59 | static detachByTarget (target) {
|
60 | for (const targetMap of Object.values(this._eventMap)) {
|
61 | for (const key of Object.keys(targetMap)) {
|
62 | if (key === target) {
|
63 | delete targetMap[key];
|
64 | }
|
65 | }
|
66 | }
|
67 | return true;
|
68 | }
|
69 |
|
70 | static detachByHandler (handler, items) {
|
71 | let detached = false;
|
72 | for (let i = items.length - 1; i >= 0; --i) {
|
73 | if (items[i][0] === handler) {
|
74 | items.splice(i, 1);
|
75 | detached = true;
|
76 | }
|
77 | }
|
78 | return detached;
|
79 | }
|
80 |
|
81 | static trigger (sender, name, event, items = []) {
|
82 | if (Object.prototype.hasOwnProperty.call(this._eventMap, name)) {
|
83 | const Class = typeof sender !== 'function' ? sender.constructor : sender;
|
84 | if (!Class.CLASS_FILE) {
|
85 | throw new Error('Invalid event sender');
|
86 | }
|
87 | this.prependHandlers(Class, this._eventMap[name], items);
|
88 | }
|
89 | if (items.length) {
|
90 | return this.executeHandlers(items, this.create(event, sender, name));
|
91 | }
|
92 | }
|
93 |
|
94 | static prependHandlers (Class, targetMap, items) {
|
95 | const parent = Object.getPrototypeOf(Class);
|
96 | if (parent && targetMap[parent.CLASS_FILE]) {
|
97 | this.prependHandlers(parent, targetMap, items);
|
98 | }
|
99 | if (Array.isArray(targetMap[Class.CLASS_FILE])) {
|
100 | items.unshift(...targetMap[Class.CLASS_FILE]);
|
101 | }
|
102 | }
|
103 |
|
104 | static async executeHandlers (items, event) {
|
105 | for (const item of items) {
|
106 | if (event.handled) {
|
107 | break;
|
108 | }
|
109 | await item[0].call(this, event, item[1]);
|
110 | if (typeof item[2] === 'number' && --item[2] < 1) {
|
111 | ArrayHelper.removeValue(item, item[3]);
|
112 | }
|
113 | }
|
114 | }
|
115 |
|
116 | static create (event, sender, name) {
|
117 | event = event || new this;
|
118 | event.sender = sender;
|
119 | event.handled = false;
|
120 | event.name = name;
|
121 | return event;
|
122 | }
|
123 |
|
124 | static hasHandlerByName (name) {
|
125 | return Object.prototype.hasOwnProperty.call(this._eventMap, name);
|
126 | }
|
127 |
|
128 | static hasHandlerByTarget (target, name) {
|
129 | if (!this.hasHandlerByName(name)) {
|
130 | return false;
|
131 | }
|
132 | if (typeof target !== 'function') {
|
133 | target = target.constructor;
|
134 | }
|
135 | const targetMap = this._eventMap[name];
|
136 |
|
137 | let id = target.CLASS_FILE;
|
138 | while (id) {
|
139 | if (targetMap[id] && targetMap[id].length) {
|
140 | return true;
|
141 | }
|
142 | target = Object.getPrototypeOf(target);
|
143 | id = target ? target.CLASS_FILE : null;
|
144 | }
|
145 | return false;
|
146 | }
|
147 |
|
148 | constructor (config) {
|
149 | super({
|
150 | name: null,
|
151 | sender: null,
|
152 | handled: false,
|
153 | ...config
|
154 | });
|
155 | }
|
156 | };
|
157 | module.exports._eventMap = {};
|
158 |
|
159 | const ArrayHelper = require('../helper/ArrayHelper'); |
\ | No newline at end of file |