UNPKG

34.4 kBJavaScriptView Raw
1'use strict';
2
3function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
5function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
6
7function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
8
9function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
10
11function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12
13function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
14
15function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
16
17function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
18
19function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
20
21function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
22
23function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
24
25function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
26
27function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
28
29function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
30
31var EventEmitter = require('eventemitter3');
32
33var keys = require('lodash.keys');
34
35var isObject = require('lodash.isplainobject');
36
37var isArray = require('lodash.isarray');
38
39var isEqual = require('lodash.isequal');
40
41var isString = require('lodash.isstring');
42
43var includes = require('lodash.includes');
44
45var ObjectGenerator = require('./object-generator');
46
47var TypeChecker = require('hadron-type-checker');
48
49var uuid = require('uuid');
50
51var DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS';
52/**
53 * The event constant.
54 */
55
56var Events = {
57 'Added': 'Element::Added',
58 'Edited': 'Element::Edited',
59 'Removed': 'Element::Removed',
60 'Reverted': 'Element::Reverted',
61 'Converted': 'Element::Converted',
62 'Invalid': 'Element::Invalid',
63 'Valid': 'Element::Valid'
64};
65/**
66 * Id field constant.
67 */
68
69var ID = '_id';
70/**
71 * Types that are not editable.
72 */
73
74var UNEDITABLE_TYPES = ['Binary', 'Code', 'MinKey', 'MaxKey', 'Timestamp', 'BSONRegExp', 'Undefined', 'Null'];
75/**
76 * Curly brace constant.
77 */
78
79var CURLY = '{';
80/**
81 * Bracket constant.
82 */
83
84var BRACKET = '[';
85/**
86 * Regex to match an array or object string.
87 */
88
89var ARRAY_OR_OBJECT = /^(\[|\{)(.+)(\]|\})$/;
90/**
91 * Represents an element in a document.
92 */
93
94var Element = /*#__PURE__*/function (_EventEmitter) {
95 _inherits(Element, _EventEmitter);
96
97 var _super = _createSuper(Element);
98
99 /**
100 * Create the element.
101 *
102 * @param {String} key - The key.
103 * @param {Object} value - The value.
104 * @param {Boolean} added - Is the element a new 'addition'?
105 * @param {Element} parent - The parent element.
106 * @param {Element} previousElement - The previous element in the list.
107 * @param {Element} nextElement - The next element in the list.
108 */
109 function Element(key, value, added, parent, previousElement, nextElement) {
110 var _this;
111
112 _classCallCheck(this, Element);
113
114 _this = _super.call(this);
115 _this.uuid = uuid.v4();
116 _this.key = key;
117 _this.currentKey = key;
118 _this.parent = parent;
119 _this.previousElement = previousElement;
120 _this.nextElement = nextElement;
121 _this.added = added;
122 _this.removed = false;
123 _this.type = TypeChecker.type(value);
124 _this.currentType = _this.type;
125
126 _this.setValid();
127
128 if (_this._isExpandable(value)) {
129 _this.elements = _this._generateElements(value);
130 _this.originalExpandableValue = value;
131 } else {
132 _this.value = value;
133 _this.currentValue = value;
134 }
135
136 return _this;
137 }
138 /**
139 * Edit the element.
140 *
141 * @param {Object} value - The new value.
142 */
143
144
145 _createClass(Element, [{
146 key: "bulkEdit",
147 value:
148 /**
149 * Bulk edit the element. Can accept JSON strings.
150 *
151 * @param {String} value - The JSON string value.
152 */
153 function bulkEdit(value) {
154 if (value.match(ARRAY_OR_OBJECT)) {
155 this.edit(JSON.parse(value));
156
157 this._bubbleUp(Events.Converted);
158 } else {
159 this.edit(value);
160 }
161 }
162 /**
163 * Cancel any modifications to the element.
164 */
165
166 }, {
167 key: "cancel",
168 value: function cancel() {
169 if (this.elements) {
170 var _iterator = _createForOfIteratorHelper(this.elements),
171 _step;
172
173 try {
174 for (_iterator.s(); !(_step = _iterator.n()).done;) {
175 var element = _step.value;
176 element.cancel();
177 }
178 } catch (err) {
179 _iterator.e(err);
180 } finally {
181 _iterator.f();
182 }
183 }
184
185 if (this.isModified()) {
186 this.revert();
187 }
188 }
189 }, {
190 key: "edit",
191 value: function edit(value) {
192 this.currentType = TypeChecker.type(value);
193
194 if (this._isExpandable(value) && !this._isExpandable(this.currentValue)) {
195 this.currentValue = null;
196 this.elements = this._generateElements(value);
197 } else if (!this._isExpandable(value) && this.elements) {
198 this.currentValue = value;
199 this.elements = undefined;
200 } else {
201 this.currentValue = value;
202 }
203
204 this.setValid();
205
206 this._bubbleUp(Events.Edited);
207 }
208 /**
209 * Get an element by its key.
210 *
211 * @param {String} key - The key name.
212 *
213 * @returns {Element} The element.
214 */
215
216 }, {
217 key: "get",
218 value: function get(key) {
219 return this.elements ? this.elements.get(key) : undefined;
220 }
221 /**
222 * Get an element by its index.
223 *
224 * @param {Number} i - The index.
225 *
226 * @returns {Element} The element.
227 */
228
229 }, {
230 key: "at",
231 value: function at(i) {
232 return this.elements ? this.elements.at(i) : undefined;
233 }
234 /**
235 * Go to the next edit.
236 *
237 * Will check if the value is either { or [ and take appropriate action.
238 *
239 * @returns {Element} The next element.
240 */
241
242 }, {
243 key: "next",
244 value: function next() {
245 if (this.currentValue === CURLY) {
246 return this._convertToEmptyObject();
247 } else if (this.currentValue === BRACKET) {
248 return this._convertToEmptyArray();
249 }
250
251 return this._next();
252 }
253 /**
254 * Rename the element. Update the parent's mapping if available.
255 *
256 * @param {String} key - The new key.
257 */
258
259 }, {
260 key: "rename",
261 value: function rename(key) {
262 if (this.parent !== undefined) {
263 var elm = this.parent.elements._map[this.currentKey];
264 delete this.parent.elements._map[this.currentKey];
265 this.parent.elements._map[key] = elm;
266 }
267
268 this.currentKey = key;
269
270 this._bubbleUp(Events.Edited);
271 }
272 /**
273 * Generate the javascript object for this element.
274 *
275 * @returns {Object} The javascript object.
276 */
277
278 }, {
279 key: "generateObject",
280 value: function generateObject() {
281 if (this.currentType === 'Array') {
282 return ObjectGenerator.generateArray(this.elements);
283 }
284
285 if (this.elements) {
286 return ObjectGenerator.generate(this.elements);
287 }
288
289 return this.currentValue;
290 }
291 /**
292 * Generate the javascript object representing the original values
293 * for this element (pre-element removal, renaming, editing).
294 *
295 * @returns {Object} The javascript object.
296 */
297
298 }, {
299 key: "generateOriginalObject",
300 value: function generateOriginalObject() {
301 if (this.type === 'Array') {
302 var originalElements = this._generateElements(this.originalExpandableValue);
303
304 return ObjectGenerator.generateOriginalArray(originalElements);
305 }
306
307 if (this.type === 'Object') {
308 var _originalElements = this._generateElements(this.originalExpandableValue);
309
310 return ObjectGenerator.generateOriginal(_originalElements);
311 }
312
313 return this.value;
314 }
315 /**
316 * Insert an element after the provided element. If this element is an array,
317 * then ignore the key specified by the caller and use the correct index.
318 * Update the keys of the rest of the elements in the LinkedList.
319 *
320 * @param {Element} element - The element to insert after.
321 * @param {String} key - The key.
322 * @param {Object} value - The value.
323 *
324 * @returns {Element} The new element.
325 */
326
327 }, {
328 key: "insertAfter",
329 value: function insertAfter(element, key, value) {
330 if (this.currentType === 'Array') {
331 if (element.currentKey === '') {
332 this.elements.handleEmptyKeys(element);
333 }
334
335 key = element.currentKey + 1;
336 }
337
338 var newElement = this.elements.insertAfter(element, key, value, true, this);
339
340 if (this.currentType === 'Array') {
341 this.elements.updateKeys(newElement, 1);
342 }
343
344 this._bubbleUp(Events.Added);
345
346 return newElement;
347 }
348 /**
349 * Add a new element to this element.
350 *
351 * @param {String | Number} key - The element key.
352 * @param {Object} value - The value.
353 *
354 * @returns {Element} The new element.
355 */
356
357 }, {
358 key: "insertEnd",
359 value: function insertEnd(key, value) {
360 if (this.currentType === 'Array') {
361 this.elements.flush();
362 key = 0;
363
364 if (this.elements.lastElement) {
365 if (this.elements.lastElement.currentKey === '') {
366 this.elements.handleEmptyKeys(this.elements.lastElement);
367 }
368
369 key = this.elements.lastElement.currentKey + 1;
370 }
371 }
372
373 var newElement = this.elements.insertEnd(key, value, true, this);
374
375 this._bubbleUp(Events.Added);
376
377 return newElement;
378 }
379 /**
380 * Insert a placeholder element at the end of the element.
381 *
382 * @returns {Element} The placeholder element.
383 */
384
385 }, {
386 key: "insertPlaceholder",
387 value: function insertPlaceholder() {
388 var newElement = this.elements.insertEnd('', '', true, this);
389
390 this._bubbleUp(Events.Added);
391
392 return newElement;
393 }
394 /**
395 * Is the element a newly added element?
396 *
397 * @returns {Boolean} If the element is newly added.
398 */
399
400 }, {
401 key: "isAdded",
402 value: function isAdded() {
403 return this.added || this.parent && this.parent.isAdded();
404 }
405 /**
406 * Is the element blank?
407 *
408 * @returns {Boolean} If the element is blank.
409 */
410
411 }, {
412 key: "isBlank",
413 value: function isBlank() {
414 return this.currentKey === '' && this.currentValue === '';
415 }
416 /**
417 * Does the element have a valid value for the current type?
418 *
419 * @returns {Boolean} If the value is valid.
420 */
421
422 }, {
423 key: "isCurrentTypeValid",
424 value: function isCurrentTypeValid() {
425 return this.currentTypeValid;
426 }
427 /**
428 * Set the element as valid.
429 */
430
431 }, {
432 key: "setValid",
433 value: function setValid() {
434 this.currentTypeValid = true;
435 this.invalidTypeMessage = undefined;
436
437 this._bubbleUp(Events.Valid, this.uuid);
438 }
439 /**
440 * Set the element as invalid.
441 *
442 * @param {Object} value - The value.
443 * @param {String} newType - The new type.
444 * @param {String} message - The error message.
445 */
446
447 }, {
448 key: "setInvalid",
449 value: function setInvalid(value, newType, message) {
450 this.currentValue = value;
451 this.currentType = newType;
452 this.currentTypeValid = false;
453 this.invalidTypeMessage = message;
454
455 this._bubbleUp(Events.Invalid, this.uuid);
456 }
457 /**
458 * Determine if the key is a duplicate.
459 *
460 * @param {String} value - The value to check.
461 *
462 * @returns {Boolean} If the key is a duplicate.
463 */
464
465 }, {
466 key: "isDuplicateKey",
467 value: function isDuplicateKey(value) {
468 if (value === this.currentKey) {
469 return false;
470 }
471
472 var _iterator2 = _createForOfIteratorHelper(this.parent.elements),
473 _step2;
474
475 try {
476 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
477 var element = _step2.value;
478
479 if (element.currentKey === value) {
480 return true;
481 }
482 }
483 } catch (err) {
484 _iterator2.e(err);
485 } finally {
486 _iterator2.f();
487 }
488
489 return false;
490 }
491 /**
492 * Determine if the element is edited - returns true if
493 * the key or value changed. Does not count array values whose keys have
494 * changed as edited.
495 *
496 * @returns {Boolean} If the element is edited.
497 */
498
499 }, {
500 key: "isEdited",
501 value: function isEdited() {
502 return (this.isRenamed() || !this._valuesEqual() || this.type !== this.currentType) && !this.isAdded();
503 }
504 /**
505 * Check for value equality.
506 * @returns {Boolean} If the value is equal.
507 */
508
509 }, {
510 key: "_valuesEqual",
511 value: function _valuesEqual() {
512 if (this.currentType === 'Date' && isString(this.currentValue)) {
513 return isEqual(this.value, new Date(this.currentValue));
514 } else if (this.currentType === 'ObjectId' && isString(this.currentValue)) {
515 return this._isObjectIdEqual();
516 }
517
518 return isEqual(this.value, this.currentValue);
519 }
520 }, {
521 key: "_isObjectIdEqual",
522 value: function _isObjectIdEqual() {
523 try {
524 return this.value.toHexString() === this.currentValue;
525 } catch (_) {
526 return false;
527 }
528 }
529 /**
530 * Is the element the last in the elements.
531 *
532 * @returns {Boolean} If the element is last.
533 */
534
535 }, {
536 key: "isLast",
537 value: function isLast() {
538 return this.parent.elements.lastElement === this;
539 }
540 /**
541 * Determine if the element is renamed.
542 *
543 * @returns {Boolean} If the element was renamed.
544 */
545
546 }, {
547 key: "isRenamed",
548 value: function isRenamed() {
549 var keyChanged = false;
550
551 if (!this.parent || this.parent.isRoot() || this.parent.currentType === 'Object') {
552 keyChanged = this.key !== this.currentKey;
553 }
554
555 return keyChanged;
556 }
557 /**
558 * Can changes to the elemnt be reverted?
559 *
560 * @returns {Boolean} If the element can be reverted.
561 */
562
563 }, {
564 key: "isRevertable",
565 value: function isRevertable() {
566 return this.isEdited() || this.isRemoved();
567 }
568 /**
569 * Can the element be removed?
570 *
571 * @returns {Boolean} If the element can be removed.
572 */
573
574 }, {
575 key: "isRemovable",
576 value: function isRemovable() {
577 return !this.parent.isRemoved();
578 }
579 /**
580 * Can no action be taken on the element?
581 *
582 * @returns {Boolean} If no action can be taken.
583 */
584
585 }, {
586 key: "isNotActionable",
587 value: function isNotActionable() {
588 return this.key === ID && !this.isAdded() || !this.isRemovable();
589 }
590 /**
591 * Determine if the value is editable.
592 *
593 * @returns {Boolean} If the value is editable.
594 */
595
596 }, {
597 key: "isValueEditable",
598 value: function isValueEditable() {
599 return this.isKeyEditable() && !includes(UNEDITABLE_TYPES, this.currentType);
600 }
601 /**
602 * Determine if the key of the parent element is editable.
603 *
604 * @returns {Boolean} If the parent's key is editable.
605 */
606
607 }, {
608 key: "isParentEditable",
609 value: function isParentEditable() {
610 if (this.parent && !this.parent.isRoot()) {
611 return this.parent.isKeyEditable();
612 }
613
614 return true;
615 }
616 /**
617 * Determine if the key is editable.
618 *
619 * @returns {Boolean} If the key is editable.
620 */
621
622 }, {
623 key: "isKeyEditable",
624 value: function isKeyEditable() {
625 return this.isParentEditable() && (this.isAdded() || this.currentKey !== ID);
626 }
627 /**
628 * Determine if the element is modified at all.
629 *
630 * @returns {Boolean} If the element is modified.
631 */
632
633 }, {
634 key: "isModified",
635 value: function isModified() {
636 if (this.elements) {
637 var _iterator3 = _createForOfIteratorHelper(this.elements),
638 _step3;
639
640 try {
641 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
642 var element = _step3.value;
643
644 if (element.isModified()) {
645 return true;
646 }
647 }
648 } catch (err) {
649 _iterator3.e(err);
650 } finally {
651 _iterator3.f();
652 }
653 }
654
655 return this.isAdded() || this.isEdited() || this.isRemoved();
656 }
657 /**
658 * Is the element flagged for removal?
659 *
660 * @returns {Boolean} If the element is flagged for removal.
661 */
662
663 }, {
664 key: "isRemoved",
665 value: function isRemoved() {
666 return this.removed;
667 }
668 /**
669 * Elements themselves are not the root.
670 *
671 * @returns {false} Always false.
672 */
673
674 }, {
675 key: "isRoot",
676 value: function isRoot() {
677 return false;
678 }
679 /**
680 * Flag the element for removal.
681 */
682
683 }, {
684 key: "remove",
685 value: function remove() {
686 this.revert();
687 this.removed = true;
688
689 this._bubbleUp(Events.Removed);
690 }
691 /**
692 * Revert the changes to the element.
693 */
694
695 }, {
696 key: "revert",
697 value: function revert() {
698 if (this.isAdded()) {
699 if (this.parent && this.parent.currentType === 'Array') {
700 this.parent.elements.updateKeys(this, -1);
701 }
702
703 this.parent.elements.remove(this);
704 this.parent.emit(Events.Removed);
705 this.parent = null;
706 } else {
707 if (this.originalExpandableValue) {
708 this.elements = this._generateElements(this.originalExpandableValue);
709 this.currentValue = undefined;
710 } else {
711 if (this.currentValue === null && this.value !== null) {
712 this.elements = null;
713 } else {
714 this._removeAddedElements();
715 }
716
717 this.currentValue = this.value;
718 }
719
720 this.currentKey = this.key;
721 this.currentType = this.type;
722 this.removed = false;
723 }
724
725 this.setValid();
726
727 this._bubbleUp(Events.Reverted);
728 }
729 /**
730 * Fire and bubble up the event.
731 *
732 * @param {Event} evt - The event.
733 * @param {*} data - Optional.
734 */
735
736 }, {
737 key: "_bubbleUp",
738 value: function _bubbleUp(evt, data) {
739 this.emit(evt, data);
740 var element = this.parent;
741
742 if (element) {
743 if (element.isRoot()) {
744 element.emit(evt, data);
745 } else {
746 element._bubbleUp(evt, data);
747 }
748 }
749 }
750 /**
751 * Convert this element to an empty object.
752 */
753
754 }, {
755 key: "_convertToEmptyObject",
756 value: function _convertToEmptyObject() {
757 this.edit({});
758 this.insertPlaceholder();
759 }
760 /**
761 * Convert to an empty array.
762 */
763
764 }, {
765 key: "_convertToEmptyArray",
766 value: function _convertToEmptyArray() {
767 this.edit([]);
768 this.insertPlaceholder();
769 }
770 /**
771 * Is the element empty?
772 *
773 * @param {Element} element - The element to check.
774 *
775 * @returns {Boolean} If the element is empty.
776 */
777
778 }, {
779 key: "_isElementEmpty",
780 value: function _isElementEmpty(element) {
781 return element && element.isAdded() && element.isBlank();
782 }
783 /**
784 * Check if the value is expandable.
785 *
786 * @param {Object} value - The value to check.
787 *
788 * @returns {Boolean} If the value is expandable.
789 */
790
791 }, {
792 key: "_isExpandable",
793 value: function _isExpandable(value) {
794 return isObject(value) || isArray(value);
795 }
796 /**
797 * Generates a sequence of child elements.
798 *
799 * @param {Object} object - The object to generate from.
800 *
801 * @returns {Array} The elements.
802 */
803
804 }, {
805 key: "_generateElements",
806 value: function _generateElements(object) {
807 return new LinkedList(this, object); // eslint-disable-line no-use-before-define
808 }
809 /**
810 * Get the key for the element.
811 *
812 * @param {String} key
813 * @param {Number} index
814 *
815 * @returns {String|Number} The index if the type is an array, or the key.
816 */
817
818 }, {
819 key: "_key",
820 value: function _key(key, index) {
821 return this.currentType === 'Array' ? index : key;
822 }
823 /**
824 * Add a new element to the parent.
825 */
826
827 }, {
828 key: "_next",
829 value: function _next() {
830 if (!this._isElementEmpty(this.nextElement) && !this._isElementEmpty(this)) {
831 this.parent.insertAfter(this, '', '');
832 }
833 }
834 /**
835 * Removes the added elements from the element.
836 */
837
838 }, {
839 key: "_removeAddedElements",
840 value: function _removeAddedElements() {
841 if (this.elements) {
842 var _iterator4 = _createForOfIteratorHelper(this.elements),
843 _step4;
844
845 try {
846 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
847 var element = _step4.value;
848
849 if (element.isAdded()) {
850 this.elements.remove(element);
851 }
852 }
853 } catch (err) {
854 _iterator4.e(err);
855 } finally {
856 _iterator4.f();
857 }
858 }
859 }
860 }]);
861
862 return Element;
863}(EventEmitter);
864/**
865 * Represents a doubly linked list.
866 */
867
868
869var LinkedList = /*#__PURE__*/function () {
870 // Instantiate the new doubly linked list.
871 function LinkedList(doc, originalDoc) {
872 _classCallCheck(this, LinkedList);
873
874 this.firstElement = null;
875 this.lastElement = null;
876 this.doc = doc;
877 this.originalDoc = originalDoc;
878 this.keys = keys(this.originalDoc);
879
880 if (this.doc.currentType === 'Array') {
881 this.keys = this.keys.map(function (k) {
882 return parseInt(k, 10);
883 });
884 }
885
886 this.size = this.keys.length;
887 this.loaded = 0;
888 this._map = {};
889 }
890 /**
891 * Insert data after the provided element.
892 *
893 * @param {Element} element - The element to insert after.
894 * @param {String} key - The element key.
895 * @param {Object} value - The element value.
896 * @param {Boolean} added - If the element is new.
897 * @param {Object} parent - The parent.
898 *
899 * @returns {Element} The inserted element.
900 */
901
902
903 _createClass(LinkedList, [{
904 key: "at",
905 value:
906 /**
907 * Get the element at the provided index.
908 *
909 * @param {Integer} index - The index.
910 *
911 * @returns {Element} The matching element.
912 */
913 function at(index) {
914 this.flush();
915
916 if (!Number.isInteger(index)) {
917 return undefined;
918 }
919
920 var element = this.firstElement;
921
922 for (var i = 0; i < index; i++) {
923 if (!element) {
924 return undefined;
925 }
926
927 element = element.nextElement;
928 }
929
930 return element === null ? undefined : element;
931 }
932 }, {
933 key: "get",
934 value: function get(key) {
935 this.flush();
936 return this._map[key];
937 }
938 }, {
939 key: "insertAfter",
940 value: function insertAfter(element, key, value, added, parent) {
941 this.flush();
942 return this._insertAfter(element, key, value, added, parent);
943 }
944 /**
945 * Update the currentKey of each element if array elements.
946 *
947 * @param {Element} element - The element to insert after.
948 * @param {Number} add - 1 if adding a new element, -1 if removing.
949 */
950
951 }, {
952 key: "updateKeys",
953 value: function updateKeys(element, add) {
954 this.flush();
955
956 while (element.nextElement) {
957 element.nextElement.currentKey += add;
958 element = element.nextElement;
959 }
960 }
961 /**
962 * If an element is added after a placeholder, convert that placeholder
963 * into an empty element with the correct key.
964 *
965 * @param {Element} element - The placeholder element.
966 */
967
968 }, {
969 key: "handleEmptyKeys",
970 value: function handleEmptyKeys(element) {
971 if (element.currentKey === '') {
972 var e = element;
973
974 while (e.currentKey === '') {
975 if (!e.previousElement) {
976 e.currentKey = 0;
977 break;
978 } else {
979 e = e.previousElement;
980 }
981 }
982
983 while (e.nextElement) {
984 e.nextElement.currentKey = e.currentKey + 1;
985 e = e.nextElement;
986 }
987 }
988 }
989 /**
990 * Insert data before the provided element.
991 *
992 * @param {Element} element - The element to insert before.
993 * @param {String} key - The element key.
994 * @param {Object} value - The element value.
995 * @param {Boolean} added - If the element is new.
996 * @param {Object} parent - The parent.
997 *
998 * @returns {Element} The inserted element.
999 */
1000
1001 }, {
1002 key: "insertBefore",
1003 value: function insertBefore(element, key, value, added, parent) {
1004 this.flush();
1005 return this._insertBefore(element, key, value, added, parent);
1006 }
1007 /**
1008 * Insert data at the beginning of the list.
1009 *
1010 * @param {String} key - The element key.
1011 * @param {Object} value - The element value.
1012 * @param {Boolean} added - If the element is new.
1013 * @param {Object} parent - The parent.
1014 *
1015 * @returns {Element} The data element.
1016 */
1017
1018 }, {
1019 key: "insertBeginning",
1020 value: function insertBeginning(key, value, added, parent) {
1021 this.flush();
1022 return this._insertBeginning(key, value, added, parent);
1023 }
1024 /**
1025 * Insert data at the end of the list.
1026 *
1027 * @param {String} key - The element key.
1028 * @param {Object} value - The element value.
1029 * @param {Boolean} added - If the element is new.
1030 * @param {Object} parent - The parent.
1031 *
1032 * @returns {Element} The data element.
1033 */
1034
1035 }, {
1036 key: "insertEnd",
1037 value: function insertEnd(key, value, added, parent) {
1038 this.flush();
1039
1040 if (!this.lastElement) {
1041 return this.insertBeginning(key, value, added, parent);
1042 }
1043
1044 return this.insertAfter(this.lastElement, key, value, added, parent);
1045 }
1046 }, {
1047 key: "flush",
1048 value: function flush() {
1049 if (this.loaded < this.size) {
1050 // Only iterate from the loaded index to the size.
1051 var _iterator5 = _createForOfIteratorHelper(this),
1052 _step5;
1053
1054 try {
1055 for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
1056 var element = _step5.value;
1057
1058 if (element && element.elements) {
1059 element.elements.flush();
1060 }
1061 }
1062 } catch (err) {
1063 _iterator5.e(err);
1064 } finally {
1065 _iterator5.f();
1066 }
1067 }
1068 }
1069 /**
1070 * Get an iterator for the list.
1071 *
1072 * @returns {Iterator} The iterator.
1073 */
1074
1075 }, {
1076 key: Symbol.iterator,
1077 value: function value() {
1078 var _this2 = this;
1079
1080 var currentElement;
1081 var index = 0;
1082 return {
1083 next: function next() {
1084 if (_this2._needsLazyLoad(index)) {
1085 var key = _this2.keys[index];
1086 index += 1;
1087 currentElement = _this2._lazyInsertEnd(key);
1088 return {
1089 value: currentElement
1090 };
1091 } else if (_this2._needsStandardIteration(index)) {
1092 if (currentElement) {
1093 currentElement = currentElement.nextElement;
1094 } else {
1095 currentElement = _this2.firstElement;
1096 }
1097
1098 if (currentElement) {
1099 index += 1;
1100 return {
1101 value: currentElement
1102 };
1103 }
1104
1105 return {
1106 done: true
1107 };
1108 }
1109
1110 return {
1111 done: true
1112 };
1113 }
1114 };
1115 }
1116 }, {
1117 key: "_needsLazyLoad",
1118 value: function _needsLazyLoad(index) {
1119 return index === 0 && this.loaded === 0 && this.size > 0 || this.loaded <= index && index < this.size;
1120 }
1121 }, {
1122 key: "_needsStandardIteration",
1123 value: function _needsStandardIteration(index) {
1124 return this.loaded > 0 && index < this.loaded && index < this.size;
1125 }
1126 /**
1127 * Insert on the end of the list lazily.
1128 *
1129 * @param {String} key - The key.
1130 *
1131 * @returns {Element} The inserted element.
1132 */
1133
1134 }, {
1135 key: "_lazyInsertEnd",
1136 value: function _lazyInsertEnd(key) {
1137 this.size -= 1;
1138 return this._insertEnd(key, this.originalDoc[key], this.doc.cloned, this.doc);
1139 }
1140 }, {
1141 key: "_insertEnd",
1142 value: function _insertEnd(key, value, added, parent) {
1143 if (!this.lastElement) {
1144 return this._insertBeginning(key, value, added, parent);
1145 }
1146
1147 return this._insertAfter(this.lastElement, key, value, added, parent);
1148 }
1149 }, {
1150 key: "_insertBefore",
1151 value: function _insertBefore(element, key, value, added, parent) {
1152 var newElement = new Element(key, value, added, parent, element.previousElement, element);
1153
1154 if (element.previousElement) {
1155 element.previousElement.nextElement = newElement;
1156 } else {
1157 this.firstElement = newElement;
1158 }
1159
1160 element.previousElement = newElement;
1161 this._map[newElement.key] = newElement;
1162 this.size += 1;
1163 this.loaded += 1;
1164 return newElement;
1165 }
1166 }, {
1167 key: "_insertBeginning",
1168 value: function _insertBeginning(key, value, added, parent) {
1169 if (!this.firstElement) {
1170 var element = new Element(key, value, added, parent, null, null);
1171 this.firstElement = this.lastElement = element;
1172 this.size += 1;
1173 this.loaded += 1;
1174 this._map[element.key] = element;
1175 return element;
1176 }
1177
1178 var newElement = this.insertBefore(this.firstElement, key, value, added, parent);
1179 this._map[newElement.key] = newElement;
1180 return newElement;
1181 }
1182 }, {
1183 key: "_insertAfter",
1184 value: function _insertAfter(element, key, value, added, parent) {
1185 var newElement = new Element(key, value, added, parent, element, element.nextElement);
1186
1187 if (element.nextElement) {
1188 element.nextElement.previousElement = newElement;
1189 } else {
1190 this.lastElement = newElement;
1191 }
1192
1193 element.nextElement = newElement;
1194 this._map[newElement.key] = newElement;
1195 this.size += 1;
1196 this.loaded += 1;
1197 return newElement;
1198 }
1199 /**
1200 * Remove the element from the list.
1201 *
1202 * @param {Element} element - The element to remove.
1203 *
1204 * @returns {DoublyLinkedList} The list with the element removed.
1205 */
1206
1207 }, {
1208 key: "remove",
1209 value: function remove(element) {
1210 this.flush();
1211
1212 if (element.previousElement) {
1213 element.previousElement.nextElement = element.nextElement;
1214 } else {
1215 this.firstElement = element.nextElement;
1216 }
1217
1218 if (element.nextElement) {
1219 element.nextElement.previousElement = element.previousElement;
1220 } else {
1221 this.lastElement = element.previousElement;
1222 }
1223
1224 element.nextElement = element.previousElement = null;
1225 delete this._map[element.currentKey];
1226 this.size -= 1;
1227 this.loaded -= 1;
1228 return this;
1229 }
1230 }]);
1231
1232 return LinkedList;
1233}();
1234
1235module.exports = Element;
1236module.exports.LinkedList = LinkedList;
1237module.exports.Events = Events;
1238module.exports.DATE_FORMAT = DATE_FORMAT;
\No newline at end of file