UNPKG

4.9 kBJavaScriptView Raw
1/**
2 * @copyright Copyright (c) 2019 Maxim Khorin <maksimovichu@gmail.com>
3 */
4'use strict';
5
6const Base = require('./Base');
7
8module.exports = class Event extends Base {
9
10 // CLASS LEVEL EVENTS
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 // check target listeners and its ancestors
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); // get parent class
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};
157module.exports._eventMap = {};
158
159const ArrayHelper = require('../helper/ArrayHelper');
\No newline at end of file