UNPKG

@openxmldev/linq-to-xml

Version:
130 lines 4.67 kB
/** * @author Thomas Barnekow * @license MIT */ import { ArgumentException, XAttribute, XContainer, XNode, XText, } from './internal.js'; /** * Provides tools for inserting content into `XContainer`s. * * @internal */ export class Inserter { /** * Initializes a new instance with the parent `XContainer` and either * - the anchor `XNode` after which content is to be added or * - `null` if content is to be prepended before the first node. * * @param parent The parent `XContainer` to which the content is added. * @param anchor The anchor `XNode` after which content is added or `null`. */ constructor(parent, anchor) { this._parent = parent; this._previous = anchor; this._text = null; } /** * Adds the given content to the parent `XContainer`. * * @param content The content to be added. */ add(content) { this.addContent(content); if (this._text === null) return; if (this._parent._content === null) { this._parent._content = this._text; } else { if (this._previous instanceof XText) { this._previous.value += this._text; } else if (this._text.length > 0) { this._parent.convertTextToNode(); this.insertNode(new XText(this._text)); } } } addContent(content) { if (content == null) return; if (content instanceof XNode) { this.addNode(content); } else if (typeof content === 'string') { this.addString(content); } else if (typeof content[Symbol.iterator] === 'function') { for (const item of content) { this.addContent(item); } } else if (content instanceof XAttribute) { throw new ArgumentException('content', 'Attribute cannot be added to content.'); } else { this.addString(XContainer.getStringValue(content)); } } addNode(node) { this._parent.validateNode(node, this._previous); // Create a deep clone of the node if necessary. if (node._parent !== null) { node = node.cloneNode(); } else { let parent = this._parent; while (parent._parent !== null) parent = parent._parent; if (node === parent) node = node.cloneNode(); } // Ensure the future parent's string content, if any, is turned into an // XText node so that we can add further XNode siblings. this._parent.convertTextToNode(); // Before adding the node, "consume" any text content we assembled so far, // inserting an XText node with such text content. if (this._text !== null && this._text.length > 0) { if (this._previous instanceof XText) { this._previous.value += this._text; } else { this.insertNode(new XText(this._text)); } this._text = null; } // Finally, insert the node, which does not have a parent at this point. this.insertNode(node); } addString(s) { this._parent.validateString(s); this._text = this._text === null ? s : this._text + s; } // Prepends if previous == null, otherwise inserts after previous insertNode(node) { node._parent = this._parent; if (this._parent._content === null || typeof this._parent._content === 'string') { // Insert node as only node. node._next = node; this._parent._content = node; } else if (this._previous === null) { // Insert node as first node. const last = this._parent._content; node._next = last._next; last._next = node; } else { // Insert node after this._previous, considering whether this._previous // was the last node (this._parent._content). If so, make the node the // new last node. node._next = this._previous._next; this._previous._next = node; if (this._parent._content === this._previous) this._parent._content = node; } // Insert further nodes after the node we just inserted. this._previous = node; } } //# sourceMappingURL=Inserter.js.map