multyx-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controller.js +356 -0
- package/dist/index.js +213 -0
- package/dist/items/index.js +9 -0
- package/dist/items/list.js +250 -0
- package/dist/items/object.js +142 -0
- package/dist/items/router.js +8 -0
- package/dist/items/value.js +131 -0
- package/dist/message.js +36 -0
- package/dist/options.js +10 -0
- package/dist/types.js +2 -0
- package/dist/utils.js +74 -0
- package/multyx.js +1 -0
- package/package.json +34 -0
- package/src/controller.ts +353 -0
- package/src/index.ts +261 -0
- package/src/items/index.ts +14 -0
- package/src/items/list.ts +270 -0
- package/src/items/object.ts +170 -0
- package/src/items/router.ts +5 -0
- package/src/items/value.ts +150 -0
- package/src/message.ts +41 -0
- package/src/options.ts +15 -0
- package/src/types.ts +17 -0
- package/src/utils.ts +78 -0
- package/tsconfig.json +9 -0
- package/webpack.config.js +13 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { Message } from "./message";
|
|
2
|
+
import { Unpack, EditWrapper, Add } from './utils';
|
|
3
|
+
import { RawObject } from "./types";
|
|
4
|
+
import { Controller } from "./controller";
|
|
5
|
+
import { MultyxClientObject } from "./items";
|
|
6
|
+
import { DefaultOptions, Options } from "./options";
|
|
7
|
+
|
|
8
|
+
export default class Multyx {
|
|
9
|
+
ws: WebSocket;
|
|
10
|
+
uuid: string;
|
|
11
|
+
joinTime: number;
|
|
12
|
+
ping: number;
|
|
13
|
+
events: Map<string | Symbol, ((data?: any) => void)[]>;
|
|
14
|
+
self: RawObject;
|
|
15
|
+
all: RawObject;
|
|
16
|
+
clients: RawObject;
|
|
17
|
+
teams: RawObject;
|
|
18
|
+
controller: Controller;
|
|
19
|
+
|
|
20
|
+
options: Options;
|
|
21
|
+
|
|
22
|
+
// Queue of functions to be called after each frame
|
|
23
|
+
private listenerQueue: ((...args: any[]) => void)[];
|
|
24
|
+
|
|
25
|
+
static Start = Symbol('start');
|
|
26
|
+
static Connection = Symbol('connection');
|
|
27
|
+
static Disconnect = Symbol('disconnect');
|
|
28
|
+
static Edit = Symbol('edit');
|
|
29
|
+
static Native = Symbol('native');
|
|
30
|
+
static Custom = Symbol('custom');
|
|
31
|
+
static Any = Symbol('any');
|
|
32
|
+
|
|
33
|
+
constructor(options: Options = {}, callback?: () => void) {
|
|
34
|
+
this.options = { ...DefaultOptions, ...options };
|
|
35
|
+
|
|
36
|
+
const url = `ws${this.options.secure ? 's' : ''}://${this.options.uri}:${this.options.port}/`;
|
|
37
|
+
this.ws = new WebSocket(url);
|
|
38
|
+
this.ping = 0;
|
|
39
|
+
this.events = new Map();
|
|
40
|
+
this.self = {};
|
|
41
|
+
this.all = {};
|
|
42
|
+
this.teams = {};
|
|
43
|
+
this.clients = {};
|
|
44
|
+
this.controller = new Controller(this.ws);
|
|
45
|
+
this.listenerQueue = [];
|
|
46
|
+
|
|
47
|
+
callback?.();
|
|
48
|
+
|
|
49
|
+
this.ws.onmessage = event => {
|
|
50
|
+
const msg = Message.Parse(event.data);
|
|
51
|
+
this.ping = 2 * (Date.now() - msg.time);
|
|
52
|
+
|
|
53
|
+
if(msg.native) {
|
|
54
|
+
this.parseNativeEvent(msg);
|
|
55
|
+
this.events.get(Multyx.Native)?.forEach(cb => cb(msg));
|
|
56
|
+
} else if(msg.name in this.events) {
|
|
57
|
+
this.events[msg.name](msg.data);
|
|
58
|
+
this.events.get(Multyx.Custom)?.forEach(cb => cb(msg));
|
|
59
|
+
}
|
|
60
|
+
this.events.get(Multyx.Any)?.forEach(cb => cb(msg));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
on(name: string | Symbol, callback: (data: RawObject) => void) {
|
|
65
|
+
const events = this.events.get(name) ?? [];
|
|
66
|
+
events.push(callback);
|
|
67
|
+
this.events.set(name, events);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
send(name: string, data: any, expectResponse: boolean = false) {
|
|
71
|
+
if(name[0] === '_') name = '_' + name;
|
|
72
|
+
this.ws.send(Message.Create(name, data));
|
|
73
|
+
if(!expectResponse) return;
|
|
74
|
+
|
|
75
|
+
return new Promise(res => this.events.set(Symbol.for("_" + name), [res]));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Loop over a function
|
|
80
|
+
* @param callback Function to call on a loop
|
|
81
|
+
* @param timesPerSecond Recommended to leave blank. Number of times to loop in each second, if undefined, use requestAnimationFrame
|
|
82
|
+
*/
|
|
83
|
+
loop(callback: () => void, timesPerSecond?: number) {
|
|
84
|
+
if(timesPerSecond) {
|
|
85
|
+
this.on(Multyx.Start, () => setInterval(callback, Math.round(1000/timesPerSecond)));
|
|
86
|
+
} else {
|
|
87
|
+
const caller = () => {
|
|
88
|
+
callback();
|
|
89
|
+
requestAnimationFrame(caller);
|
|
90
|
+
}
|
|
91
|
+
this.on(Multyx.Start, () => requestAnimationFrame(caller));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a callback function that gets called for any current or future client
|
|
98
|
+
* @param callbackfn Function to call for every client
|
|
99
|
+
*/
|
|
100
|
+
forAll(callback: (client: MultyxClientObject) => void) {
|
|
101
|
+
this.on(Multyx.Start, () => {
|
|
102
|
+
this.teams.all.clients.forAll((uuid) => callback(this.clients[uuid]));
|
|
103
|
+
});
|
|
104
|
+
this.on(Multyx.Connection, callback);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private parseNativeEvent(msg: Message) {
|
|
108
|
+
if(this.options.logUpdateFrame) console.log(msg);
|
|
109
|
+
|
|
110
|
+
for(const update of msg.data) {
|
|
111
|
+
switch(update.instruction) {
|
|
112
|
+
// Initialization
|
|
113
|
+
case 'init': {
|
|
114
|
+
this.initialize(update);
|
|
115
|
+
|
|
116
|
+
for(const listener of this.events.get(Multyx.Start) ?? []) {
|
|
117
|
+
this.listenerQueue.push(() => listener(update));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Clear start event as it will never be called again
|
|
121
|
+
if(this.events.has(Multyx.Start)) this.events.get(Multyx.Start).length = 0;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Client or team data edit
|
|
126
|
+
case 'edit': {
|
|
127
|
+
this.parseEdit(update);
|
|
128
|
+
|
|
129
|
+
for(const listener of this.events.get(Multyx.Edit) ?? []) {
|
|
130
|
+
this.listenerQueue.push(() => listener(update));
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Other data change
|
|
136
|
+
case 'self': {
|
|
137
|
+
this.parseSelf(update);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Connection
|
|
142
|
+
case 'conn': {
|
|
143
|
+
this.clients[update.uuid] = new MultyxClientObject(
|
|
144
|
+
this,
|
|
145
|
+
update.data,
|
|
146
|
+
[update.uuid],
|
|
147
|
+
false
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
for(const listener of this.events.get(Multyx.Connection) ?? []) {
|
|
151
|
+
this.listenerQueue.push(() => listener(this.clients[update.uuid]));
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Disconnection
|
|
157
|
+
case 'dcon': {
|
|
158
|
+
for(const listener of this.events.get(Multyx.Disconnect) ?? []) {
|
|
159
|
+
const clientValue = this.clients[update.client].value;
|
|
160
|
+
this.listenerQueue.push(() => listener(clientValue));
|
|
161
|
+
}
|
|
162
|
+
delete this.clients[update.client];
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Response to client
|
|
167
|
+
case 'resp': {
|
|
168
|
+
const promiseResolve = this.events.get(Symbol.for("_" + update.name))[0];
|
|
169
|
+
promiseResolve(update.response);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
default: {
|
|
174
|
+
if(this.options.verbose) {
|
|
175
|
+
console.error("Server error: Unknown native Multyx instruction");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.listenerQueue.forEach(x => x());
|
|
182
|
+
this.listenerQueue.length = 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private initialize(update: RawObject) {
|
|
186
|
+
this.uuid = update.client.uuid;
|
|
187
|
+
this.joinTime = update.client.joinTime;
|
|
188
|
+
this.controller.listening = new Set(update.client.controller);
|
|
189
|
+
|
|
190
|
+
// Create MultyxClientObject for all teams
|
|
191
|
+
this.teams = new MultyxClientObject(this, {}, [], true);
|
|
192
|
+
for(const team of Object.keys(update.teams)) {
|
|
193
|
+
this.teams[team] = new EditWrapper(update.teams[team]);
|
|
194
|
+
}
|
|
195
|
+
this.all = this.teams['all'];
|
|
196
|
+
|
|
197
|
+
// Create MultyxClientObject for all clients
|
|
198
|
+
this.clients = {};
|
|
199
|
+
for(const [uuid, client] of Object.entries(update.clients)) {
|
|
200
|
+
if(uuid == this.uuid) continue;
|
|
201
|
+
this.clients[uuid] = new MultyxClientObject(
|
|
202
|
+
this,
|
|
203
|
+
new EditWrapper(client),
|
|
204
|
+
[uuid],
|
|
205
|
+
false
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const client = new MultyxClientObject(
|
|
210
|
+
this,
|
|
211
|
+
new EditWrapper(update.client.self),
|
|
212
|
+
[this.uuid],
|
|
213
|
+
true
|
|
214
|
+
);
|
|
215
|
+
this.self = client;
|
|
216
|
+
this.clients[this.uuid] = client;
|
|
217
|
+
|
|
218
|
+
// Apply all constraints on self and teams
|
|
219
|
+
for(const [uuid, table] of Object.entries(update.constraintTable)) {
|
|
220
|
+
const obj = this.uuid == uuid ? this.self : this.teams[uuid];
|
|
221
|
+
obj[Unpack](table);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private parseEdit(update: RawObject) {
|
|
226
|
+
let route: any = update.team ? this.teams : this.clients;
|
|
227
|
+
if(!route) return;
|
|
228
|
+
|
|
229
|
+
// Loop through path to get to object being edited
|
|
230
|
+
for(const p of update.path.slice(0, -1)) {
|
|
231
|
+
// Create new object at path if non-existent
|
|
232
|
+
if(!(p in route)) route[p] = new EditWrapper({});
|
|
233
|
+
route = route[p];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const prop = update.path.slice(-1)[0];
|
|
237
|
+
route[prop] = new EditWrapper(update.value);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private parseSelf(update: RawObject) {
|
|
241
|
+
if(update.prop == 'controller') {
|
|
242
|
+
this.controller.listening = new Set(update.data);
|
|
243
|
+
} else if(update.prop == 'uuid') {
|
|
244
|
+
this.uuid = update.data;
|
|
245
|
+
} else if(update.prop == 'constraint') {
|
|
246
|
+
let route = this.uuid == update.data.path[0] ? this.self : this.teams[update.data.path[0]];
|
|
247
|
+
for(const prop of update.data.path.slice(1)) route = route?.[prop];
|
|
248
|
+
if(route === undefined) return;
|
|
249
|
+
|
|
250
|
+
route[Unpack]({ [update.data.name]: update.data.args });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Add function to listener queue
|
|
256
|
+
* @param fn Function to call once frame is complete
|
|
257
|
+
*/
|
|
258
|
+
[Add](fn: ((...args: any[]) => void)) {
|
|
259
|
+
this.listenerQueue.push(fn);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import MultyxClientList from "./list";
|
|
2
|
+
import MultyxClientObject from "./object";
|
|
3
|
+
import MultyxClientValue from "./value";
|
|
4
|
+
|
|
5
|
+
type MultyxClientItem<T = any> = T extends any[] ? MultyxClientList
|
|
6
|
+
: T extends object ? MultyxClientObject
|
|
7
|
+
: MultyxClientValue;
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
MultyxClientList,
|
|
11
|
+
MultyxClientObject,
|
|
12
|
+
MultyxClientValue,
|
|
13
|
+
MultyxClientItem,
|
|
14
|
+
};
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import Multyx from '../';
|
|
2
|
+
import { MultyxClientItem } from '.';
|
|
3
|
+
import { EditWrapper } from '../utils';
|
|
4
|
+
import MultyxClientObject from "./object";
|
|
5
|
+
|
|
6
|
+
export default class MultyxClientList extends MultyxClientObject {
|
|
7
|
+
length: number;
|
|
8
|
+
|
|
9
|
+
get value() {
|
|
10
|
+
const parsed: any[] = [];
|
|
11
|
+
for(let i=0; i<this.length; i++) parsed[i] = this.get(i).value;
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor(multyx: Multyx, list: any[] | EditWrapper<any[]>, propertyPath: string[] = [], editable: boolean){
|
|
16
|
+
super(multyx, {}, propertyPath, editable);
|
|
17
|
+
|
|
18
|
+
this.length = 0;
|
|
19
|
+
this.push(...(list instanceof EditWrapper ? list.value.map(x => new EditWrapper(x)) : list));
|
|
20
|
+
|
|
21
|
+
return new Proxy(this, {
|
|
22
|
+
has: (o, p) => {
|
|
23
|
+
if(p in o) return true;
|
|
24
|
+
return o.has(p);
|
|
25
|
+
},
|
|
26
|
+
get: (o, p) => {
|
|
27
|
+
if(p in o) return o[p];
|
|
28
|
+
return o.get(p);
|
|
29
|
+
},
|
|
30
|
+
set: (o, p, v) => {
|
|
31
|
+
if(p in o) {
|
|
32
|
+
o[p] = v;
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return o.set(p as string, v);
|
|
36
|
+
},
|
|
37
|
+
deleteProperty: (o, p) => {
|
|
38
|
+
return o.delete(p as string, false);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
set(index: string | number, value: any) {
|
|
44
|
+
if(typeof index == 'string') index = parseInt(index);
|
|
45
|
+
if(value === undefined) return this.delete(index, false);
|
|
46
|
+
if(value instanceof EditWrapper && value.value === undefined) return this.delete(index, true);
|
|
47
|
+
|
|
48
|
+
const result = super.set(index, value);
|
|
49
|
+
if(result && index >= this.length) this.length = index+1;
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
delete(index: string | number, native: boolean = false) {
|
|
55
|
+
if(typeof index == 'string') index = parseInt(index);
|
|
56
|
+
|
|
57
|
+
const res = super.delete(index, native);
|
|
58
|
+
if(res) this.length = this.reduce((a, c, i) => c !== undefined ? i+1 : a, 0);
|
|
59
|
+
return res;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a callback function that gets called for any current or future element in list
|
|
64
|
+
* @param callbackfn Function to call for every element
|
|
65
|
+
*/
|
|
66
|
+
forAll(callbackfn: (value: any, index: number) => void) {
|
|
67
|
+
for(let i=0; i<this.length; i++) {
|
|
68
|
+
callbackfn(this.get(i), i);
|
|
69
|
+
}
|
|
70
|
+
super.forAll((key, value) => callbackfn(value, key));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/* All general array methods */
|
|
75
|
+
push(...items: any) {
|
|
76
|
+
for(const item of items) this.set(this.length, item);
|
|
77
|
+
return this.length;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pop(): MultyxClientItem | null {
|
|
81
|
+
if(this.length === 0) return null;
|
|
82
|
+
|
|
83
|
+
const res = this.get(this.length);
|
|
84
|
+
this.delete(this.length);
|
|
85
|
+
return res;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
unshift(...items: any[]) {
|
|
89
|
+
for(let i=this.length-1; i>=0; i--) {
|
|
90
|
+
if(i >= items.length) {
|
|
91
|
+
this.set(i, this.get(i-items.length));
|
|
92
|
+
} else {
|
|
93
|
+
this.set(i, items[i]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return this.length;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
shift() {
|
|
101
|
+
if(this.length == 0) return undefined;
|
|
102
|
+
this.length--;
|
|
103
|
+
|
|
104
|
+
const res = this.get(0);
|
|
105
|
+
for(let i=0; i<this.length; i++) {
|
|
106
|
+
this.set(i, this.get(i+1));
|
|
107
|
+
}
|
|
108
|
+
return res;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
splice(start: number, deleteCount?: number, ...items: any[]) {
|
|
112
|
+
if(deleteCount === undefined) {
|
|
113
|
+
deleteCount = this.length - start;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Move elements in front of splice forward or backward
|
|
117
|
+
let move = items.length - deleteCount;
|
|
118
|
+
if(move > 0) {
|
|
119
|
+
for(let i=this.length-1; i>=start + deleteCount; i--) {
|
|
120
|
+
this.set(i + move, this.get(i));
|
|
121
|
+
}
|
|
122
|
+
} else if(move < 0) {
|
|
123
|
+
for(let i=start+deleteCount; i<this.length; i++) {
|
|
124
|
+
this.set(i + move, this.get(i));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Delete elements past end of new list
|
|
128
|
+
const originalLength = this.length;
|
|
129
|
+
for(let i=originalLength+move; i<originalLength; i++) {
|
|
130
|
+
this.set(i, undefined);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Insert new elements starting at start
|
|
135
|
+
for(let i=start; i<items.length; i++) {
|
|
136
|
+
this.set(i, items[i]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
filter(predicate: (value: any, index: number, array: MultyxClientList) => boolean) {
|
|
142
|
+
const keep = [];
|
|
143
|
+
for(let i=0; i<this.length; i++) {
|
|
144
|
+
keep.push(predicate(this.get(i), i, this));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let negativeOffset = 0;
|
|
148
|
+
for(let i=0; i<keep.length; i++) {
|
|
149
|
+
if(keep[i] && negativeOffset) this.set(i - negativeOffset, this.get(i));
|
|
150
|
+
if(!keep[i]) negativeOffset--;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
map(callbackfn: (value: any, index: number, array: MultyxClientList) => any) {
|
|
155
|
+
for(let i=0; i<this.length; i++) {
|
|
156
|
+
this.set(i, callbackfn(this.get(i), i, this));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
flat() {
|
|
161
|
+
for(let i=0; i<this.length; i++) {
|
|
162
|
+
const item = this.get(i);
|
|
163
|
+
|
|
164
|
+
if(item instanceof MultyxClientList) {
|
|
165
|
+
for(let j=0; j<item.length; j++) {
|
|
166
|
+
i++;
|
|
167
|
+
this.set(i, item[j]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
reduce(callbackfn: (accumulator: any, currentValue: any, index: number, array: MultyxClientList) => any, startingAccumulator: any) {
|
|
174
|
+
for(let i=0; i<this.length; i++) {
|
|
175
|
+
startingAccumulator = callbackfn(startingAccumulator, this.get(i), i, this);
|
|
176
|
+
}
|
|
177
|
+
return startingAccumulator;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
reduceRight(callbackfn: (accumulator: any, currentValue: any, index: number, array: MultyxClientList) => any, startingAccumulator: any) {
|
|
181
|
+
for(let i=this.length-1; i>=0; i--) {
|
|
182
|
+
startingAccumulator = callbackfn(startingAccumulator, this.get(i), i, this);
|
|
183
|
+
}
|
|
184
|
+
return startingAccumulator;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
reverse() {
|
|
188
|
+
let right = this.length-1;
|
|
189
|
+
for(let left=0; left<right; left++) {
|
|
190
|
+
const a = this.get(left);
|
|
191
|
+
const b = this.get(right);
|
|
192
|
+
this.set(left, b);
|
|
193
|
+
this.set(right, a);
|
|
194
|
+
}
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
forEach(callbackfn: (value: any, index: number, array: MultyxClientList) => void) {
|
|
199
|
+
for(let i=0; i<this.length; i++) {
|
|
200
|
+
callbackfn(this.get(i), i, this);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
every(predicate: (value: any, index: number, array: MultyxClientList) => boolean) {
|
|
205
|
+
for(let i=0; i<this.length; i++) {
|
|
206
|
+
if(!predicate(this.get(i), i, this)) return false;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
some(predicate: (value: any, index: number, array: MultyxClientList) => boolean) {
|
|
212
|
+
for(let i=0; i<this.length; i++) {
|
|
213
|
+
if(predicate(this.get(i), i, this)) return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
find(predicate: (value: any, index: number, array: MultyxClientList) => boolean) {
|
|
219
|
+
for(let i=0; i<this.length; i++) {
|
|
220
|
+
if(predicate(this.get(i), i, this)) return this.get(i);
|
|
221
|
+
}
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
findIndex(predicate: (value: any, index: number, array: MultyxClientList) => boolean) {
|
|
226
|
+
for(let i=0; i<this.length; i++) {
|
|
227
|
+
if(predicate(this.get(i), i, this)) return i;
|
|
228
|
+
}
|
|
229
|
+
return -1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
deorder(): MultyxClientItem[] {
|
|
233
|
+
const values = [];
|
|
234
|
+
for(const index in this.object) {
|
|
235
|
+
values.push(this.get(index));
|
|
236
|
+
}
|
|
237
|
+
return values;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
deorderEntries(): [number, MultyxClientItem][] {
|
|
241
|
+
const values = [];
|
|
242
|
+
for(const index in this.object) {
|
|
243
|
+
values.push([parseInt(index), this.get(index)]);
|
|
244
|
+
}
|
|
245
|
+
return values;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
entries(): [any, number][] {
|
|
249
|
+
const entryList: [any, number][] = [];
|
|
250
|
+
for(let i=0; i<this.length; i++) {
|
|
251
|
+
entryList.push([this.get(i), i]);
|
|
252
|
+
}
|
|
253
|
+
return entryList;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
keys(): number[] {
|
|
257
|
+
return Array(this.length).fill(0).map((_, i) => i);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
/* Native methods to allow MultyxClientList to be treated as array */
|
|
262
|
+
[Symbol.iterator](): Iterator<MultyxClientItem> {
|
|
263
|
+
const values = [];
|
|
264
|
+
for(let i=0; i<this.length; i++) values[i] = this.get(i);
|
|
265
|
+
return values[Symbol.iterator]();
|
|
266
|
+
}
|
|
267
|
+
toString = () => this.value.toString();
|
|
268
|
+
valueOf = () => this.value;
|
|
269
|
+
[Symbol.toPrimitive] = () => this.value;
|
|
270
|
+
}
|