@woosh/meep-engine 2.50.1 → 2.50.3

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.
Files changed (78) hide show
  1. package/README.md +42 -0
  2. package/editor/Editor.js +5 -1
  3. package/editor/SelectionVisualizer.js +9 -3
  4. package/editor/tools/paint/TerrainTexturePaintTool.js +6 -1
  5. package/editor/view/EditorView.js +5 -1
  6. package/editor/view/ecs/EntityList.js +48 -37
  7. package/editor/view/makeEntityDecorators.js +125 -0
  8. package/package.json +7 -3
  9. package/samples/generation/README.md +6 -0
  10. package/{src/generation/example → samples/generation}/SampleGenerator0.js +60 -46
  11. package/{src/generation/example → samples/generation}/filters/SampleGroundMoistureFilter.js +14 -12
  12. package/samples/generation/filters/SampleNoise20_0.js +3 -0
  13. package/{src/generation/example → samples/generation}/generators/interactive/mir_generator_place_buff_objects.js +29 -23
  14. package/{src/generation/example → samples/generation}/generators/mir_generator_place_bases.js +33 -25
  15. package/{src/generation/example → samples/generation}/generators/mir_generator_place_road_decorators.js +16 -14
  16. package/samples/generation/generators/mir_generator_place_starting_point.js +60 -0
  17. package/{src/generation/example → samples/generation}/grid/configureMirGrid.js +2 -2
  18. package/{src/generation/example → samples/generation}/main.js +29 -28
  19. package/{src/generation/example → samples/generation}/rules/matcher_not_play_area.js +1 -1
  20. package/samples/generation/rules/matcher_play_area.js +5 -0
  21. package/{src/generation/example → samples/generation}/rules/matcher_tag_not_traversable.js +1 -1
  22. package/samples/generation/rules/matcher_tag_occupied.js +5 -0
  23. package/samples/generation/rules/matcher_tag_traversable.js +5 -0
  24. package/{src/generation/example → samples/generation}/rules/matcher_tag_traversable_unoccupied.js +1 -1
  25. package/{src/generation/example → samples/generation}/rules/matcher_tag_unoccupied.js +1 -1
  26. package/{src/generation/example → samples/generation}/rules/mir_matcher_attack_corridor.js +3 -3
  27. package/{src/generation/example → samples/generation}/themes/SampleTheme0.js +57 -47
  28. package/{src/generation/example → samples/generation}/themes/SampleTheme1.js +4 -4
  29. package/{src/generation/example → samples/generation}/themes/SampleTheme2.js +4 -4
  30. package/src/core/cache/Cache.d.ts +4 -0
  31. package/src/core/cache/Cache.js +31 -15
  32. package/src/core/cache/Cache.spec.js +33 -0
  33. package/src/core/cache/FrequencySketch.js +26 -23
  34. package/src/core/cache/FrequencySketch.spec.js +5 -0
  35. package/src/core/cache/LoadingCache.d.ts +2 -0
  36. package/src/core/cache/LoadingCache.js +16 -7
  37. package/src/core/cache/LoadingCache.spec.js +24 -0
  38. package/src/core/collection/HashMap.d.ts +2 -0
  39. package/src/core/collection/HashMap.spec.js +51 -2
  40. package/src/core/collection/list/List.d.ts +13 -1
  41. package/src/core/collection/list/List.js +702 -684
  42. package/src/core/collection/list/List.spec.js +93 -0
  43. package/src/core/collection/map/AsyncLoadingCache.js +1 -0
  44. package/src/core/fsm/simple/SimpleStateMachine.js +23 -22
  45. package/src/core/function/extractFunctionBody.spec.js +15 -0
  46. package/src/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +69 -33
  47. package/src/core/geom/2d/convex-hull/convex_hull_monotone_2d.spec.js +33 -0
  48. package/src/core/geom/2d/convex-hull/orientation3.js +55 -10
  49. package/src/core/process/matcher/Matchers.js +1 -1
  50. package/src/engine/asset/loaders/image/png/PNG.spec.js +5 -0
  51. package/src/engine/asset/loaders/image/png/PNGReader.spec.js +5 -0
  52. package/src/engine/asset/loaders/image/png/crc.spec.js +15 -0
  53. package/src/engine/ecs/EntityBlueprint.js +4 -0
  54. package/src/engine/ecs/EntityBuilder.js +48 -49
  55. package/src/engine/ecs/EntityBuilderFlags.js +2 -2
  56. package/src/engine/ecs/guid/GUID.js +1 -0
  57. package/src/engine/ecs/{Blueprint.js → storage/json/Blueprint.js} +1 -1
  58. package/src/engine/ecs/{EntityFactory.js → storage/json/EntityFactory.js} +1 -1
  59. package/src/engine/ecs/storage/{JSONDeSerializer.js → json/JSONDeSerializer.js} +4 -4
  60. package/src/engine/ecs/storage/json/README.md +5 -0
  61. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.spec.js +5 -0
  62. package/src/engine/intelligence/behavior/Behavior.d.ts +5 -0
  63. package/src/engine/intelligence/behavior/Behavior.js +19 -0
  64. package/src/engine/intelligence/behavior/SelectorBehavior.js +4 -2
  65. package/src/engine/intelligence/behavior/composite/ParallelBehavior.js +2 -0
  66. package/src/engine/intelligence/behavior/composite/SequenceBehavior.js +3 -1
  67. package/src/engine/intelligence/behavior/decorator/AbstractDecoratorBehavior.js +2 -3
  68. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.js +2 -2
  69. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +1 -1
  70. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +2 -2
  71. package/src/view/common/VirtualListView.js +2 -0
  72. package/src/generation/example/filters/SampleNoise20_0.js +0 -3
  73. package/src/generation/example/generators/mir_generator_place_starting_point.js +0 -52
  74. package/src/generation/example/rules/matcher_play_area.js +0 -5
  75. package/src/generation/example/rules/matcher_tag_occupied.js +0 -5
  76. package/src/generation/example/rules/matcher_tag_traversable.js +0 -5
  77. /package/{src/generation/example → samples/generation}/grid/MirGridLayers.js +0 -0
  78. /package/src/engine/ecs/storage/{JSONSerializer.js → json/JSONSerializer.js} +0 -0
@@ -11,651 +11,625 @@ import { arrayIndexByEquality } from "../array/arrayIndexByEquality.js";
11
11
 
12
12
 
13
13
  /**
14
- * @callback List~ConsumerCallback
15
- * @template T
16
- * @param {T} element
17
- * @param {number} [index]
18
- */
19
-
20
- /**
14
+ *
21
15
  * List structure with event signals for observing changes.
22
- * @param {Array.<T>} [array]
23
16
  * @template T
24
- * @constructor
25
- * @property {{added: Signal, removed: Signal}} on
26
17
  */
