-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloud-array.js
More file actions
154 lines (141 loc) · 5.47 KB
/
loud-array.js
File metadata and controls
154 lines (141 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// author: 1076
// license: MIT
// source: https://github.com/the1076/loud-array
const privateKey = { id: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) )};
export default class LoudArray extends Array
{
constructor(listeners, ...fromValues)
{
super(...fromValues);
this._private = new WeakMap();
this._private.set(privateKey, { listeners: listeners || [] });
//handle listeners when methods that change the array are called (list sourced from MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Methods_2)
const mutatorMethods = 'copyWithin fill pop push reverse shift sort splice unshift'.split(' ');
const privateData = this._private.get(privateKey);
for(let i = 0; i < mutatorMethods.length; i++)
{
let method = mutatorMethods[i];
privateData[method] = this[method].bind(this);
this[method] = (...args) =>
{
let listeners = privateData.listeners.filter((value) =>
{
if(method === 'copyWithin' && value.event.indexOf('copy-within') > -1)
{
return true;
}
let event = value.event.toLowerCase();
return (event.indexOf(method) > -1) || event.indexOf('any') > -1 || event.indexOf('all') > -1;
});
_dispatchEvents(this, listeners, 'before', args);
let returnValue = privateData[method](...args);
_dispatchEvents(this, listeners, 'after', args);
return returnValue;
}
}
//handle functions that return new arrays because otherwise they will return LoudArray instances but the _privacy WeakMap won't work right.
privateData['concat'] = this['concat'].bind(this);
this['concat'] = (...args) =>
{
let returnValue = privateData['concat'](...args);
returnValue = new Array(...returnValue);
return returnValue;
}
privateData['slice'] = this['slice'].bind(this);
this['slice'] = (...args) =>
{
let returnValue = privateData['slice'](...args);
returnValue = new Array(...returnValue);
return returnValue;
}
}
addEventListener(event, handler)
{
let listener = event;
if(!(event instanceof ArrayListener))
{
listener = new ArrayListener(event, handler);
}
const privateData = this._private.get(privateKey);
let listenerIndex = _array_findListener(this, listener.event, listener.handler);
if(listenerIndex > -1)
{
return privateData.listeners[listenerIndex];
}
privateData.listeners.push(listener);
return listener;
}
removeEventListener(event, handler)
{
let listener = event;
if(!(event instanceof ArrayListener))
{
listener = new ArrayListener(event, handler);
}
let listenerIndex = _array_findListener(this, listener.event, listener.handler);
if(listenerIndex == -1)
{
return;
}
const privateData = this._private.get(privateKey);
privateData.listeners.splice(listenerIndex, 1);
}
}
export class ArrayListener
{
constructor(event, handler)
{
this.event = event
this.handler = handler;
}
}
export class ArrayEvent //String enum uses getters because Firefox doesn't play nice with static properties yet.
{
static get BeforeAll() { return 'before-all'; }
static get BeforeCopyWithin () { return 'before-copy-within'; }
static get BeforeFill () { return 'before-fill'; }
static get BeforePop () { return 'before-pop'; }
static get BeforePush () { return 'before-push'; }
static get BeforeReverse () { return 'before-reverse'; }
static get BeforeShift () { return 'before-shift'; }
static get BeforeSort () { return 'before-sort'; }
static get BeforeSplice () { return 'before-splice'; }
static get BeforeUnshift () { return 'before-unshift'; }
static get AfterAll() { return 'after-all'; }
static get AfterCopyWithin () { return 'after-copy-within'; }
static get AfterFill () { return 'after-fill'; }
static get AfterPop () { return 'after-pop'; }
static get AfterPush () { return 'after-push'; }
static get AfterReverse () { return 'after-reverse'; }
static get AfterShift () { return 'after-shift'; }
static get AfterSort () { return 'after-sort'; }
static get AfterSplice () { return 'after-splice'; }
static get AfterUnshift () { return 'after-unshift'; }
}
function _array_findListener(target, event, handler)
{
let index = -1;
const privateData = target._private.get(privateKey);
for(let i = 0; i < privateData.listeners.length; i++)
{
let listener = privateData.listeners[i];
let listenerEvent = listener.event.toLowerCase();
if(listenerEvent == event && listener.handler == handler)
{
index = i;
break;
}
}
return index;
}
function _dispatchEvents(target, listeners, stage, args)
{
for(let i = 0; i < listeners.length; i++)
{
let listener = listeners[i];
if(listener.event.toLowerCase().indexOf(stage) > -1)
{
listener.handler(target, args);
}
}
}