hadron-document 7.7.0 → 7.10.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.
Files changed (82) hide show
  1. package/README.md +5 -5
  2. package/dist/document.d.ts +53 -0
  3. package/dist/document.d.ts.map +1 -0
  4. package/dist/document.js +248 -0
  5. package/dist/document.js.map +1 -0
  6. package/dist/editor/date.d.ts +13 -0
  7. package/dist/editor/date.d.ts.map +1 -0
  8. package/dist/editor/date.js +65 -0
  9. package/dist/editor/date.js.map +1 -0
  10. package/dist/editor/decimal128.d.ts +9 -0
  11. package/dist/editor/decimal128.d.ts.map +1 -0
  12. package/dist/editor/decimal128.js +32 -0
  13. package/dist/editor/decimal128.js.map +1 -0
  14. package/dist/editor/double.d.ts +10 -0
  15. package/dist/editor/double.d.ts.map +1 -0
  16. package/dist/editor/double.js +44 -0
  17. package/dist/editor/double.js.map +1 -0
  18. package/dist/editor/index.d.ts +38 -0
  19. package/dist/editor/index.d.ts.map +1 -0
  20. package/dist/editor/index.js +51 -0
  21. package/dist/editor/index.js.map +1 -0
  22. package/dist/editor/int32.d.ts +7 -0
  23. package/dist/editor/int32.d.ts.map +1 -0
  24. package/dist/editor/int32.js +17 -0
  25. package/dist/editor/int32.js.map +1 -0
  26. package/dist/editor/int64.d.ts +9 -0
  27. package/dist/editor/int64.d.ts.map +1 -0
  28. package/dist/editor/int64.js +32 -0
  29. package/dist/editor/int64.js.map +1 -0
  30. package/dist/editor/null.d.ts +8 -0
  31. package/dist/editor/null.d.ts.map +1 -0
  32. package/dist/editor/null.js +20 -0
  33. package/dist/editor/null.js.map +1 -0
  34. package/dist/editor/objectid.d.ts +10 -0
  35. package/dist/editor/objectid.d.ts.map +1 -0
  36. package/dist/editor/objectid.js +38 -0
  37. package/dist/editor/objectid.js.map +1 -0
  38. package/dist/editor/standard.d.ts +16 -0
  39. package/dist/editor/standard.d.ts.map +1 -0
  40. package/dist/editor/standard.js +49 -0
  41. package/dist/editor/standard.js.map +1 -0
  42. package/dist/editor/string.d.ts +7 -0
  43. package/dist/editor/string.d.ts.map +1 -0
  44. package/dist/editor/string.js +15 -0
  45. package/dist/editor/string.js.map +1 -0
  46. package/dist/editor/undefined.d.ts +8 -0
  47. package/dist/editor/undefined.d.ts.map +1 -0
  48. package/dist/editor/undefined.js +20 -0
  49. package/dist/editor/undefined.js.map +1 -0
  50. package/dist/element-events.d.ts +11 -0
  51. package/dist/element-events.d.ts.map +1 -0
  52. package/dist/element-events.js +12 -0
  53. package/dist/element-events.js.map +1 -0
  54. package/dist/element.d.ts +112 -0
  55. package/dist/element.d.ts.map +1 -0
  56. package/dist/element.js +652 -0
  57. package/dist/element.js.map +1 -0
  58. package/dist/index.d.ts +6 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +35 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/object-generator.d.ts +9 -0
  63. package/dist/object-generator.d.ts.map +1 -0
  64. package/dist/object-generator.js +82 -0
  65. package/dist/object-generator.js.map +1 -0
  66. package/dist/utils.d.ts +11 -0
  67. package/dist/utils.d.ts.map +1 -0
  68. package/dist/utils.js +37 -0
  69. package/dist/utils.js.map +1 -0
  70. package/package.json +45 -28
  71. package/.babelrc.js +0 -14
  72. package/.depcheckrc +0 -8
  73. package/.eslintignore +0 -1
  74. package/index.js +0 -1
  75. package/lib/document.js +0 -557
  76. package/lib/element.js +0 -1236
  77. package/lib/index.js +0 -4
  78. package/lib/object-generator.js +0 -180
  79. package/src/document.js +0 -391
  80. package/src/element.js +0 -951
  81. package/src/index.js +0 -2
  82. package/src/object-generator.js +0 -96