27
- function List(array) {
28
- this.on = {
18
+ class List {
19
+ /**
20
+ * @readonly
21
+ */
22
+ on = {
23
+ /**
24
+ * @type {Signal<T,number>}
25
+ */
29
26
  added: new Signal(),
27
+ /**
28
+ * @type {Signal<T,number>}
29
+ */
30
30
  removed: new Signal()
31
31
  };
32
32
 
33
33
  /**
34
- * @type {T[]}
34
+ * @param {T[]} [array]
35
+ * @constructor
35
36
  */
36
- this.data = array !== undefined ? array.slice() : [];
37
+ constructor(array) {
38
+
39
+ /**
40
+ * @type {T[]}
41
+ */
42
+ this.data = array !== undefined ? array.slice() : [];
43
+
44
+ /**
45
+ *
46
+ * @type {number}
47
+ */
48
+ this.length = this.data.length;
49
+ }
37
50
 
38
51
  /**
39
- *
40
- * @type {number}
52
+ * Retrieve element at a given position in the list
53
+ * @param {number} index
54
+ * @returns {T}
41
55
  */
42
- this.length = this.data.length;
43
- }
56
+ get(index) {
57
+ return this.data[index];
58
+ }
44
59
 
45
- /**
46
- *
47
- * @param {number} index
48
- * @returns {T}
49
- */
50
- List.prototype.get = function (index) {
51
- return this.data[index];
52
- };
60
+ /**
61
+ * Set element at a given position inside the list
62
+ * @param {number} index
63
+ * @param {T} value
64
+ */
65
+ set(index, value) {
66
+ const oldValue = this.data[index];
53
67
 
54
- /**
55
- *
56
- * @param {number} index
57
- * @param {T} value
58
- */
59
- List.prototype.set = function (index, value) {
60
- const oldValue = this.data[index];
68
+ if (oldValue !== undefined) {
69
+ this.on.removed.send2(oldValue, index)
70
+ } else {
71
+ if (index >= this.length) {
61
72
 
62
- if (oldValue !== undefined) {
63
- this.on.removed.send2(oldValue, index)
64
- } else {
65
- if (index >= this.length) {
73
+ this.length = index + 1;
66
74
 
67
- this.length = index + 1;
75
+ if (index > this.length) {
76
+ console.error(`Overflow, attempted to set element at ${index} past the list length(=${this.length})`);
77
+ }
68
78
 
69
- if (index > this.length) {
70
- console.error(`Overflow, attempted to set element at ${index} past the list length(=${this.length})`);
71
79
  }
72
-
73
80
  }
81
+
82
+ this.data[index] = value;
83
+
84
+ this.on.added.send2(value, index);
74
85
  }
75
86
 
76
- this.data[index] = value;
87
+ /**
88
+ *
89
+ * @param {T} el
90
+ */
91
+ add(el) {
92
+ this.data.push(el);
93
+ const oldLength = this.length;
77
94
 
78
- this.on.added.send2(value, index);
79
- };
95
+ this.length++;
80
96
 
81
- /**
82
- *
83
- * @param {T} el
84
- */
85
- List.prototype.add = function (el) {
86
- this.data.push(el);
87
- const oldLength = this.length;
97
+ this.on.added.send2(el, oldLength);
98
+ return this;
99
+ }
88
100
 
89
- this.length++;
101
+ /**
102
+ * Only add element if it doesn't already exist in the list
103
+ * Useful when you wish to ensure uniqueness of elements inside the list
104
+ * Note that this operation is rather slow as it triggers a linear scan
105
+ * If the list gets large - consider using a {@link Set} class instead
106
+ * @param {T} el
107
+ * @return {boolean}
108
+ */
109
+ addUnique(el) {
110
+ if (this.contains(el)) {
111
+ return false;
112
+ }
90
113
 
91
- this.on.added.send2(el, oldLength);
92
- return this;
93
- };
114
+ this.add(el);
94
115
 
95
- /**
96
- *
97
- * @param {T} el
98
- * @return {boolean}
99
- */
100
- List.prototype.addUnique = function (el) {
101
- if (this.contains(el)) {
102
- return false;
116
+ return true;
103
117
  }
104
118
 
105
- this.add(el);
119
+ /**
120
+ * Insert element at a specific position into the list
121
+ * This operation will result in a larger list
122
+ * @param {number} index
123
+ * @param {T} el
124
+ * @returns {List}
125
+ * @throws {Error} when trying to insert past list end
126
+ */
127
+ insert(index, el) {
106
128
 
107
- return true;
108
- }
129
+ if (index > this.length) {
130
+ console.error(`Overflow, attempted to insert element at ${index} past the list length(=${this.length})`);
131
+ this.length = index;
132
+ }
109
133
 
110
- /**
111
- * Insert element at a specific position into the list
112
- * @param {number} index
113
- * @param {T} el
114
- * @returns {List}
115
- * @throws {Error} when trying to insert past list end
116
- */
117
- List.prototype.insert = function (index, el) {
134
+ this.data.splice(index, 0, el);
118
135
 
119
- if (index > this.length) {
120
- console.error(`Overflow, attempted to insert element at ${index} past the list length(=${this.length})`);
121
- this.length = index + 1;
122
- }
136
+ this.length++;
123
137
 
124
- this.data.splice(index, 0, el);
138
+ this.on.added.send2(el, index);
125
139
 
126
- this.length++;
140
+ return this;
141
+ }
127
142
 
128
- this.on.added.send2(el, index);
143
+ /**
144
+ * Reduces the list to a subsection but removing everything before startIndex and everything after endIndex
145
+ * @param {int} startIndex
146
+ * @param {int} endIndex up to this index, not including it
147
+ * @returns {number}
148
+ */
149
+ crop(startIndex, endIndex) {
129
150
 
130
- return this;
131
- };
151
+ const data = this.data;
132
152
 
133
- /**
134
- * Reduces the list to a sub-section but removing everything before startIndex and everything after endIndex
135
- * @param {int} startIndex
136
- * @param {int} endIndex up to this index, not including it
137
- * @returns {number}
138
- */
139
- List.prototype.crop = function (startIndex, endIndex) {
153
+ const tail = data.splice(endIndex, this.length - endIndex);
140
154
 
141
- const data = this.data;
155
+ const head = data.splice(0, startIndex);
142
156
 
143
- const tail = data.splice(endIndex, this.length - endIndex);
157
+ this.length = endIndex - startIndex;
144
158
 
145
- const head = data.splice(0, startIndex);
146
159
 
147
- this.length = endIndex - startIndex;
160
+ const headLength = head.length;
148
161
 
162
+ const tailLength = tail.length;
149
163
 
150
- const headLength = head.length;
164
+ const onRemoved = this.on.removed;
151
165
 
152
- const tailLength = tail.length;
166
+ if (onRemoved.hasHandlers()) {
167
+ let i;
153
168
 
154
- const onRemoved = this.on.removed;
169
+ for (i = 0; i < headLength; i++) {
170
+ onRemoved.send2(head[i], i);
171
+ }
155
172
 
156
- if (onRemoved.hasHandlers()) {
157
- let i;
158
173
 
159
- for (i = 0; i < headLength; i++) {
160
- onRemoved.send2(head[i], i);
174
+ for (i = 0; i < tailLength; i++) {
175
+ onRemoved.send2(tail[i], endIndex + i);
176
+ }
161
177
  }
162
178
 
163
-
164
- for (i = 0; i < tailLength; i++) {
165
- onRemoved.send2(tail[i], endIndex + i);
166
- }
179
+ //return number of dropped elements
180
+ return headLength + tailLength;
167
181
  }
168
182
 
169
- //return number of dropped elements
170
- return headLength + tailLength;
171
- };
172
-
173
- /**
174
- * Replace the data, replacements is performed surgically, meaning that diff is computed and add/remove operations are performed on the set
175
- * This method is tailored to work well with visualisation as only elements that's missing from the new set is removed, and only elements that are new to are added
176
- * Conversely, relevant events are dispatched that a visualisation can observe. This results in fewer changes required to the visualisation
177
- * @param {T[]} newOutput
178
- */
179
- List.prototype.patch = function (newOutput) {
183
+ /**
184
+ * Replace the data, replacements is performed surgically, meaning that diff is computed and add/remove operations are performed on the set
185
+ * This method is tailored to work well with visualisation as only elements that's missing from the new set is removed, and only elements that are new to are added
186
+ * Conversely, relevant events are dispatched that a visualisation can observe. This results in fewer changes required to the visualisation
187
+ * @param {T[]} newOutput
188
+ */
189
+ patch(newOutput) {
180
190
 
181
- const data = this.data;
191
+ const data = this.data;
182
192
 
183
- const diff = arraySetDiff(data, newOutput, invokeObjectEquals);
193
+ const diff = arraySetDiff(data, newOutput, invokeObjectEquals);
184
194
 
185
- //resolve diff
186
- const removals = diff.uniqueA;
195
+ //resolve diff
196
+ const removals = diff.uniqueA;
187
197
 
188
- const removalCount = removals.length;
198
+ const removalCount = removals.length;
189
199
 
190
- for (let i = 0; i < removalCount; i++) {
191
- const item = removals[i];
200
+ for (let i = 0; i < removalCount; i++) {
201
+ const item = removals[i];
192
202
 
193
- this.removeOneOf(item);
194
- }
203
+ this.removeOneOf(item);
204
+ }
195
205
 
196
- const additions = diff.uniqueB;
206
+ const additions = diff.uniqueB;
197
207
 
198
- //sort additions by their position in the input
199
- additions.sort((a, b) => {
200
- const ai = this.indexOf(a);
201
- const bi = this.indexOf(b);
208
+ //sort additions by their position in the input
209
+ additions.sort((a, b) => {
210
+ const ai = this.indexOf(a);
211
+ const bi = this.indexOf(b);
202
212
 
203
- return bi - ai;
204
- });
213
+ return bi - ai;
214
+ });
205
215
 
206
- const additionsCount = additions.length;
216
+ const additionsCount = additions.length;
207
217
 
208
- for (let i = 0; i < additionsCount; i++) {
209
- const item = additions[i];
218
+ for (let i = 0; i < additionsCount; i++) {
219
+ const item = additions[i];
210
220
 
211
- //find where the item is in the input
212
- const inputIndex = this.indexOf(item);
221
+ //find where the item is in the input
222
+ const inputIndex = this.indexOf(item);
213
223
 
214
- let p = 0;
215
- for (let i = inputIndex - 1; i >= 0; i--) {
216
- const prev = this.get(i);
224
+ let p = 0;
225
+ for (let i = inputIndex - 1; i >= 0; i--) {
226
+ const prev = this.get(i);
217
227
 
218
- //look for previous item in the output
219
- const j = this.indexOf(prev);
228
+ //look for previous item in the output
229
+ const j = this.indexOf(prev);
220
230
 
221
- if (j !== -1) {
222
- p = j + 1;
223
- break;
231
+ if (j !== -1) {
232
+ p = j + 1;
233
+ break;
234
+ }
224
235
  }
236
+
237
+ this.insert(p, item);
225
238
  }
226
239
 
227
- this.insert(p, item);
228
240
  }
229
241
 
230
- };
231
-
232
- /**
233
- *
234
- * @param {Array.<T>} elements
235
- */
236
- List.prototype.addAll = function (elements) {
237
- const addedElementsCount = elements.length;
242
+ /**
243
+ *
244
+ * @param {Array.<T>} elements
245
+ */
246
+ addAll(elements) {
247
+ const addedElementsCount = elements.length;
238
248
 
239
- const added = this.on.added;
249
+ const added = this.on.added;
240
250
 
241
- if (added.hasHandlers()) {
242
- //only signal if there are listeners attached
243
- for (let i = 0; i < addedElementsCount; i++) {
244
- const element = elements[i];
245
- this.data.push(element);
246
- added.send2(element, this.length++);
251
+ if (added.hasHandlers()) {
252
+ //only signal if there are listeners attached
253
+ for (let i = 0; i < addedElementsCount; i++) {
254
+ const element = elements[i];
255
+ this.data.push(element);
256
+ added.send2(element, this.length++);
257
+ }
258
+ } else {
259
+ //no observers, we can add elements faster
260
+ Array.prototype.push.apply(this.data, elements);
261
+ this.length += addedElementsCount;
247
262
  }
248
- } else {
249
- //no observers, we can add elements faster
250
- Array.prototype.push.apply(this.data, elements);
251
- this.length += addedElementsCount;
252
263
  }
253
- };
254
264
 
255
- /**
256
- *
257
- * @param {Array.<T>} elements
258
- */
259
- List.prototype.addAllUnique = function (elements) {
260
- const data = this.data;
265
+ /**
266
+ *
267
+ * @param {Array.<T>} elements
268
+ */
269
+ addAllUnique(elements) {
270
+ const data = this.data;
261
271
 
262
- const unique = elements.filter(function (e, i) {
263
- return data.indexOf(e) === -1 && elements.indexOf(e, i + 1) === -1;
264
- });
272
+ const unique = elements.filter(function (e, i) {
273
+ return data.indexOf(e) === -1 && elements.indexOf(e, i + 1) === -1;
274
+ });
265
275
 
266
- this.addAll(unique);
267
- };
276
+ this.addAll(unique);
277
+ }
268
278
 
269
- /**
270
- *
271
- * @param {number} index
272
- * @param {number} removeCount
273
- * @returns {T[]}
274
- */
275
- List.prototype.removeMany = function (index, removeCount) {
276
- assert.equal(typeof index, 'number', `index must a number, instead was '${typeof index}'`);
277
- assert.ok(index < this.length || index < 0, `index(=${index}) out of range (${this.length})`);
279
+ /**
280
+ *
281
+ * @param {number} index
282
+ * @param {number} removeCount
283
+ * @returns {T[]}
284
+ */
285
+ removeMany(index, removeCount) {
286
+ assert.equal(typeof index, 'number', `index must a number, instead was '${typeof index}'`);
287
+ assert.ok(index < this.length || index < 0, `index(=${index}) out of range (${this.length})`);
278
288
 
279
- const els = this.data.splice(index, removeCount);
289
+ const els = this.data.splice(index, removeCount);
280
290
 
281
- const removedCount = els.length;
291
+ const removedCount = els.length;
282
292
 
283
- this.length -= removedCount;
293
+ this.length -= removedCount;
284
294
 
285
- assert.equal(this.length, this.data.length, `length(=${this.length}) is inconsistent with underlying data array length(=${this.data.length})`)
295
+ assert.equal(this.length, this.data.length, `length(=${this.length}) is inconsistent with underlying data array length(=${this.data.length})`)
286
296
 
287
- if (this.on.removed.hasHandlers()) {
288
- for (let i = 0; i < removedCount; i++) {
289
- const element = els[i];
297
+ if (this.on.removed.hasHandlers()) {
298
+ for (let i = 0; i < removedCount; i++) {
299
+ const element = els[i];
290
300
 
291
- this.on.removed.send2(element, index + i);
301
+ this.on.removed.send2(element, index + i);
302
+ }
292
303
  }
293
- }
294
304
 
295
- return els;
296
- };
305
+ return els;
306
+ }
297
307
 
298
- /**
299
- *
300
- * @param {number} index
301
- * @returns {T}
302
- */
303
- List.prototype.remove = function (index) {
304
- assert.equal(typeof index, 'number', `index must a number, instead was '${typeof index}'`);
305
- assert.ok(index < this.length || index < 0, `index(=${index}) out of range (${this.length})`);
308
+ /**
309
+ *
310
+ * @param {number} index
311
+ * @returns {T}
312
+ */
313
+ remove(index) {
314
+ assert.equal(typeof index, 'number', `index must a number, instead was '${typeof index}'`);
315
+ assert.ok(index < this.length || index < 0, `index(=${index}) out of range (${this.length})`);
306
316
 
307
- const els = this.data.splice(index, 1);
317
+ const els = this.data.splice(index, 1);
308
318
 
309
- this.length--;
319
+ this.length--;
310
320
 
311
- assert.equal(this.length, this.data.length, `length(=${this.length}) is inconsistent with underlying data array length(=${this.data.length})`)
321
+ assert.equal(this.length, this.data.length, `length(=${this.length}) is inconsistent with underlying data array length(=${this.data.length})`)
312
322
 
313
- const element = els[0];
323
+ const element = els[0];
314
324
 
315
- this.on.removed.send2(element, index);
325
+ this.on.removed.send2(element, index);
316
326
 
317
- return element;
318
- };
327
+ return element;
328
+ }
319
329
 
320
- /**
321
- *
322
- * @param {Object} a
323
- * @param {Object} b
324
- * @returns {boolean}
325
- */
326
- function objectsEqual(a, b) {
327
- return a === b || (typeof a.equals === "function" && a.equals(b));
328
- }
330
+ /**
331
+ *
332
+ * @param {T[]} elements
333
+ * @returns {boolean} True is all specified elements were found and removed, False if some elements were not present in the list
334
+ */
335
+ removeAll(elements) {
336
+ let i, il;
337
+ let j, jl;
329
338
 
330
- /**
331
- *
332
- * @param {Array.<T>} elements
333
- * @returns {boolean} True is all specified elements were found and removed, False if some elements were not present in the list
334
- */
335
- List.prototype.removeAll = function (elements) {
336
- let i, il;
337
- let j, jl;
339
+ il = elements.length;
340
+ jl = this.length;
338
341
 
339
- il = elements.length;
340
- jl = this.length;
342
+ let missCount = 0;
341
343
 
342
- let missCount = 0;
344
+ const data = this.data;
343
345
 
344
- const data = this.data;
346
+ main_loop: for (i = 0; i < il; i++) {
345
347
 
346
- main_loop: for (i = 0; i < il; i++) {
348
+ const expected = elements[i];
347
349
 
348
- const expected = elements[i];
350
+ for (j = jl - 1; j >= 0; j--) {
349
351
 
350
- for (j = jl - 1; j >= 0; j--) {
352
+ const actual = data[j];
351
353
 
352
- const actual = data[j];
354
+ if (objectsEqual(actual, expected)) {
353
355
 
354
- if (objectsEqual(actual, expected)) {
356
+ this.remove(j);
355
357
 
356
- this.remove(j);
358
+ jl--;
357
359
 
358
- jl--;
359
360
 
361
+ continue main_loop;
360
362
 
361
- continue main_loop;
363
+ }
362
364
 
363
365
  }
364
366
 
365
- }
366
367
 
368
+ missCount++;
369
+ }
367
370
 
368
- missCount++;
371
+ //some elements were not deleted
372
+ return missCount === 0;
369
373
  }
370
374
 
371
- //some elements were not deleted
372
- return missCount === 0;
373
- };
374
-
375
- function conditionEqualsViaMethod(v) {
376
- return this === v || this.equals(v);
377
- }
378
-
379
- function conditionEqualsStrict(v) {
380
- return this === v;
381
- }
382
-
383
- /**
384
- *
385
- * @param {T} value
386
- * @return {boolean}
387
- */
388
- List.prototype.removeOneOf = function (value) {
389
- if (typeof value === "object" && typeof value.equals === "function") {
390
- return this.removeOneIf(conditionEqualsViaMethod, value);
391
- } else {
392
- return this.removeOneIf(conditionEqualsStrict, value);
375
+ /**
376
+ *
377
+ * @param {T} value
378
+ * @return {boolean}
379
+ */
380
+ removeOneOf(value) {
381
+ if (typeof value === "object" && typeof value.equals === "function") {
382
+ return this.removeOneIf(conditionEqualsViaMethod, value);
383
+ } else {
384
+ return this.removeOneIf(conditionEqualsStrict, value);
385
+ }
393
386
  }
394
- };
395
387
 
396
- List.prototype.sort = function () {
397
- Array.prototype.sort.apply(this.data, arguments);
398
- return this;
399
- };
388
+ sort() {
389
+ Array.prototype.sort.apply(this.data, arguments);
390
+ return this;
391
+ }
400
392
 
401
- /**
402
- * Copy of this list
403
- * @returns {List.<T>}
404
- */
405
- List.prototype.clone = function () {
406
- return new List(this.data);
407
- };
393
+ /**
394
+ * Copy of this list
395
+ * @returns {List.<T>}
396
+ */
397
+ clone() {
398
+ return new List(this.data);
399
+ }
408
400
 
409
- /**
410
- *
411
- * @param {function(element:T):boolean} condition
412
- * @returns {boolean}
413
- */
414
- List.prototype.some = function (condition) {
415
- const l = this.length;
416
- const data = this.data;
417
- for (let i = 0; i < l; i++) {
418
- if (condition(data[i])) {
419
- return true;
401
+ /**
402
+ *
403
+ * @param {function(element:T):boolean} condition
404
+ * @returns {boolean}
405
+ */
406
+ some(condition) {
407
+ const l = this.length;
408
+ const data = this.data;
409
+ for (let i = 0; i < l; i++) {
410
+ if (condition(data[i])) {
411
+ return true;
412
+ }
420
413
  }
414
+ return false;
421
415
  }
422
- return false;
423
- };
424
416
 
425
- /**
426
- *
427
- * @param {function(T):boolean} condition must return boolean value
428
- */
429
- List.prototype.removeIf = function (condition) {
430
- let l = this.length;
431
- const data = this.data;
432
- for (let i = 0; i < l; i++) {
433
- if (condition(data[i])) {
434
- this.remove(i);
435
- i--;
436
- l--;
417
+ /**
418
+ *
419
+ * @param {function(T):boolean} condition must return boolean value
420
+ * @param {*} [thisArg]
421
+ */
422
+ removeIf(condition, thisArg) {
423
+ assert.isFunction(condition, 'condition');
424
+
425
+ let l = this.length;
426
+ const data = this.data;
427
+ for (let i = 0; i < l; i++) {
428
+ const element = data[i];
429
+
430
+ if (condition.call(thisArg, element)) {
431
+ this.remove(i);
432
+ i--;
433
+ l--;
434
+ }
437
435
  }
438
436
  }
439
- };
440
437
 
441
- /**
442
- *
443
- * @param {function(T):boolean} condition
444
- * @param {*} [thisArg]
445
- * @return {boolean}
446
- */
447
- List.prototype.removeOneIf = function (condition, thisArg) {
448
- const l = this.length;
449
- const data = this.data;
438
+ /**
439
+ *
440
+ * @param {function(T):boolean} condition
441
+ * @param {*} [thisArg]
442
+ * @return {boolean}
443
+ */
444
+ removeOneIf(condition, thisArg) {
445
+ const l = this.length;
446
+ const data = this.data;
450
447
 
451
- for (let i = 0; i < l; i++) {
452
- const element = data[i];
448
+ for (let i = 0; i < l; i++) {
449
+ const element = data[i];
453
450
 
454
- if (condition.call(thisArg, element)) {
455
- this.remove(i);
456
- return true;
451
+ if (condition.call(thisArg, element)) {
452
+ this.remove(i);
453
+ return true;
454
+ }
457
455
  }
458
- }
459
456
 
460
- return false;
461
- };
457
+ return false;
458
+ }
462
459
 
463
- /**
464
- * INVARIANT: List length must not change during the traversal
465
- * @param {function(el:T, index:number):?} f
466
- * @param {*} [thisArg]
467
- */
468
- List.prototype.forEach = function (f, thisArg) {
469
- const l = this.length;
470
- const data = this.data;
460
+ /**
461
+ * INVARIANT: List length must not change during the traversal
462
+ * @param {function(el:T, index:number):?} f
463
+ * @param {*} [thisArg]
464
+ */
465
+ forEach(f, thisArg) {
466
+ const l = this.length;
467
+ const data = this.data;
471
468
 
472
- for (let i = 0; i < l; i++) {
469
+ for (let i = 0; i < l; i++) {
473
470
 
474
- f.call(thisArg, data[i], i);
471
+ f.call(thisArg, data[i], i);
475
472
 
473
+ }
476
474
  }
477
- };
478
475
 
479
- /**
480
- * @param {function(*,T):*} f
481
- * @param {*} initial
482
- * @returns {*}
483
- */
484
- List.prototype.reduce = function (f, initial) {
485
- let t = initial;
486
- this.forEach(function (v) {
487
- t = f(t, v);
488
- });
489
- return t;
490
- };
476
+ /**
477
+ * @param {function(*,T):*} f
478
+ * @param {*} initial
479
+ * @returns {*}
480
+ */
481
+ reduce(f, initial) {
482
+ let t = initial;
483
+ this.forEach(function (v) {
484
+ t = f(t, v);
485
+ });
486
+ return t;
487
+ }
491
488
 
492
- /**
493
- *
494
- * @param {function(T):boolean} f
495
- * @returns {Array.<T>}
496
- */
497
- List.prototype.filter = function (f) {
498
- return this.data.filter(f);
499
- };
489
+ /**
490
+ *
491
+ * @param {function(T):boolean} f
492
+ * @returns {Array.<T>}
493
+ */
494
+ filter(f) {
495
+ return this.data.filter(f);
496
+ }
500
497
 
501
- /**
502
- *
503
- * @param {function(el:T):boolean} matcher
504
- * @returns {T|undefined}
505
- */
506
- List.prototype.find = function (matcher) {
507
- const data = this.data;
508
- let i = 0;
509
- const l = this.length;
510
- for (; i < l; i++) {
511
- const el = data[i];
512
- if (matcher(el)) {
513
- return el;
498
+ /**
499
+ *
500
+ * @param {function(el:T):boolean} matcher
501
+ * @returns {T|undefined}
502
+ */
503
+ find(matcher) {
504
+ const data = this.data;
505
+ let i = 0;
506
+ const l = this.length;
507
+ for (; i < l; i++) {
508
+ const el = data[i];
509
+ if (matcher(el)) {
510
+ return el;
511
+ }
514
512
  }
515
- }
516
513
 
517
- return undefined;
518
- };
514
+ return undefined;
515
+ }
519
516
 
520
- /**
521
- *
522
- * @param {function(T):boolean} matcher
523
- * @returns {number} Index of the first match or -1
524
- */
525
- List.prototype.findIndex = function (matcher) {
526
- const data = this.data;
517
+ /**
518
+ *
519
+ * @param {function(T):boolean} matcher
520
+ * @returns {number} Index of the first match or -1
521
+ */
522
+ findIndex(matcher) {
523
+ const data = this.data;
527
524
 
528
- let i = 0;
525
+ let i = 0;
529
526
 
530
- const l = this.length;
527
+ const l = this.length;
531
528
 
532
- for (; i < l; i++) {
533
- const el = data[i];
534
- if (matcher(el)) {
535
- return i;
529
+ for (; i < l; i++) {
530
+ const el = data[i];
531
+ if (matcher(el)) {
532
+ return i;
533
+ }
536
534
  }
537
- }
538
535
 
539
- return -1;
540
- };
541
-
542
- /**
543
- *
544
- * @param {function} matcher
545
- * @param {List~ConsumerCallback<T>} callback
546
- */
547
- List.prototype.visitFirstMatch = function (matcher, callback) {
548
- const data = this.data;
549
- let i = 0;
550
- const l = this.length;
551
- for (; i < l; i++) {
552
- const el = data[i];
553
- if (matcher(el)) {
554
- callback(el, i);
555
- return;
556
- }
557
- }
558
- };
559
- /**
560
- *
561
- * @param {T} v
562
- * @returns {boolean}
563
- */
564
- List.prototype.contains = function (v) {
565
- return this.data.indexOf(v) !== -1;
566
- };
567
-
568
- /**
569
- * Does the list contain at least one of the given options?
570
- * @param {T[]} options
571
- * @returns {boolean}
572
- */
573
- List.prototype.containsAny = function (options) {
574
- const n = options.length;
536
+ return -1;
537
+ }
575
538
 
576
- for (let i = 0; i < n; i++) {
577
- const option = options[i];
578
- if (this.contains(option)) {
579
- return true;
539
+ /**
540
+ *
541
+ * @param {function(el:T):boolean} matcher
542
+ * @param {function(el:T, index:number):*} callback
543
+ */
544
+ visitFirstMatch(matcher, callback) {
545
+ const data = this.data;
546
+ let i = 0;
547
+ const l = this.length;
548
+ for (; i < l; i++) {
549
+ const el = data[i];
550
+ if (matcher(el)) {
551
+ callback(el, i);
552
+ return;
553
+ }
580
554
  }
581
555
  }
582
556
 
583
- return false;
584
- };
585
-
586
- /**
587
- * List has no elements
588
- * @returns {boolean}
589
- */
590
- List.prototype.isEmpty = function () {
591
- return this.length <= 0;
592
- };
593
-
594
- /**
595
- *
596
- * @param {T} el
597
- * @returns {number}
598
- */
599
- List.prototype.indexOf = function (el) {
600
- return this.data.indexOf(el);
601
- };
602
-
603
- /**
604
- * @template R
605
- * @param {function(T):R} callback
606
- * @param {*} [thisArg]
607
- * @returns {R[]}
608
- */
609
- List.prototype.map = function (callback, thisArg) {
610
- const result = [];
611
- const data = this.data;
612
- const l = this.length;
557
+ /**
558
+ *
559
+ * @param {T} v
560
+ * @returns {boolean}
561
+ */
562
+ contains(v) {
563
+ return this.data.indexOf(v) !== -1;
564
+ }
613
565
 
614
- for (let i = 0; i < l; i++) {
566
+ /**
567
+ * Does the list contain at least one of the given options?
568
+ * @param {T[]} options
569
+ * @returns {boolean}
570
+ */
571
+ containsAny(options) {
572
+ const n = options.length;
615
573
 
616
- const datum = data[i];
617
- if (datum !== undefined) {
618
- result[i] = callback.call(thisArg, datum, i);
574
+ for (let i = 0; i < n; i++) {
575
+ const option = options[i];
576
+ if (this.contains(option)) {
577
+ return true;
578
+ }
619
579
  }
620
- }
621
580
 
622
- return result;
623
- };
581
+ return false;
582
+ }
624
583
 
625
- /**
626
- *
627
- * @param {function(element:T,index:number)} callback
628
- * @param {*} [thisArg]
629
- */
630
- List.prototype.resetViaCallback = function (callback, thisArg) {
631
- const length = this.length;
632
- if (length > 0) {
584
+ /**
585
+ * List has no elements
586
+ * @returns {boolean}
587
+ */
588
+ isEmpty() {
589
+ return this.length <= 0;
590
+ }
633
591
 
634
- const removed = this.on.removed;
592
+ /**
593
+ *
594
+ * @param {T} el
595
+ * @returns {number}
596
+ */
597
+ indexOf(el) {
598
+ return this.data.indexOf(el);
599
+ }
635
600
 
636
- const oldElements = this.data;
601
+ /**
602
+ * @template R
603
+ * @param {function(T):R} callback
604
+ * @param {*} [thisArg]
605
+ * @returns {R[]}
606
+ */
607
+ map(callback, thisArg) {
608
+ const result = [];
609
+ const data = this.data;
610
+ const l = this.length;
637
611
 
638
- //only signal if there are listeners attached
639
- for (let i = length - 1; i >= 0; i--) {
640
- const element = oldElements[i];
641
- // decrement data length gradually to allow handlers access to the rest of the elements
642
- this.data.length = i;
643
- this.length = i;
644
- removed.send2(element, i);
612
+ for (let i = 0; i < l; i++) {
645
613
 
646
- callback.call(thisArg, element, i);
614
+ const datum = data[i];
615
+ if (datum !== undefined) {
616
+ result[i] = callback.call(thisArg, datum, i);
617
+ }
647
618
  }
648
619
 
620
+ return result;
649
621
  }
650
- };
651
-
652
- List.prototype.reset = function () {
653
- const length = this.length;
654
- if (length > 0) {
655
622
 
656
- const removed = this.on.removed;
623
+ /**
624
+ *
625
+ * @param {function(element:T,index:number)} callback
626
+ * @param {*} [thisArg]
627
+ */
628
+ resetViaCallback(callback, thisArg) {
629
+ const length = this.length;
630
+ if (length > 0) {
657
631
 
658
- if (removed.hasHandlers()) {
632
+ const removed = this.on.removed;
659
633
 
660
634
  const oldElements = this.data;
661
635
 
@@ -666,281 +640,325 @@ List.prototype.reset = function () {
666
640
  this.data.length = i;
667
641
  this.length = i;
668
642
  removed.send2(element, i);
643
+
644
+ callback.call(thisArg, element, i);
669
645
  }
670
646
 
671
- } else {
672
- this.data = [];
673
- this.length = 0;
674
647
  }
675
-
676
648
  }
677
- };
678
649
 
679
- /**
680
- *
681
- * @param {List<T>} other
682
- * @param {function} [removeCallback]
683
- * @param {*} [thisArg] Used on removeCallback
684
- */
685
- List.prototype.deepCopy = function (other, removeCallback, thisArg) {
686
- assert.notEqual(other, undefined, 'other is undefined');
687
- assert.notEqual(other, null, 'other is null');
650
+ reset() {
651
+ const length = this.length;
652
+ if (length > 0) {
688
653
 
689
- const newData = [];
654
+ const removed = this.on.removed;
690
655
 
691
- const otherItems = other.asArray();
692
- const nOtherItems = otherItems.length;
656
+ if (removed.hasHandlers()) {
693
657
 
694
- const thisItems = this.data;
695
- let nThisItems = this.length;
658
+ const oldElements = this.data;
696
659
 
697
- //walk existing elements and see what can be kept
698
- for (let i = 0; i < nThisItems; i++) {
699
- const a = thisItems[i];
660
+ //only signal if there are listeners attached
661
+ for (let i = length - 1; i >= 0; i--) {
662
+ const element = oldElements[i];
663
+ // decrement data length gradually to allow handlers access to the rest of the elements
664
+ this.data.length = i;
665
+ this.length = i;
666
+ removed.send2(element, i);
667
+ }
700
668
 
701
- const index = arrayIndexByEquality(otherItems, a, invokeObjectEquals);
669
+ } else {
670
+ this.data = [];
671
+ this.length = 0;
672
+ }
702
673
 
703
- if (index !== -1) {
704
- newData[index] = a;
705
- } else if (typeof removeCallback === "function") {
706
- removeCallback.call(thisArg, a);
707
674
  }
708
675
  }
709
676
 
710
- //fill in the blanks
711
- for (let i = 0; i < nOtherItems; i++) {
712
- if (newData[i] === undefined) {
713
- const otherItem = otherItems[i];
714
- newData[i] = otherItem.clone();
677
+ /**
678
+ * Note that elements must have .clone method for this to work
679
+ * @param {List<T>} other
680
+ * @param {function} [removeCallback]
681
+ * @param {*} [thisArg] Used on removeCallback
682
+ */
683
+ deepCopy(other, removeCallback, thisArg) {
684
+ assert.notEqual(other, undefined, 'other is undefined');
685
+ assert.notEqual(other, null, 'other is null');
686
+
687
+ const newData = [];
688
+
689
+ const otherItems = other.asArray();
690
+ const nOtherItems = otherItems.length;
691
+
692
+ const thisItems = this.data;
693
+ let nThisItems = this.length;
694
+
695
+ //walk existing elements and see what can be kept
696
+ for (let i = 0; i < nThisItems; i++) {
697
+ const a = thisItems[i];
698
+
699
+ const index = arrayIndexByEquality(otherItems, a, invokeObjectEquals);
700
+
701
+ if (index !== -1) {
702
+ newData[index] = a;
703
+ } else if (typeof removeCallback === "function") {
704
+ removeCallback.call(thisArg, a);
705
+ }
715
706
  }
716
- }
717
707
 
718
- this.reset();
719
- this.addAll(newData);
720
- };
708
+ //fill in the blanks
709
+ for (let i = 0; i < nOtherItems; i++) {
710
+ if (newData[i] === undefined) {
711
+ const otherItem = otherItems[i];
712
+ newData[i] = otherItem.clone();
713
+ }
714
+ }
721
715
 
722
- /**
723
- *
724
- * @param {List<T>|T[]} other
725
- */
726
- List.prototype.copy = function (other) {
727
- if (this !== other) {
728
716
  this.reset();
729
- if (other.length > 0) {
730
- if (other instanceof List) {
731
- this.addAll(other.data);
732
- } else {
733
- this.addAll(other);
717
+ this.addAll(newData);
718
+ }
719
+
720
+ /**
721
+ *
722
+ * @param {List<T>|T[]} other
723
+ */
724
+ copy(other) {
725
+ if (this !== other) {
726
+ this.reset();
727
+ if (other.length > 0) {
728
+ if (other instanceof List) {
729
+ this.addAll(other.data);
730
+ } else {
731
+ this.addAll(other);
732
+ }
734
733
  }
735
734
  }
736
735
  }
737
- };
738
736
 
739
- /**
740
- * NOTE: do not modify resulting array
741
- * @returns {T[]}
742
- */
743
- List.prototype.asArray = function () {
744
- return this.data;
745
- };
737
+ /**
738
+ * NOTE: do not modify resulting array
739
+ * @returns {T[]}
740
+ */
741
+ asArray() {
742
+ return this.data;
743
+ }
746
744
 
747
- List.prototype.toJSON = function () {
748
- return JSON.parse(JSON.stringify(this.data));
749
- };
745
+ toJSON() {
746
+ return JSON.parse(JSON.stringify(this.data));
747
+ }
750
748
 
751
- /**
752
- *
753
- * @param {object} json
754
- * @param {function} constructor
755
- */
756
- List.prototype.fromJSON = function (json, constructor) {
757
- this.reset();
749
+ /**
750
+ *
751
+ * @param {[]} json
752
+ * @param {function} constructor
753
+ */
754
+ fromJSON(json, constructor) {
755
+ this.reset();
758
756
 
759
- assert.isArray(json, 'json');
757
+ assert.isArray(json, 'json');
760
758
 
761
- if (typeof constructor === "function") {
762
- this.addAll(json.map(function (elJSON) {
763
- const el = new constructor();
764
- el.fromJSON(elJSON);
765
- return el;
766
- }));
767
- } else {
768
- this.addAll(json);
759
+ if (typeof constructor === "function") {
760
+ this.addAll(json.map(function (elJSON) {
761
+ const el = new constructor();
762
+ el.fromJSON(elJSON);
763
+ return el;
764
+ }));
765
+ } else {
766
+ this.addAll(json);
767
+ }
769
768
  }
770
- };
771
769
 
770
+ /**
771
+ *
772
+ * @param {BinaryBuffer} buffer
773
+ */
774
+ toBinaryBuffer(buffer) {
775
+ const n = this.length;
772
776
 
773
- /**
774
- *
775
- * @param {BinaryBuffer} buffer
776
- */
777
- List.prototype.toBinaryBuffer = function (buffer) {
778
- const n = this.length;
777
+ buffer.writeUint32(n);
779
778
 
780
- buffer.writeUint32(n);
779
+ for (let i = 0; i < n; i++) {
780
+ const item = this.data[i];
781
781
 
782
- for (let i = 0; i < n; i++) {
783
- const item = this.data[i];
782
+ if (typeof item.toBinaryBuffer !== "function") {
783
+ throw new Error('item.toBinaryBuffer is not a function');
784
+ }
784
785
 
785
- if (typeof item.toBinaryBuffer !== "function") {
786
- throw new Error('item.toBinaryBuffer is not a function');
786
+ item.toBinaryBuffer(buffer);
787
787
  }
788
-
789
- item.toBinaryBuffer(buffer);
790
788
  }
791
- };
792
789
 
793
- /**
794
- *
795
- * @param {BinaryBuffer} buffer
796
- * @param constructor
797
- */
798
- List.prototype.fromBinaryBuffer = function (buffer, constructor) {
799
- this.fromBinaryBufferViaFactory(buffer, function (buffer) {
790
+ /**
791
+ *
792
+ * @param {BinaryBuffer} buffer
793
+ * @param constructor
794
+ */
795
+ fromBinaryBuffer(buffer, constructor) {
796
+ this.fromBinaryBufferViaFactory(buffer, function (buffer) {
800
797
 
801
- const el = new constructor();
798
+ const el = new constructor();
802
799
 
803
- if (typeof el.fromBinaryBuffer !== "function") {
804
- throw new Error('item.fromBinaryBuffer is not a function');
805
- }
800
+ if (typeof el.fromBinaryBuffer !== "function") {
801
+ throw new Error('item.fromBinaryBuffer is not a function');
802
+ }
806
803
 
807
- el.fromBinaryBuffer(buffer);
804
+ el.fromBinaryBuffer(buffer);
808
805
 
809
- return el;
810
- });
811
- };
806
+ return el;
807
+ });
808
+ }
812
809
 
813
- /**
814
- *
815
- * @param {BinaryBuffer} buffer
816
- * @param {function(buffer:BinaryBuffer)} factory
817
- */
818
- List.prototype.fromBinaryBufferViaFactory = function (buffer, factory) {
819
- this.reset();
810
+ /**
811
+ *
812
+ * @param {BinaryBuffer} buffer
813
+ * @param {function(buffer:BinaryBuffer)} factory
814
+ */
815
+ fromBinaryBufferViaFactory(buffer, factory) {
816
+ this.reset();
820
817
 
821
- this.addFromBinaryBufferViaFactory(buffer, factory);
822
- };
823
- /**
824
- *
825
- * @param {BinaryBuffer} buffer
826
- * @param {function(buffer:BinaryBuffer)} factory
827
- */
828
- List.prototype.addFromBinaryBufferViaFactory = function (buffer, factory) {
829
- const length = buffer.readUint32();
818
+ this.addFromBinaryBufferViaFactory(buffer, factory);
819
+ }
820
+
821
+ /**
822
+ *
823
+ * @param {BinaryBuffer} buffer
824
+ * @param {function(buffer:BinaryBuffer)} factory
825
+ */
826
+ addFromBinaryBufferViaFactory(buffer, factory) {
827
+ const length = buffer.readUint32();
830
828
 
831
- for (let i = 0; i < length; i++) {
832
- const el = factory(buffer);
829
+ for (let i = 0; i < length; i++) {
830
+ const el = factory(buffer);
833
831
 
834
- this.add(el);
832
+ this.add(el);
833
+ }
835
834
  }
836
- };
837
835
 
838
- /**
839
- * @template J
840
- * @param {J[]} data
841
- * @param {function(J):T} factory
842
- */
843
- List.prototype.addFromJSONViaFactory = function (data, factory) {
844
- const n = data.length;
845
- for (let i = 0; i < n; i++) {
846
- const datum = data[i];
836
+ /**
837
+ * @template J
838
+ * @param {J[]} data
839
+ * @param {function(J):T} factory
840
+ */
841
+ addFromJSONViaFactory(data, factory) {
842
+ const n = data.length;
843
+ for (let i = 0; i < n; i++) {
844
+ const datum = data[i];
847
845
 
848
- const el = factory(datum);
846
+ const el = factory(datum);
849
847
 
850
- this.add(el);
848
+ this.add(el);
849
+ }
851
850
  }
852
- };
853
851
 
854
- /**
855
- * NOTE: Elements must have hash method
856
- * @returns {number}
857
- */
858
- List.prototype.hash = function () {
859
- let hash = 0;
860
- const length = this.length;
852
+ /**
853
+ * NOTE: Elements must have hash method
854
+ * @returns {number}
855
+ */
856
+ hash() {
857
+ let hash = 0;
858
+ const length = this.length;
859
+
860
+ for (let i = 0; i < length; i++) {
861
+ const datum = this.data[i];
862
+ const singleValue = datum.hash();
863
+ hash = ((hash << 5) - hash) + singleValue;
864
+ hash |= 0; // Convert to 32bit integer
865
+ }
861
866
 
862
- for (let i = 0; i < length; i++) {
863
- const datum = this.data[i];
864
- const singleValue = datum.hash();
865
- hash = ((hash << 5) - hash) + singleValue;
866
- hash |= 0; // Convert to 32bit integer
867
+ return hash;
867
868
  }
868
869
 
869
- return hash;
870
- };
870
+ /**
871
+ *
872
+ * @return {T|undefined}
873
+ */
874
+ last() {
875
+ return this.data[this.length - 1];
876
+ }
871
877
 
872
- /**
873
- *
874
- * @return {T|undefined}
875
- */
876
- List.prototype.last = function () {
877
- return this.data[this.length - 1];
878
- };
878
+ /**
879
+ * Perform element-wise equality comparison with another list
880
+ * @param {List} other
881
+ */
882
+ equals(other) {
883
+ assert.defined(other, 'other');
879
884
 
880
- /**
881
- * Perform element-wise equality comparison with another list
882
- * @param {List} other
883
- */
884
- List.prototype.equals = function (other) {
885
- assert.defined(other, 'other');
885
+ const length = this.length;
886
886
 
887
- const length = this.length;
887
+ if (length !== other.length) {
888
+ return false;
889
+ }
888
890
 
889
- if (length !== other.length) {
890
- return false;
891
- }
891
+ let i;
892
892
 
893
- let i;
893
+ for (i = 0; i < length; i++) {
894
+ const a = this.get(i);
894
895
 
895
- for (i = 0; i < length; i++) {
896
- const a = this.get(i);
896
+ const b = other.get(i);
897
897
 
898
- const b = other.get(i);
898
+ if (a === b) {
899
+ continue;
900
+ }
899
901
 
900
- if (a === b) {
901
- continue;
902
- }
902
+ if (typeof a === "object" && typeof b === "object" && typeof a.equals === "function" && a.equals(b)) {
903
+ //test via "equals" method
904
+ continue;
905
+ }
903
906
 
904
- if (typeof a === "object" && typeof b === "object" && typeof a.equals === "function" && a.equals(b)) {
905
- //test via "equals" method
906
- continue;
907
+ //elements not equal
908
+ return false;
907
909
  }
908
910
 
909
- //elements not equal
910
- return false;
911
+ return true;
911
912
  }
912
913
 
913
- return true;
914
- };
914
+ /**
915
+ *
916
+ * @param {List<T>} other
917
+ * @returns {number}
918
+ */
919
+ compare(other) {
920
+ const l = this.length;
915
921
 
916
- /**
917
- *
918
- * @param {List<T>} other
919
- * @returns {number}
920
- */
921
- List.prototype.compare = function (other) {
922
- const l = this.length;
922
+ const other_length = other.length;
923
+ if (l !== other_length) {
924
+ return l - other_length;
925
+ }
923
926
 
924
- const other_length = other.length;
925
- if (l !== other_length) {
926
- return l - other_length;
927
- }
927
+ for (let i = 0; i < l; i++) {
928
+ const a = this.get(i);
928
929
 
929
- for (let i = 0; i < l; i++) {
930
- const a = this.get(i);
930
+ const b = other.get(i);
931
931
 
932
- const b = other.get(i);
932
+ const delta = a.compare(b);
933
933
 
934
- const delta = a.compare(b);
934
+ if (delta !== 0) {
935
+ return delta;
936
+ }
935
937
 
936
- if (delta !== 0) {
937
- return delta;
938
938
  }
939
939
 
940
+ // same
941
+ return 0;
940
942
  }
943
+ }
944
+
945
+ /**
946
+ *
947
+ * @param {Object} a
948
+ * @param {Object} b
949
+ * @returns {boolean}
950
+ */
951
+ function objectsEqual(a, b) {
952
+ return a === b || (typeof a.equals === "function" && a.equals(b));
953
+ }
954
+
955
+ function conditionEqualsViaMethod(v) {
956
+ return this === v || this.equals(v);
957
+ }
958
+
959
+ function conditionEqualsStrict(v) {
960
+ return this === v;
961
+ }
941
962
 
942
- // same
943
- return 0;
944
- };
945
963
 
946
964
  export default List;