UNPKG

4.96 kBJavaScriptView Raw
1'use strict';
2
3const EventEmitter = require('eventemitter3');
4const Element = require('./element');
5const LinkedList = Element.LinkedList;
6const ObjectGenerator = require('./object-generator');
7
8/**
9 * The event constant.
10 */
11const Events = {
12 'Cancel': 'Document::Cancel'
13};
14
15/**
16 * The id field.
17 */
18const ID = '_id';
19
20/**
21 * Represents a document.
22 */
23class Document extends EventEmitter {
24 /**
25 * Send cancel event.
26 */
27 cancel() {
28 for (let element of this.elements) {
29 element.cancel();
30 }
31 this.emit(Events.Cancel);
32 }
33
34 /**
35 * Create the new document from the provided object.
36 *
37 * @param {Object} doc - The document.
38 * @param {boolean} cloned - If it is a cloned document.
39 */
40 constructor(doc, cloned) {
41 super();
42 this.doc = doc;
43 this.cloned = cloned || false;
44 this.isUpdatable = true;
45 this.elements = this._generateElements();
46 }
47
48 /**
49 * Generate the javascript object for this document.
50 *
51 * @returns {Object} The javascript object.
52 */
53 generateObject() {
54 return ObjectGenerator.generate(this.elements);
55 }
56
57 /**
58 * Generate the javascript object with the original elements in this document.
59 *
60 * @returns {Object} The original javascript object.
61 */
62 generateOriginalObject() {
63 return ObjectGenerator.generateOriginal(this.elements);
64 }
65
66 /**
67 * Get an element by its key.
68 *
69 * @param {String} key
70 *
71 * @returns {Element} The element.
72 */
73 get(key) {
74 return this.elements.get(key);
75 }
76
77 /**
78 * Get an element by a series of segment names.
79 *
80 * @param {Array} path - The series of fieldnames. Cannot be empty.
81 *
82 * @returns {Element} The element.
83 */
84 getChild(path) {
85 if (!path) {
86 return undefined;
87 }
88 let element = (this.currentType === 'Array') ? this.elements.at(path[0]) : this.elements.get(path[0]);
89 let i = 1;
90 while (i < path.length) {
91 if (element === undefined) {
92 return undefined;
93 }
94 element = element.currentType === 'Array' ? element.at(path[i]) : element.get(path[i]);
95 i++;
96 }
97 return element;
98 }
99
100 /**
101 * Get the _id value for the document.
102 *
103 * @returns {Object} The id.
104 */
105 getId() {
106 const element = this.get(ID);
107 return element ? element.generateObject() : null;
108 }
109
110 /**
111 * Get the _id value as a string. Required if _id is not always an ObjectId.
112 *
113 * @returns {String} The string id.
114 */
115 getStringId() {
116 const element = this.get(ID);
117 if (!element) {
118 return null;
119 } else if (element.currentType === 'Array' || element.currentType === 'Object') {
120 return JSON.stringify(element.generateObject());
121 }
122 return '' + element.value;
123 }
124
125 /**
126 * Insert a placeholder element at the end of the document.
127 *
128 * @returns {Element} The placeholder element.
129 */
130 insertPlaceholder() {
131 return this.insertEnd('', '');
132 }
133
134 /**
135 * Add a new element to this document.
136 *
137 * @param {String} key - The element key.
138 * @param {Object} value - The value.
139 *
140 * @returns {Element} The new element.
141 */
142 insertEnd(key, value) {
143 var newElement = this.elements.insertEnd(key, value, true, this);
144 this.emit(Element.Events.Added);
145 return newElement;
146 }
147
148 /**
149 * Insert an element after the provided element.
150 *
151 * @param {Element} element - The element to insert after.
152 * @param {String} key - The key.
153 * @param {Object} value - The value.
154 *
155 * @returns {Element} The new element.
156 */
157 insertAfter(element, key, value) {
158 var newElement = this.elements.insertAfter(element, key, value, true, this);
159 this.emit(Element.Events.Added);
160 return newElement;
161 }
162
163 /**
164 * A document always exists, is never added.
165 *
166 * @returns {false} Always false.
167 */
168 isAdded() {
169 return false;
170 }
171
172 /**
173 * Determine if the element is modified at all.
174 *
175 * @returns {Boolean} If the element is modified.
176 */
177 isModified() {
178 for (let element of this.elements) {
179 if (element.isModified()) {
180 return true;
181 }
182 }
183 return false;
184 }
185
186 /**
187 * A document is never removed
188 *
189 * @returns {false} Always false.
190 */
191 isRemoved() {
192 return false;
193 }
194
195 /**
196 * The document object is always the root object.
197 *
198 * @returns {true} Always true.
199 */
200 isRoot() {
201 return true;
202 }
203
204 /**
205 * Handle the next element in the document.
206 */
207 next() {
208 this.elements.flush();
209 const lastElement = this.elements.lastElement;
210 if (lastElement && lastElement.isAdded()) {
211 if (lastElement.isBlank()) {
212 lastElement.remove();
213 } else {
214 this.insertPlaceholder();
215 }
216 } else {
217 this.insertPlaceholder();
218 }
219 }
220
221 /**
222 * Generates a sequence of elements.
223 *
224 * @returns {Array} The elements.
225 */
226 _generateElements() {
227 return new LinkedList(this, this.doc);
228 }
229}
230
231module.exports = Document;
232module.exports.Events = Events;