package/src/element.js DELETED
@@ -1,951 +0,0 @@
1
- 'use strict';
2
-
3
- const EventEmitter = require('eventemitter3');
4
- const keys = require('lodash.keys');
5
- const isObject = require('lodash.isplainobject');
6
- const isArray = require('lodash.isarray');
7
- const isEqual = require('lodash.isequal');
8
- const isString = require('lodash.isstring');
9
- const ObjectGenerator = require('./object-generator');
10
- const TypeChecker = require('hadron-type-checker');
11
- const uuid = require('uuid');
12
-
13
- const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS';
14
-
15
- /**
16
- * The event constant.
17
- */
18
- const Events = {
19
- 'Added': 'Element::Added',
20
- 'Edited': 'Element::Edited',
21
- 'Removed': 'Element::Removed',
22
- 'Reverted': 'Element::Reverted',
23
- 'Converted': 'Element::Converted',
24
- 'Invalid': 'Element::Invalid',
25
- 'Valid': 'Element::Valid'
26
- };
27
-
28
- /**
29
- * Id field constant.
30
- */
31
- const ID = '_id';
32
-
33
- /**
34
- * Types that are not editable.
35
- */
36
- const UNEDITABLE_TYPES = [
37
- 'Binary',
38
- 'Code',
39
- 'MinKey',
40
- 'MaxKey',
41
- 'Timestamp',
42
- 'BSONRegExp',
43
- 'Undefined',
44
- 'Null'
45
- ];
46
-
47
- /**
48
- * Curly brace constant.
49
- */
50
- const CURLY = '{';
51
-
52
- /**
53
- * Bracket constant.
54
- */
55
- const BRACKET = '[';
56
-
57
- /**
58
- * Regex to match an array or object string.
59
- */
60
- const ARRAY_OR_OBJECT = /^(\[|\{)(.+)(\]|\})$/;
61
-
62
- /**
63
- * Represents an element in a document.
64
- */
65
- class Element extends EventEmitter {
66
- /**
67
- * Bulk edit the element. Can accept JSON strings.
68
- *
69
- * @param {String} value - The JSON string value.
70
- */
71
- bulkEdit(value) {
72
- if (value.match(ARRAY_OR_OBJECT)) {
73
- this.edit(JSON.parse(value));
74
- this._bubbleUp(Events.Converted);
75
- } else {
76
- this.edit(value);
77
- }
78
- }
79
-
80
- /**
81
- * Cancel any modifications to the element.
82
- */
83
- cancel() {
84
- if (this.elements) {
85
- for (let element of this.elements) {
86
- element.cancel();
87
- }
88
- }
89
- if (this.isModified()) {
90
- this.revert();
91
- }
92
- }
93
-
94
- /**
95
- * Create the element.
96
- *
97
- * @param {String} key - The key.
98
- * @param {Object} value - The value.
99
- * @param {Boolean} added - Is the element a new 'addition'?
100
- * @param {Element} parent - The parent element.
101
- * @param {Element} previousElement - The previous element in the list.
102
- * @param {Element} nextElement - The next element in the list.
103
- */
104
- constructor(key, value, added, parent, previousElement, nextElement) {
105
- super();
106
- this.uuid = uuid.v4();
107
- this.key = key;
108
- this.currentKey = key;
109
- this.parent = parent;
110
- this.previousElement = previousElement;
111
- this.nextElement = nextElement;
112
- this.added = added;
113
- this.removed = false;
114
- this.type = TypeChecker.type(value);
115
- this.currentType = this.type;
116
- this.setValid();
117
-
118
- if (this._isExpandable(value)) {
119
- this.elements = this._generateElements(value);
120
- this.originalExpandableValue = value;
121
- } else {
122
- this.value = value;
123
- this.currentValue = value;
124
- }
125
- }
126
-
127
- /**
128
- * Edit the element.
129
- *
130
- * @param {Object} value - The new value.
131
- */
132
- edit(value) {
133
- this.currentType = TypeChecker.type(value);
134
- if (this._isExpandable(value) && !this._isExpandable(this.currentValue)) {
135
- this.currentValue = null;
136
- this.elements = this._generateElements(value);
137
- } else if (!this._isExpandable(value) && this.elements) {
138
- this.currentValue = value;
139
- this.elements = undefined;
140
- } else {
141
- this.currentValue = value;
142
- }
143
- this.setValid();
144
- this._bubbleUp(Events.Edited);
145
- }
146
-
147
- /**
148
- * Get an element by its key.
149
- *
150
- * @param {String} key - The key name.
151
- *
152
- * @returns {Element} The element.
153
- */
154
- get(key) {
155
- return this.elements ? this.elements.get(key) : undefined;
156
- }
157
-
158
- /**
159
- * Get an element by its index.
160
- *
161
- * @param {Number} i - The index.
162
- *
163
- * @returns {Element} The element.
164
- */
165
- at(i) {
166
- return this.elements ? this.elements.at(i) : undefined;
167
- }
168
-
169
- /**
170
- * Go to the next edit.
171
- *
172
- * Will check if the value is either { or [ and take appropriate action.
173
- *
174
- * @returns {Element} The next element.
175
- */
176
- next() {
177
- if (this.currentValue === CURLY) {
178
- return this._convertToEmptyObject();
179
- } else if (this.currentValue === BRACKET) {
180
- return this._convertToEmptyArray();
181
- }
182
- return this._next();
183
- }
184
-
185
- /**
186
- * Rename the element. Update the parent's mapping if available.
187
- *
188
- * @param {String} key - The new key.
189
- */
190
- rename(key) {
191
- if (this.parent !== undefined) {
192
- const elm = this.parent.elements._map[this.currentKey];
193
- delete this.parent.elements._map[this.currentKey];
194
- this.parent.elements._map[key] = elm;
195
- }
196
-
197
- this.currentKey = key;
198
- this._bubbleUp(Events.Edited);
199
- }
200
-
201
- /**
202
- * Generate the javascript object for this element.
203
- *
204
- * @returns {Object} The javascript object.
205
- */
206
- generateObject() {
207
- if (this.currentType === 'Array') {
208
- return ObjectGenerator.generateArray(this.elements);
209
- }
210
- if (this.elements) {
211
- return ObjectGenerator.generate(this.elements);
212
- }
213
- return this.currentValue;
214
- }
215
-
216
- /**
217
- * Generate the javascript object representing the original values
218
- * for this element (pre-element removal, renaming, editing).
219
- *
220
- * @returns {Object} The javascript object.
221
- */
222
- generateOriginalObject() {
223
- if (this.type === 'Array') {
224
- const originalElements = this._generateElements(this.originalExpandableValue);
225
- return ObjectGenerator.generateOriginalArray(originalElements);
226
- }
227
- if (this.type === 'Object') {
228
- const originalElements = this._generateElements(this.originalExpandableValue);
229
- return ObjectGenerator.generateOriginal(originalElements);
230
- }
231
-
232
- return this.value;
233
- }
234
-
235
- /**
236
- * Insert an element after the provided element. If this element is an array,
237
- * then ignore the key specified by the caller and use the correct index.
238
- * Update the keys of the rest of the elements in the LinkedList.
239
- *
240
- * @param {Element} element - The element to insert after.
241
- * @param {String} key - The key.
242
- * @param {Object} value - The value.
243
- *
244
- * @returns {Element} The new element.
245
- */
246
- insertAfter(element, key, value) {
247
- if (this.currentType === 'Array') {
248
- if (element.currentKey === '') {
249
- this.elements.handleEmptyKeys(element);
250
- }
251
- key = element.currentKey + 1;
252
- }
253
- var newElement = this.elements.insertAfter(element, key, value, true, this);
254
- if (this.currentType === 'Array') {
255
- this.elements.updateKeys(newElement, 1);
256
- }
257
- this._bubbleUp(Events.Added);
258
- return newElement;
259
- }
260
-
261
- /**
262
- * Add a new element to this element.
263
- *
264
- * @param {String | Number} key - The element key.
265
- * @param {Object} value - The value.
266
- *
267
- * @returns {Element} The new element.
268
- */
269
- insertEnd(key, value) {
270
- if (this.currentType === 'Array') {
271
- this.elements.flush();
272
- key = 0;
273
- if (this.elements.lastElement) {
274
- if (this.elements.lastElement.currentKey === '') {
275
- this.elements.handleEmptyKeys(this.elements.lastElement);
276
- }
277
- key = this.elements.lastElement.currentKey + 1;
278
- }
279
- }
280
- var newElement = this.elements.insertEnd(key, value, true, this);
281
- this._bubbleUp(Events.Added);
282
- return newElement;
283
- }
284
-
285
- /**
286
- * Insert a placeholder element at the end of the element.
287
- *
288
- * @returns {Element} The placeholder element.
289
- */
290
- insertPlaceholder() {
291
- var newElement = this.elements.insertEnd('', '', true, this);
292
- this._bubbleUp(Events.Added);
293
- return newElement;
294
- }
295
-
296
- /**
297
- * Is the element a newly added element?
298
- *
299
- * @returns {Boolean} If the element is newly added.
300
- */
301
- isAdded() {
302
- return this.added || (this.parent && this.parent.isAdded());
303
- }
304
-
305
- /**
306
- * Is the element blank?
307
- *
308
- * @returns {Boolean} If the element is blank.
309
- */
310
- isBlank() {
311
- return this.currentKey === '' && this.currentValue === '';
312
- }
313
-
314
- /**
315
- * Does the element have a valid value for the current type?
316
- *
317
- * @returns {Boolean} If the value is valid.
318
- */
319
- isCurrentTypeValid() {
320
- return this.currentTypeValid;
321
- }
322
-
323
- /**
324
- * Set the element as valid.
325
- */
326
- setValid() {
327
- this.currentTypeValid = true;
328
- this.invalidTypeMessage = undefined;
329
- this._bubbleUp(Events.Valid, this.uuid);
330
- }
331
-
332
- /**
333
- * Set the element as invalid.
334
- *
335
- * @param {Object} value - The value.
336
- * @param {String} newType - The new type.
337
- * @param {String} message - The error message.
338
- */
339
- setInvalid(value, newType, message) {
340
- this.currentValue = value;
341
- this.currentType = newType;
342
- this.currentTypeValid = false;
343
- this.invalidTypeMessage = message;
344
- this._bubbleUp(Events.Invalid, this.uuid);
345
- }
346
-
347
- /**
348
- * Determine if the key is a duplicate.
349
- *
350
- * @param {String} value - The value to check.
351
- *
352
- * @returns {Boolean} If the key is a duplicate.
353
- */
354
- isDuplicateKey(value) {
355
- if (value === this.currentKey) {
356
- return false;
357
- }
358
- for (let element of this.parent.elements) {
359
- if (element.currentKey === value) {
360
- return true;
361
- }
362
- }
363
- return false;
364
- }
365
-
366
- /**
367
- * Determine if the element is edited - returns true if
368
- * the key or value changed. Does not count array values whose keys have
369
- * changed as edited.
370
- *
371
- * @returns {Boolean} If the element is edited.
372
- */
373
- isEdited() {
374
- return (this.isRenamed() ||
375
- !this._valuesEqual() ||
376
- this.type !== this.currentType) &&
377
- !this.isAdded();
378
- }
379
-
380
- /**
381
- * Check for value equality.
382
-
383
- * @returns {Boolean} If the value is equal.
384
- */
385
- _valuesEqual() {
386
- if (this.currentType === 'Date' && isString(this.currentValue)) {
387
- return isEqual(this.value, new Date(this.currentValue));
388
- } else if (this.currentType === 'ObjectId' && isString(this.currentValue)) {
389
- return this._isObjectIdEqual();
390
- }
391
- return isEqual(this.value, this.currentValue);
392
- }
393
-
394
- _isObjectIdEqual() {
395
- try {
396
- return this.value.toHexString() === this.currentValue;
397
- } catch (_) {
398
- return false;
399
- }
400
- }
401
-
402
- /**
403
- * Is the element the last in the elements.
404
- *
405
- * @returns {Boolean} If the element is last.
406
- */
407
- isLast() {
408
- return this.parent.elements.lastElement === this;
409
- }
410
-
411
- /**
412
- * Determine if the element is renamed.
413
- *
414
- * @returns {Boolean} If the element was renamed.
415
- */
416
- isRenamed() {
417
- let keyChanged = false;
418
- if (!this.parent || this.parent.isRoot() || this.parent.currentType === 'Object') {
419
- keyChanged = (this.key !== this.currentKey);
420
- }
421
-
422
- return keyChanged;
423
- }
424
-
425
- /**
426
- * Can changes to the elemnt be reverted?
427
- *
428
- * @returns {Boolean} If the element can be reverted.
429
- */
430
- isRevertable() {
431
- return this.isEdited() || this.isRemoved();
432
- }
433
-
434
- /**
435
- * Can the element be removed?
436
- *
437
- * @returns {Boolean} If the element can be removed.
438
- */
439
- isRemovable() {
440
- return !this.parent.isRemoved();
441
- }
442
-
443
- /**
444
- * Can no action be taken on the element?
445
- *
446
- * @returns {Boolean} If no action can be taken.
447
- */
448
- isNotActionable() {
449
- return (this.key === ID && !this.isAdded()) || !this.isRemovable();
450
- }
451
-
452
- /**
453
- * Determine if the value is editable.
454
- *
455
- * @returns {Boolean} If the value is editable.
456
- */
457
- isValueEditable() {
458
- return this.isKeyEditable() && !UNEDITABLE_TYPES.includes(this.currentType);
459
- }
460
-
461
- /**
462
- * Determine if the key of the parent element is editable.
463
- *
464
- * @returns {Boolean} If the parent's key is editable.
465
- */
466
- isParentEditable() {
467
- if (this.parent && !this.parent.isRoot()) {
468
- return this.parent.isKeyEditable();
469
- }
470
- return true;
471
- }
472
-
473
- /**
474
- * Determine if the key is editable.
475
- *
476
- * @returns {Boolean} If the key is editable.
477
- */
478
- isKeyEditable() {
479
- return this.isParentEditable() && (this.isAdded() || (this.currentKey !== ID));
480
- }
481
-
482
- /**
483
- * Determine if the element is modified at all.
484
- *
485
- * @returns {Boolean} If the element is modified.
486
- */
487
- isModified() {
488
- if (this.elements) {
489
- for (let element of this.elements) {
490
- if (element.isModified()) {
491
- return true;
492
- }
493
- }
494
- }
495
- return this.isAdded() || this.isEdited() || this.isRemoved();
496
- }
497
-
498
- /**
499
- * Is the element flagged for removal?
500
- *
501
- * @returns {Boolean} If the element is flagged for removal.
502
- */
503
- isRemoved() {
504
- return this.removed;
505
- }
506
-
507
- /**
508
- * Elements themselves are not the root.
509
- *
510
- * @returns {false} Always false.
511
- */
512
- isRoot() {
513
- return false;
514
- }
515
-
516
- /**
517
- * Flag the element for removal.
518
- */
519
- remove() {
520
- this.revert();
521
- this.removed = true;
522
- this._bubbleUp(Events.Removed);
523
- }
524
-
525
- /**
526
- * Revert the changes to the element.
527
- */
528
- revert() {
529
- if (this.isAdded()) {
530
- if (this.parent && this.parent.currentType === 'Array') {
531
- this.parent.elements.updateKeys(this, -1);
532
- }
533
- this.parent.elements.remove(this);
534
- this.parent.emit(Events.Removed);
535
- this.parent = null;
536
- } else {
537
- if (this.originalExpandableValue) {
538
- this.elements = this._generateElements(this.originalExpandableValue);
539
- this.currentValue = undefined;
540
- } else {
541
- if (this.currentValue === null && this.value !== null) {
542
- this.elements = null;
543
- } else {
544
- this._removeAddedElements();
545
- }
546
- this.currentValue = this.value;
547
- }
548
- this.currentKey = this.key;
549
- this.currentType = this.type;
550
- this.removed = false;
551
- }
552
- this.setValid();
553
- this._bubbleUp(Events.Reverted);
554
- }
555
-
556
- /**
557
- * Fire and bubble up the event.
558
- *
559
- * @param {Event} evt - The event.
560
- * @param {*} data - Optional.
561
- */
562
- _bubbleUp(evt, data) {
563
- this.emit(evt, data);
564
- var element = this.parent;
565
- if (element) {
566
- if (element.isRoot()) {
567
- element.emit(evt, data);
568
- } else {
569
- element._bubbleUp(evt, data);
570
- }
571
- }
572
- }
573
-
574
- /**
575
- * Convert this element to an empty object.
576
- */
577
- _convertToEmptyObject() {
578
- this.edit({});
579
- this.insertPlaceholder();
580
- }
581
-
582
- /**
583
- * Convert to an empty array.
584
- */
585
- _convertToEmptyArray() {
586
- this.edit([]);
587
- this.insertPlaceholder();
588
- }
589
-
590
- /**
591
- * Is the element empty?
592
- *
593
- * @param {Element} element - The element to check.
594
- *
595
- * @returns {Boolean} If the element is empty.
596
- */
597
- _isElementEmpty(element) {
598
- return element && element.isAdded() && element.isBlank();
599
- }
600
-
601
- /**
602
- * Check if the value is expandable.
603
- *
604
- * @param {Object} value - The value to check.
605
- *
606
- * @returns {Boolean} If the value is expandable.
607
- */
608
- _isExpandable(value) {
609
- return isObject(value) || isArray(value);
610
- }
611
-
612
- /**
613
- * Generates a sequence of child elements.
614
- *
615
- * @param {Object} object - The object to generate from.
616
- *
617
- * @returns {Array} The elements.
618
- */
619
- _generateElements(object) {
620
- return new LinkedList(this, object); // eslint-disable-line no-use-before-define
621
- }
622
-
623
- /**
624
- * Get the key for the element.
625
- *
626
- * @param {String} key
627
- * @param {Number} index
628
- *
629
- * @returns {String|Number} The index if the type is an array, or the key.
630
- */
631
- _key(key, index) {
632
- return this.currentType === 'Array' ? index : key;
633
- }
634
-
635
- /**
636
- * Add a new element to the parent.
637
- */
638
- _next() {
639
- if (!this._isElementEmpty(this.nextElement) && !this._isElementEmpty(this)) {
640
- this.parent.insertAfter(this, '', '');
641
- }
642
- }
643
-
644
- /**
645
- * Removes the added elements from the element.
646
- */
647
- _removeAddedElements() {
648
- if (this.elements) {
649
- for (let element of this.elements) {
650
- if (element.isAdded()) {
651
- this.elements.remove(element);
652
- }
653
- }
654
- }
655
- }
656
- }
657
-
658
- /**
659
- * Represents a doubly linked list.
660
- */
661
- class LinkedList {
662
- /**
663
- * Get the element at the provided index.
664
- *
665
- * @param {Integer} index - The index.
666
- *
667
- * @returns {Element} The matching element.
668
- */
669
- at(index) {
670
- this.flush();
671
- if (!Number.isInteger(index)) {
672
- return undefined;
673
- }
674
-
675
- var element = this.firstElement;
676
- for (var i = 0; i < index; i++) {
677
- if (!element) {
678
- return undefined;
679
- }
680
- element = element.nextElement;
681
- }
682
- return element === null ? undefined : element;
683
- }
684
-
685
- get(key) {
686
- this.flush();
687
- return this._map[key];
688
- }
689
-
690
- // Instantiate the new doubly linked list.
691
- constructor(doc, originalDoc) {
692
- this.firstElement = null;
693
- this.lastElement = null;
694
- this.doc = doc;
695
- this.originalDoc = originalDoc;
696
- this.keys = keys(this.originalDoc);
697
- if (this.doc.currentType === 'Array') {
698
- this.keys = this.keys.map(k => parseInt(k, 10));
699
- }
700
- this.size = this.keys.length;
701
- this.loaded = 0;
702
- this._map = {};
703
- }
704
-
705
- /**
706
- * Insert data after the provided element.
707
- *
708
- * @param {Element} element - The element to insert after.
709
- * @param {String} key - The element key.
710
- * @param {Object} value - The element value.
711
- * @param {Boolean} added - If the element is new.
712
- * @param {Object} parent - The parent.
713
- *
714
- * @returns {Element} The inserted element.
715
- */
716
- insertAfter(element, key, value, added, parent) {
717
- this.flush();
718
- return this._insertAfter(element, key, value, added, parent);
719
- }
720
-
721
- /**
722
- * Update the currentKey of each element if array elements.
723
- *
724
- * @param {Element} element - The element to insert after.
725
- * @param {Number} add - 1 if adding a new element, -1 if removing.
726
- */
727
- updateKeys(element, add) {
728
- this.flush();
729
- while (element.nextElement) {
730
- element.nextElement.currentKey += add;
731
- element = element.nextElement;
732
- }
733
- }
734
-
735
- /**
736
- * If an element is added after a placeholder, convert that placeholder
737
- * into an empty element with the correct key.
738
- *
739
- * @param {Element} element - The placeholder element.
740
- */
741
- handleEmptyKeys(element) {
742
- if (element.currentKey === '') {
743
- let e = element;
744
- while (e.currentKey === '') {
745
- if (!e.previousElement) {
746
- e.currentKey = 0;
747
- break;
748
- } else {
749
- e = e.previousElement;
750
- }
751
- }
752
- while (e.nextElement) {
753
- e.nextElement.currentKey = e.currentKey + 1;
754
- e = e.nextElement;
755
- }
756
- }
757
- }
758
-
759
- /**
760
- * Insert data before the provided element.
761
- *
762
- * @param {Element} element - The element to insert before.
763
- * @param {String} key - The element key.
764
- * @param {Object} value - The element value.
765
- * @param {Boolean} added - If the element is new.
766
- * @param {Object} parent - The parent.
767
- *
768
- * @returns {Element} The inserted element.
769
- */
770
- insertBefore(element, key, value, added, parent) {
771
- this.flush();
772
- return this._insertBefore(element, key, value, added, parent);
773
- }
774
-
775
- /**
776
- * Insert data at the beginning of the list.
777
- *
778
- * @param {String} key - The element key.
779
- * @param {Object} value - The element value.
780
- * @param {Boolean} added - If the element is new.
781
- * @param {Object} parent - The parent.
782
- *
783
- * @returns {Element} The data element.
784
- */
785
- insertBeginning(key, value, added, parent) {
786
- this.flush();
787
- return this._insertBeginning(key, value, added, parent);
788
- }
789
-
790
- /**
791
- * Insert data at the end of the list.
792
- *
793
- * @param {String} key - The element key.
794
- * @param {Object} value - The element value.
795
- * @param {Boolean} added - If the element is new.
796
- * @param {Object} parent - The parent.
797
- *
798
- * @returns {Element} The data element.
799
- */
800
- insertEnd(key, value, added, parent) {
801
- this.flush();
802
- if (!this.lastElement) {
803
- return this.insertBeginning(key, value, added, parent);
804
- }
805
- return this.insertAfter(this.lastElement, key, value, added, parent);
806
- }
807
-
808
- flush() {
809
- if (this.loaded < this.size) {
810
- // Only iterate from the loaded index to the size.
811
- for (let element of this) {
812
- if (element && element.elements) {
813
- element.elements.flush();
814
- }
815
- }
816
- }
817
- }
818
-
819
- /**
820
- * Get an iterator for the list.
821
- *
822
- * @returns {Iterator} The iterator.
823
- */
824
- [Symbol.iterator]() {
825
- let currentElement;
826
- let index = 0;
827
- return {
828
- next: () => {
829
- if (this._needsLazyLoad(index)) {
830
- const key = this.keys[index];
831
- index += 1;
832
- currentElement = this._lazyInsertEnd(key);
833
- return { value: currentElement };
834
- } else if (this._needsStandardIteration(index)) {
835
- if (currentElement) {
836
- currentElement = currentElement.nextElement;
837
- } else {
838
- currentElement = this.firstElement;
839
- }
840
- if (currentElement) {
841
- index += 1;
842
- return { value: currentElement };
843
- }
844
- return { done: true };
845
- }
846
- return { done: true };
847
- }
848
- };
849
- }
850
-
851
- _needsLazyLoad(index) {
852
- return (index === 0 && this.loaded === 0 && this.size > 0) ||
853
- (this.loaded <= index && index < this.size);
854
- }
855
-
856
- _needsStandardIteration(index) {
857
- return (this.loaded > 0 && index < this.loaded && index < this.size);
858
- }
859
-
860
- /**
861
- * Insert on the end of the list lazily.
862
- *
863
- * @param {String} key - The key.
864
- *
865
- * @returns {Element} The inserted element.
866
- */
867
- _lazyInsertEnd(key) {
868
- this.size -= 1;
869
- return this._insertEnd(key, this.originalDoc[key], this.doc.cloned, this.doc);
870
- }
871
-
872
- _insertEnd(key, value, added, parent) {
873
- if (!this.lastElement) {
874
- return this._insertBeginning(key, value, added, parent);
875
- }
876
- return this._insertAfter(this.lastElement, key, value, added, parent);
877
- }
878
-
879
- _insertBefore(element, key, value, added, parent) {
880
- var newElement = new Element(key, value, added, parent, element.previousElement, element);
881
- if (element.previousElement) {
882
- element.previousElement.nextElement = newElement;
883
- } else {
884
- this.firstElement = newElement;
885
- }
886
- element.previousElement = newElement;
887
- this._map[newElement.key] = newElement;
888
- this.size += 1;
889
- this.loaded += 1;
890
- return newElement;
891
- }
892
-
893
- _insertBeginning(key, value, added, parent) {
894
- if (!this.firstElement) {
895
- var element = new Element(key, value, added, parent, null, null);
896
- this.firstElement = this.lastElement = element;
897
- this.size += 1;
898
- this.loaded += 1;
899
- this._map[element.key] = element;
900
- return element;
901
- }
902
- const newElement = this.insertBefore(this.firstElement, key, value, added, parent);
903
- this._map[newElement.key] = newElement;
904
- return newElement;
905
- }
906
-
907
- _insertAfter(element, key, value, added, parent) {
908
- var newElement = new Element(key, value, added, parent, element, element.nextElement);
909
- if (element.nextElement) {
910
- element.nextElement.previousElement = newElement;
911
- } else {
912
- this.lastElement = newElement;
913
- }
914
- element.nextElement = newElement;
915
- this._map[newElement.key] = newElement;
916
- this.size += 1;
917
- this.loaded += 1;
918
- return newElement;
919
- }
920
-
921
- /**
922
- * Remove the element from the list.
923
- *
924
- * @param {Element} element - The element to remove.
925
- *
926
- * @returns {DoublyLinkedList} The list with the element removed.
927
- */
928
- remove(element) {
929
- this.flush();
930
- if (element.previousElement) {
931
- element.previousElement.nextElement = element.nextElement;
932
- } else {
933
- this.firstElement = element.nextElement;
934
- }
935
- if (element.nextElement) {
936
- element.nextElement.previousElement = element.previousElement;
937
- } else {
938
- this.lastElement = element.previousElement;
939
- }
940
- element.nextElement = element.previousElement = null;
941
- delete this._map[element.currentKey];
942
- this.size -= 1;
943
- this.loaded -= 1;
944
- return this;
945
- }
946
- }
947
-
948
- module.exports = Element;
949
- module.exports.LinkedList = LinkedList;
950
- module.exports.Events = Events;
951
- module.exports.DATE_FORMAT = DATE_FORMAT;