angular-xml-editor
Version:
XML editor component for Angular
1,265 lines (1,255 loc) • 388 kB
JavaScript
import { Subject } from 'rxjs';
import { Injectable, Inject, Component, Input, NgModule, EventEmitter, ViewEncapsulation, forwardRef, Renderer2, ViewChild, Output, defineInjectable, inject } from '@angular/core';
import { DOCUMENT, CommonModule } from '@angular/common';
import { NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule } from '@angular/forms';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlEditorDebugger {
constructor() {
this.actualCursorPosChanged$ = new Subject();
this.inputSelectionChanged$ = new Subject();
}
/**
* @param {?} pos
* @return {?}
*/
setActualCursorPos(pos) {
this.actualCursorPosChanged$.next(pos);
}
/**
* @param {?} info
* @return {?}
*/
setInputSelection(info) {
this.inputSelectionChanged$.next(info);
}
/**
* @return {?}
*/
ngOnDestroy() {
this.actualCursorPosChanged$.complete();
this.inputSelectionChanged$.complete();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlToolbox {
/**
* @param {?} node
* @return {?}
*/
static IsTextOrCommentNode(node) {
switch (node.nodeType) {
case Node.TEXT_NODE: // text node
case Node.COMMENT_NODE:
return true;
default:
return false;
}
// return ((node.nodeType === System.Xml.XmlText) || (node.nodeType === System.Xml.XmlComment));
}
/**
* @param {?} node
* @return {?}
*/
static GetNodeDebugContext(node) {
if (node === null || node === undefined) {
return 'NULL';
}
if (node.nodeType === Node.TEXT_NODE) {
if (node.textContent === ' ' || node.textContent === String.fromCharCode(160)) {
return `TextContent!Parent=${XmlToolbox.GetNodeDebugContext(node.parentNode)}`;
}
else {
return `TextContent='${node.textContent}'`;
}
}
/** @type {?} */
const asElem = /** @type {?} */ (node);
if (asElem === undefined) {
return `localName: ${node.nodeName}, nodeType:${node.nodeType}`;
}
else {
return asElem.outerHTML;
}
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class Xml2htmlAttributeWorker {
/**
* @param {?} rules
*/
constructor(rules) {
this.rules = rules;
}
/**
* writes the given attributes to the html node in data-attribute-syntax *
* @param {?} xmlElement
* @param {?} htmlElement
* @return {?}
*/
writeAllXmlAttributesToHtml(xmlElement, htmlElement) {
/** @type {?} */
const attributes = xmlElement.attributes;
for (let i = 0; i < attributes.length; ++i) {
/** @type {?} */
const attr = attributes[i];
this.writeAttributeToHtml(htmlElement, attr.name, attr.value);
}
}
/**
* writes the given attribute value to the html node in data-attribute-syntax *
* @param {?} htmlElement
* @param {?} key
* @param {?} value
* @return {?}
*/
writeAttributeToHtml(htmlElement, key, value) {
/** @type {?} */
const name = `${Xml2htmlAttributeWorker.DataAttribPraefix}${key}`;
if (value && value.length > 0) {
htmlElement.setAttribute(name, value);
}
else {
htmlElement.removeAttribute(name);
}
this.updateAttributesDescriptionInHtmlElement(htmlElement);
}
/**
* writes the given attribute value from the html node data-attribute-syntax *
* @param {?} htmlElement
* @param {?} key
* @return {?}
*/
getAttributeValueFromHtml(htmlElement, key) {
/** @type {?} */
const attributes = htmlElement.attributes;
for (let i = 0; i < attributes.length; ++i) {
if (attributes[i].name === `${Xml2htmlAttributeWorker.DataAttribPraefix}${key}`) {
return attributes[i].value;
}
}
return undefined;
}
/**
* writes the given attribute value to the xml node (and gets it from the html node from data-attribute-syntax) *
* @param {?} htmlElement
* @param {?} xmlElement
* @return {?}
*/
writeAllHtmlAttributesToXml(htmlElement, xmlElement) {
/** @type {?} */
const attributes = htmlElement.attributes;
for (let i = 0; i < attributes.length; ++i) {
/** @type {?} */
const attr = attributes[i];
if (attr.name.startsWith(Xml2htmlAttributeWorker.DataAttribPraefix)) {
xmlElement.setAttribute(attr.name.substr(Xml2htmlAttributeWorker.DataAttribPraefix.length), attr.value);
}
}
}
/**
* updates the 'for human eyes' only description of the attributes inside a html node *
* @param {?} htmlElement
* @return {?}
*/
updateAttributesDescriptionInHtmlElement(htmlElement) {
/** @type {?} */
const attributes = htmlElement.attributes;
/** @type {?} */
let allAttributes = '';
for (let i = 0; i < attributes.length; ++i) {
/** @type {?} */
const attr = attributes[i];
if (attr.name.startsWith(Xml2htmlAttributeWorker.DataAttribPraefix)) {
allAttributes = `${allAttributes} ${attr.name.substr(Xml2htmlAttributeWorker.DataAttribPraefix.length)}="${attr.value}"`;
}
}
if (allAttributes.length > 0) {
htmlElement.setAttribute('data-attributes', allAttributes);
}
else {
htmlElement.removeAttribute('data-attributes');
}
}
}
Xml2htmlAttributeWorker.DataAttribPraefix = 'data-attribute-';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class Xml2html {
/**
* @param {?} rules
*/
constructor(rules) {
this.rules = rules;
this.attributeWorker = new Xml2htmlAttributeWorker(rules);
}
/**
* @param {?} node
* @return {?}
*/
static getTagNameFromNode(node) {
/** @type {?} */
const elem = /** @type {?} */ (node);
/** @type {?} */
let name = '';
if (elem) {
name = elem.getAttribute(Xml2html.DataTagName);
}
if (name === undefined || name === null || name === '') {
throw new Error(`node has no DataTagName attribute ${XmlToolbox.GetNodeDebugContext(node)}`);
}
return name;
}
/**
* @param {?} node
* @return {?}
*/
static isFloatingElement(node) {
/** @type {?} */
const elem = /** @type {?} */ (node);
if (elem && elem.classList) {
return elem.classList.contains(Xml2html.FloatingTagClassName);
}
return false;
}
/**
* @param {?} node
* @return {?}
*/
static isNoClosingElement(node) {
/** @type {?} */
const elem = /** @type {?} */ (node);
if (elem && elem.classList) {
return elem.classList.contains(Xml2html.NoClosingTagClassName);
}
return false;
}
/**
* @param {?} xml
* @return {?}
*/
ToHtml(xml) {
if (xml === '' || xml === undefined || xml === null) {
return '';
}
// alert(xml);
// remove newline / carriage return
xml = xml.replace(/\n/g, '');
xml = xml.replace(/\r/g, '');
// remove whitespace (tabs) before tags
xml = xml.replace(/[\t]+\</g, '<');
// remove whitespace between tags
xml = xml.replace(/\>[\t]+\</g, '><');
// remove whitespace after tags
xml = xml.replace(/\>[\t]+$/g, '>');
/** @type {?} */
const domparser = new DOMParser();
/** @type {?} */
const domdoc = domparser.parseFromString(xml, 'text/xml');
/** @type {?} */
const targetDocument = new Document();
if (domdoc.documentElement === null) {
return '';
}
/** @type {?} */
const body = this.GetHtmlElementFromXmlElement(domdoc.documentElement, targetDocument);
if (body && body.nodeType === Node.ELEMENT_NODE) {
/** @type {?} */
const html = (/** @type {?} */ (body)).outerHTML;
return html;
}
else {
throw new Error(`root node ${XmlToolbox.GetNodeDebugContext(body)} is no element`);
}
}
/**
* @param {?} ruleElem
* @return {?}
*/
createNewElementHtml(ruleElem) {
/** @type {?} */
const elem = this.createNewElementByName(ruleElem.tagName);
return elem.outerHTML;
}
/**
* @param {?} tagName
* @return {?}
*/
createNewElementByName(tagName) {
/** @type {?} */
const tempDoc = new Document();
/** @type {?} */
const newElement = tempDoc.createElement('div');
/** @type {?} */
const rule = this.rules.ruleElements.get(tagName);
if (rule && rule.floating === true) {
newElement.classList.add(Xml2html.FloatingTagClassName);
}
// visualize closing tag or not?
if (rule && rule.empty === true) {
newElement.classList.add(Xml2html.NoClosingTagClassName);
}
newElement.setAttribute(Xml2html.DataTagName, tagName);
return newElement;
}
/**
* @param {?} node
* @param {?} targetDocument
* @return {?}
*/
GetHtmlElementFromXmlElement(node, targetDocument) {
switch (node.nodeType) {
case Node.ELEMENT_NODE: // element node
/** @type {?} */
const asElement = /** @type {?} */ (node);
/** @type {?} */
const nodeName = asElement.nodeName;
/** @type {?} */
let newElement = targetDocument.createElement('div');
/** @type {?} */
const rule = this.rules.ruleElements.get(nodeName);
if (rule && rule.floating) {
newElement.classList.add(Xml2html.FloatingTagClassName);
}
// visualize closing tag or not?
if (rule && rule.empty) {
newElement.classList.add(Xml2html.NoClosingTagClassName);
}
newElement.setAttribute(Xml2html.DataTagName, nodeName);
/** @type {?} */
const children = asElement.childNodes;
if (children.length === 0) {
newElement.appendChild(targetDocument.createTextNode('')); // to prevent non closing tags
}
else {
for (let i = 0; i < children.length; ++i) {
/** @type {?} */
const childAsHtml = this.GetHtmlElementFromXmlElement(children[i], targetDocument);
if (childAsHtml !== undefined) {
newElement.appendChild(childAsHtml);
}
}
}
// insert attributes
this.attributeWorker.writeAllXmlAttributesToHtml(asElement, newElement);
return newElement;
case Node.TEXT_NODE: // text node
/** @type {?} */
const content = (/** @type {?} */ (node)).textContent;
if (content.length > 0) {
return targetDocument.createTextNode(content);
}
else {
return undefined;
}
case Node.COMMENT_NODE:
// create element node
newElement = targetDocument.createElement('div');
newElement.classList.add(Xml2html.CommentTagClassName);
newElement.setAttribute(Xml2html.DataTagName, 'comment');
newElement.textContent = (/** @type {?} */ (node)).textContent;
return newElement;
/* yet not implemented types */
case Node.PROCESSING_INSTRUCTION_NODE:
console.warn(`nodeType "PROCESSING_INSTRUCTION_NODE" ${XmlToolbox.GetNodeDebugContext(node)} not implemented yet`);
return undefined;
case Node.DOCUMENT_NODE:
throw new Error(`nodeType "Node.DOCUMENT_NODE" ${XmlToolbox.GetNodeDebugContext(node)} not implemented yet`);
case Node.DOCUMENT_TYPE_NODE:
throw new Error(`nodeType "Node.DOCUMENT_NODE" ${XmlToolbox.GetNodeDebugContext(node)} not implemented yet`);
case Node.DOCUMENT_FRAGMENT_NODE:
throw new Error(`nodeType "Node.DOCUMENT_FRAGMENT_NODE" ${XmlToolbox.GetNodeDebugContext(node)} not implemented yet`);
/* Depreached node types */
case Node.ATTRIBUTE_NODE:
throw new Error(`nodeType "Node.ATTRIBUTE_NODE" ${XmlToolbox.GetNodeDebugContext(node)} is depreached`);
case Node.CDATA_SECTION_NODE:
throw new Error(`nodeType "Node.CDATA_SECTION_NODE" ${XmlToolbox.GetNodeDebugContext(node)} is depreached`);
case Node.ENTITY_REFERENCE_NODE:
throw new Error(`nodeType "Node.ENTITY_REFERENCE_NODE" ${XmlToolbox.GetNodeDebugContext(node)} is depreached`);
case Node.ENTITY_NODE:
throw new Error(`nodeType "Node.ENTITY_NODE" ${XmlToolbox.GetNodeDebugContext(node)} is depreached`);
case Node.NOTATION_NODE:
throw new Error(`nodeType "Node.NOTATION_NODE" ${XmlToolbox.GetNodeDebugContext(node)} is depreached`);
default:
// tslint:disable-next-line:max-line-length
throw new Error(`unknown xmlElement nodeType ${node.nodeType} in value: ${XmlToolbox.GetNodeDebugContext(node)}`);
}
}
}
Xml2html.DataTagName = 'data-tagname';
Xml2html.NoClosingTagClassName = 'xmlTagNoClosing';
Xml2html.FloatingTagClassName = 'floating';
Xml2html.CommentTagClassName = 'comment';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class DomDummyNodeManager {
constructor() { }
/**
* @param {?} node
* @return {?}
*/
static IsDummyNode(node) {
/** @type {?} */
const asElem = /** @type {?} */ (node);
if (asElem && node.nodeType === Node.ELEMENT_NODE) {
return asElem.classList.contains(DomDummyNodeManager.DummyNodeClassName);
}
return false;
}
/**
* @param {?} node
* @return {?}
*/
static IsBeforeFirstTagDummyNode(node) {
if (DomDummyNodeManager.IsDummyNode(node)) {
/** @type {?} */
const asElem = /** @type {?} */ (node);
if (asElem && node.nodeType === Node.ELEMENT_NODE) {
return asElem.classList.contains(DomDummyNodeManager.DummyNodeBeforeFirstTagClassName);
}
return false;
}
else {
return false;
}
}
/**
* @param {?} node
* @return {?}
*/
static getPreviousSibling(node) {
if (!node) {
return undefined;
}
while (node) {
node = node.previousSibling;
if (node) {
if (DomDummyNodeManager.IsDummyNode(node)) ;
else {
return node;
}
}
}
return undefined;
}
/**
* @param {?} node
* @return {?}
*/
static getNextSibling(node) {
if (!node) {
return undefined;
}
while (node) {
node = node.nextSibling;
if (node) {
if (DomDummyNodeManager.IsDummyNode(node)) ;
else {
return node;
}
}
}
return undefined;
}
/**
* @param {?} node
* @return {?}
*/
static getChildNodeCount(node) {
if (!node) {
return 0;
}
/** @type {?} */
let count = 0;
for (let i = 0; i < node.childNodes.length; i++) {
if (!DomDummyNodeManager.IsDummyNode(node.childNodes[i])) {
count++;
}
}
return count;
}
/**
* @param {?} node
* @return {?}
*/
static getFirstChild(node) {
if (!node) {
return undefined;
}
/** @type {?} */
let first = /** @type {?} */ (node.firstChild);
while (DomDummyNodeManager.IsDummyNode(first)) {
first = first.nextSibling;
}
return first;
}
/**
* @param {?} node
* @return {?}
*/
static getLastChild(node) {
if (!node) {
return undefined;
}
/** @type {?} */
let last = /** @type {?} */ (node.lastChild);
while (DomDummyNodeManager.IsDummyNode(last)) {
last = last.previousSibling;
}
return last;
}
/**
* @param {?} node
* @param {?=} nodeDepth
* @return {?}
*/
RemoveAllDummyNodes(node, nodeDepth = 0) {
/** @type {?} */
let children = [];
for (let i = 0; i < node.childNodes.length; ++i) {
children.push(node.childNodes[i]);
}
// delete all dummy node children
children.forEach(child => {
if (DomDummyNodeManager.IsDummyNode(child)) {
node.removeChild(child);
}
});
children = [];
for (let i = 0; i < node.childNodes.length; ++i) {
children.push(node.childNodes[i]);
}
// update child nodes
children.forEach(child => {
this.RemoveAllDummyNodes(child, nodeDepth + 1);
});
}
/**
* @param {?} node
* @param {?=} nodeDepth
* @return {?}
*/
UpdateDummyNodes(node, nodeDepth = 0) {
/** @type {?} */
const document = node.ownerDocument;
/** @type {?} */
const asElement = /** @type {?} */ (node);
/** @type {?} */
const children = [];
for (let i = 0; i < node.childNodes.length; ++i) {
children.push(node.childNodes[i]);
}
if (asElement && !DomDummyNodeManager.IsDummyNode(asElement) && !DomDummyNodeManager.IsDummyNode(asElement.parentElement)) {
if (asElement.nodeType === Node.TEXT_NODE) {
/** @type {?} */
const before = asElement.previousSibling;
if (before.nodeType === Node.TEXT_NODE) ;
else {
if (!DomDummyNodeManager.IsDummyNode(before)) {
asElement.parentNode.insertBefore(this.createDummyNode(document), asElement);
}
}
}
if (asElement.nodeType === Node.ELEMENT_NODE) {
/** @type {?} */
let firstChild = node.firstChild;
if (!firstChild) {
// create dummy node in empty tag
node.appendChild(this.createDummyNode(document));
firstChild = node.firstChild;
}
if (Xml2html.isNoClosingElement(node)) ;
else {
firstChild = node.firstChild;
if (!firstChild) {
// create dummy node in empty tag
node.appendChild(this.createDummyNode(document));
firstChild = node.firstChild;
}
// Insert a dummy node before the first child
if (!DomDummyNodeManager.IsDummyNode(firstChild)) {
if (firstChild) {
node.insertBefore(this.createDummyNode(document), firstChild);
firstChild = node.firstChild;
}
else {
node.appendChild(this.createDummyNode(document));
}
}
/** @type {?} */
let lastChild = node.lastChild;
if (!DomDummyNodeManager.IsDummyNode(lastChild)) {
node.appendChild(this.createDummyNode(document));
lastChild = node.lastChild;
}
// Insert a dummy node before the first (dummy child) but optical in front of the node (to show the cursor in front of the node)
if (nodeDepth > 0) {
if (DomDummyNodeManager.IsDummyNode(firstChild) && !DomDummyNodeManager.IsBeforeFirstTagDummyNode(firstChild)) {
/** @type {?} */
const dummyNode = this.createDummyNode(document);
dummyNode.classList.add(DomDummyNodeManager.DummyNodeBeforeFirstTagClassName);
asElement.insertBefore(dummyNode, firstChild);
}
}
}
}
}
// update child nodes
children.forEach(child => {
this.UpdateDummyNodes(child, nodeDepth + 1);
});
}
/**
* @param {?} targetDocument
* @param {?=} content
* @return {?}
*/
createDummyNode(targetDocument, content) {
/** @type {?} */
const dummyNode = targetDocument.createElement('span');
/** @type {?} */
let dummy2TextNode;
if (content === undefined) {
dummy2TextNode = targetDocument.createTextNode(DomDummyNodeManager.DummyNodeContent);
}
else {
dummy2TextNode = targetDocument.createTextNode(content);
}
dummyNode.classList.add(DomDummyNodeManager.DummyNodeClassName);
dummyNode.appendChild(dummy2TextNode);
return dummyNode;
}
}
DomDummyNodeManager.DummyNodeClassName = 'dummyNode';
DomDummyNodeManager.DummyNodeBeforeFirstTagClassName = 'beforefirstTag';
DomDummyNodeManager.DummyNodeContent = String.fromCharCode(160);
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlCursorPos {
/**
* @param {?} position
* @return {?}
*/
static getXmlCursorPositionDebugName(position) {
switch (position) {
case XMLCursorPositions.CursorOnCompleteNode:
return 'CursorOnCompleteNode';
case XMLCursorPositions.CursorBehindNode:
return 'CursorBehindNode';
case XMLCursorPositions.CursorInEmptyNode:
return 'CursorInEmptyNode';
case XMLCursorPositions.CursorInsideTextNode:
return 'CursorInsideTextNode';
case XMLCursorPositions.CursorInFrontOfNode:
return 'CursorInFrontOfNode';
default:
return `unknown cursorPos ${position}`;
}
}
/**
* @param {?} pos
* @return {?}
*/
static getSelectionForPos(pos) {
if (DomDummyNodeManager.IsDummyNode(pos.ActualNode)) {
throw new Error(`XmlCursorPos.getSelectionForPos: pos.AktNode ${pos.getDebugDescription()} is a dummy node!`);
}
if (DomDummyNodeManager.IsDummyNode(pos.ActualNode.parentElement)) {
throw new Error(`XmlCursorPos.getSelectionForPos: pos.AktNode.parentElement ${pos.getDebugDescription()} is a dummy node!`);
}
switch (pos.PosAtNode) {
case XMLCursorPositions.CursorOnCompleteNode: // (0)
/** @type {?} */
let firstChild = pos.ActualNode.firstChild;
if (DomDummyNodeManager.IsBeforeFirstTagDummyNode(firstChild)) {
return {
node: firstChild.firstChild,
// text inside BeforeFirstTagDummyNod
offset: 0
};
}
else {
return {
node: pos.ActualNode,
offset: 0
};
}
case XMLCursorPositions.CursorInFrontOfNode: // (1)
// (1)
if (pos.ActualNode.nodeType === Node.TEXT_NODE) {
/** @type {?} */
const dummyBefore = pos.ActualNode.previousSibling;
if (DomDummyNodeManager.IsDummyNode(dummyBefore)) {
return {
node: dummyBefore.firstChild,
// before text node
offset: 1
};
}
else {
throw new Error('XMLCursorPositions.CursorInFrontOfNode (1):text node: is no dummy node: ' + pos.getDebugDescription());
}
}
else {
// no text node
firstChild = pos.ActualNode.firstChild;
if (DomDummyNodeManager.IsBeforeFirstTagDummyNode(firstChild)) {
return {
node: firstChild.firstChild,
// text inside BeforeFirstTagDummyNod
offset: 1
};
}
if (Xml2html.isNoClosingElement(pos.ActualNode) && DomDummyNodeManager.IsDummyNode(firstChild)) {
return { node: firstChild.firstChild, offset: 1 };
}
throw new Error('XMLCursorPositions.CursorInFrontOfNode (1):no text node: dont know what to do! ');
}
break;
case XMLCursorPositions.CursorBehindNode: // (7, 11)
/** @type {?} */
const dummyAfter = pos.ActualNode.nextSibling;
if (DomDummyNodeManager.IsDummyNode(dummyAfter)) {
return {
node: dummyAfter.firstChild,
// before text node
offset: 0
};
}
else {
throw new Error(`XMLCursorPositionen.CursorBehindNode should only be set, when the element has no next sibling.`);
}
case XMLCursorPositions.CursorInEmptyNode:
/** @type {?} */
const lastChild = pos.ActualNode.lastChild;
if (!DomDummyNodeManager.IsDummyNode(firstChild) && !DomDummyNodeManager.IsBeforeFirstTagDummyNode(firstChild)) {
return {
node: lastChild.firstChild,
// text inside DummyNode
offset: 0
};
}
break;
case XMLCursorPositions.CursorInsideTextNode:
return {
node: pos.ActualNode,
offset: pos.PosInTextnode
};
}
throw new Error(`showCursor.getSelectionForPos unknown CursorPos ${pos.getDebugDescription()}`);
}
constructor() {
this.ActualNode = null; // no node selected
this.PosAtNode = XMLCursorPositions.CursorOnCompleteNode;
this.PosInTextnode = 0;
}
/**
* @return {?}
*/
clone() {
/** @type {?} */
const klon = new XmlCursorPos();
klon.SetCursor(this.ActualNode, this.PosAtNode, this.PosInTextnode);
return klon;
}
/**
* @param {?} otherPos
* @return {?}
*/
equals(otherPos) {
if (!otherPos) {
return false;
}
if (otherPos.ActualNode !== this.ActualNode) {
return false;
}
if (otherPos.PosAtNode !== this.PosAtNode) {
return false;
}
if (this.PosAtNode === XMLCursorPositions.CursorInsideTextNode && this.PosInTextnode !== otherPos.PosInTextnode) {
return false;
}
return true;
}
/**
* @return {?}
*/
getDebugDescription() {
return `node:${XmlToolbox.GetNodeDebugContext(this.ActualNode)}, pos:${XmlCursorPos.getXmlCursorPositionDebugName(this.PosAtNode)}, posInText:${this.PosInTextnode}`;
}
/**
* @param {?} actualNode
* @param {?} posAtNode
* @param {?=} posInTextnode
* @return {?}
*/
SetCursor(actualNode, posAtNode, posInTextnode = 0) {
if (!actualNode) {
throw new Error('actualNode is null');
}
if (actualNode !== this.ActualNode) ;
else {
if (posAtNode !== this.PosAtNode) ;
else {
if (posInTextnode !== this.PosInTextnode) ;
}
}
this.ActualNode = actualNode;
this.PosAtNode = posAtNode;
this.PosInTextnode = posInTextnode;
}
/**
* @param {?} node
* @return {?}
*/
setCursorBehindNodeForDefaultOrRightMovement(node) {
/** @type {?} */
const nextSibling = DomDummyNodeManager.getNextSibling(node);
if (nextSibling) {
this.SetCursor(nextSibling, XMLCursorPositions.CursorInFrontOfNode);
}
else {
this.SetCursor(node, XMLCursorPositions.CursorBehindNode);
}
}
/**
* @param {?} node
* @return {?}
*/
setCursorpBehindNodeForLeftMovement(node) {
if (XmlToolbox.IsTextOrCommentNode(node)) {
// For a text node, the cursor is placed after the last character.
this.SetCursor(node, XMLCursorPositions.CursorInsideTextNode, Math.max(0, node.textContent.length - 1));
}
else {
/** @type {?} */
const childNodeCount = DomDummyNodeManager.getChildNodeCount(node);
if (childNodeCount === 0) {
// There are no children in the node.
if (!Xml2html.isNoClosingElement(node)) {
// If the cursor shows a close tag, then put it in the empty node
this.SetCursor(node, XMLCursorPositions.CursorInEmptyNode);
}
else {
// If the cursor does not display a close tag, then place it before the empty node.
this.SetCursor(node, XMLCursorPositions.CursorInFrontOfNode);
}
}
else {
// There are children in node
this.SetCursor(DomDummyNodeManager.getLastChild(node), XMLCursorPositions.CursorBehindNode);
}
}
}
}
/** @enum {number} */
const XMLCursorPositions = {
CursorInFrontOfNode: 0,
CursorOnCompleteNode: 1,
CursorInEmptyNode: 2,
CursorInsideTextNode: 3,
CursorBehindNode: 4,
};
XMLCursorPositions[XMLCursorPositions.CursorInFrontOfNode] = 'CursorInFrontOfNode';
XMLCursorPositions[XMLCursorPositions.CursorOnCompleteNode] = 'CursorOnCompleteNode';
XMLCursorPositions[XMLCursorPositions.CursorInEmptyNode] = 'CursorInEmptyNode';
XMLCursorPositions[XMLCursorPositions.CursorInsideTextNode] = 'CursorInsideTextNode';
XMLCursorPositions[XMLCursorPositions.CursorBehindNode] = 'CursorBehindNode';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlCursorPosMover {
/**
* @param {?} pos
* @param {?} rootNode
* @param {?} xmlRules
* @return {?}
*/
static moveLeft(pos, rootNode, xmlRules) {
if (pos === undefined) {
return false;
}
switch (pos.PosAtNode) {
case XMLCursorPositions.CursorOnCompleteNode:
// Put in front of the node
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInFrontOfNode);
break;
case XMLCursorPositions.CursorInFrontOfNode:
if (pos.ActualNode === rootNode) {
return false;
}
else {
/** @type {?} */
const previousSibling = DomDummyNodeManager.getPreviousSibling(pos.ActualNode);
if (previousSibling) {
// Previous sibling node exists
pos.setCursorpBehindNodeForLeftMovement(previousSibling);
}
else {
// no previous sibling node available -> set before parent node
pos.SetCursor(pos.ActualNode.parentNode, XMLCursorPositions.CursorInFrontOfNode);
}
}
break;
case XMLCursorPositions.CursorBehindNode:
pos.setCursorpBehindNodeForLeftMovement(pos.ActualNode);
break;
case XMLCursorPositions.CursorInEmptyNode:
// Put in front of the node
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInFrontOfNode);
break;
case XMLCursorPositions.CursorInsideTextNode:
if (XmlToolbox.IsTextOrCommentNode(pos.ActualNode)) {
// Node is Textnode
if (pos.PosInTextnode > 1) {
// Cursor one character to the left
pos.SetCursor(pos.ActualNode, pos.PosAtNode, pos.PosInTextnode - 1);
}
else {
// Put in front of the node
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInFrontOfNode);
}
}
else {
/** @type {?} */
const elemt = /** @type {?} */ (pos.ActualNode);
throw new Error(`XMLCursorPos.MoveLeft: CursorPos is XMLCursorPositionen.CursorInnerhalbDesTextNodes,
but no text node has been chosen, but the node ${elemt ? elemt.outerHTML : ''}`);
}
break;
default:
throw new Error(`XMLCursorPos.MoveLeft: unknown CursorPos ${pos.PosAtNode}`);
}
return true;
}
/**
* @param {?} pos
* @param {?} rootNode
* @param {?} xmlRules
* @return {?}
*/
static moveRight(pos, rootNode, xmlRules) {
if (pos === undefined) {
return false;
}
switch (pos.PosAtNode) {
case XMLCursorPositions.CursorOnCompleteNode:
/** @type {?} */
const tagName = Xml2html.getTagNameFromNode(pos.ActualNode);
/** @type {?} */
const rule = xmlRules.ruleElements.get(tagName);
if (rule && rule.empty === true) {
pos.setCursorBehindNodeForDefaultOrRightMovement(pos.ActualNode);
}
else {
/** @type {?} */
const firstChild = DomDummyNodeManager.getFirstChild(pos.ActualNode);
if (firstChild) {
pos.SetCursor(firstChild, XMLCursorPositions.CursorInFrontOfNode);
}
else {
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInEmptyNode);
}
}
break;
case XMLCursorPositions.CursorBehindNode:
/** @type {?} */
const nextSibling = DomDummyNodeManager.getNextSibling(pos.ActualNode);
if (nextSibling) {
/** @type {?} */
const elemt = /** @type {?} */ (pos.ActualNode);
throw new Error(`XMLCursorPos.MoveRight: cursor behind node should only be set, when no other siblings are following ${elemt ? elemt.outerHTML : ''}`);
}
else {
/** @type {?} */
const parentNextSibling = DomDummyNodeManager.getNextSibling(pos.ActualNode.parentElement);
if (parentNextSibling) {
pos.SetCursor(parentNextSibling, XMLCursorPositions.CursorInFrontOfNode);
}
else {
pos.SetCursor(pos.ActualNode.parentElement, XMLCursorPositions.CursorBehindNode);
}
}
break;
case XMLCursorPositions.CursorInEmptyNode:
// Place behind the node
pos.setCursorBehindNodeForDefaultOrRightMovement(pos.ActualNode);
break;
case XMLCursorPositions.CursorInFrontOfNode:
if (XmlToolbox.IsTextOrCommentNode(pos.ActualNode)) {
// Node is Textnode
if ((/** @type {?} */ (pos.ActualNode)).textContent.length > 1) {
// Textnode is not empty
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInsideTextNode, 1); // one character forward, behind the first character
}
else {
// Textnode is empty
// put behind the node
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorBehindNode);
// Because "CursorBehindNode" is not attractive, one more step
this.moveRight(pos, rootNode, xmlRules);
}
}
else {
/** @type {?} */
const childNodeCount = DomDummyNodeManager.getChildNodeCount(pos.ActualNode);
if (childNodeCount === 0) {
// no Children existing
if (Xml2html.isNoClosingElement(pos.ActualNode)) {
// If no closed tag is displayed for this node, then directly behind the node
// Place behind the node
pos.setCursorBehindNodeForDefaultOrRightMovement(pos.ActualNode);
}
else {
// Node has closing tag, so put in between
// Place in empty node
pos.SetCursor(pos.ActualNode, XMLCursorPositions.CursorInEmptyNode);
}
}
else {
/** @type {?} */
const firstChild = DomDummyNodeManager.getFirstChild(pos.ActualNode);
pos.SetCursor(firstChild, XMLCursorPositions.CursorInFrontOfNode);
}
}
break;
case XMLCursorPositions.CursorInsideTextNode:
if (XmlToolbox.IsTextOrCommentNode(pos.ActualNode)) {
// Node is Textnode
if ((/** @type {?} */ (pos.ActualNode)).textContent.length > pos.PosInTextnode + 1) {
// there is still text in the right of the textnode
// step one character forwards: behind the first char
pos.SetCursor(pos.ActualNode, pos.PosAtNode, pos.PosInTextnode + 1);
}
else {
// no text after the textnode
// place cursor behind the node
pos.setCursorBehindNodeForDefaultOrRightMovement(pos.ActualNode);
}
}
else {
/** @type {?} */
const elemt = /** @type {?} */ (pos.ActualNode);
throw new Error(`XMLCursorPos.MoveRight: CursorPos is XMLCursorPositionen.CursorInnerhalbDesTextNodes,
but no text node has been chosen, but the node ${elemt ? elemt.outerHTML : ''}`);
}
break;
default:
throw new Error(`String.Format("XMLCursorPos.MoveRight: unknown CursorPos ${pos.PosAtNode}`);
}
return true;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlCursor {
constructor() {
this.xmlDocumentRootNode = undefined;
this.domDummyNodeManager = new DomDummyNodeManager();
this.StartPos = new XmlCursorPos();
this.EndPos = new XmlCursorPos();
}
/**
* is there nothing between start and end pos?
* @return {?}
*/
isEmptySelection() {
if (this.StartPos.equals(this.EndPos)) {
return true;
}
return false;
}
/**
* @return {?}
*/
sortStartAndEnd() {
if (this.StartPos.ActualNode && this.EndPos.ActualNode) {
if (this.StartPos.ActualNode.isSameNode(this.EndPos.ActualNode)) {
if (this.StartPos.PosAtNode === XMLCursorPositions.CursorInsideTextNode &&
this.EndPos.PosAtNode === XMLCursorPositions.CursorInsideTextNode &&
this.StartPos.PosInTextnode > this.EndPos.PosInTextnode) {
this.swapStartAndEnd();
}
}
else {
/** @type {?} */
const compared = this.StartPos.ActualNode.compareDocumentPosition(this.EndPos.ActualNode);
// tslint:disable-next-line:no-bitwise
if (!(compared & Node.DOCUMENT_POSITION_FOLLOWING)) {
this.swapStartAndEnd();
}
}
}
}
/**
* @return {?}
*/
swapStartAndEnd() {
/** @type {?} */
const node = this.StartPos.ActualNode;
/** @type {?} */
const pos = this.StartPos.PosAtNode;
/** @type {?} */
const text = this.StartPos.PosInTextnode;
this.StartPos.SetCursor(this.EndPos.ActualNode, this.EndPos.PosAtNode, this.EndPos.PosInTextnode);
this.EndPos.SetCursor(node, pos, text);
}
/**
* @return {?}
*/
show() {
if (window.getSelection) {
/** @type {?} */
const selection = window.getSelection();
this.show2(selection);
}
else {
throw new Error('showCursor: unable to get "window.getSelection"');
}
}
/**
* @param {?} selection
* @return {?}
*/
show2(selection) {
if (!selection) {
return;
}
/** @type {?} */
const elems = document.querySelectorAll('.selected');
[].forEach.call(elems, function (el) {
el.classList.remove('selected');
});
/** @type {?} */
const startSelection = XmlCursorPos.getSelectionForPos(this.StartPos);
if (this.EndPos && this.EndPos.ActualNode && !this.EndPos.equals(this.StartPos)) {
/** @type {?} */
const endSelection = XmlCursorPos.getSelectionForPos(this.EndPos);
selection.setBaseAndExtent(startSelection.node, startSelection.offset, endSelection.node, endSelection.offset);
}
else {
// only start pos
selection.setPosition(startSelection.node, startSelection.offset);
// select tag by adding "select" class
if (this.StartPos.PosAtNode === XMLCursorPositions.CursorOnCompleteNode) {
/** @type {?} */
const asElem = /** @type {?} */ (this.StartPos.ActualNode);
if (asElem && asElem.classList) {
asElem.classList.add('selected');
console.log('selected!');
}
else {
console.error(`no asElem.classList for select! ${XmlToolbox.GetNodeDebugContext(asElem)}`);
}
}
}
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class XmlCursorPosSetter {
/**
* @param {?} pos
* @param {?} selectionAnchorNode
* @param {?} selectionAnchorOffset
* @param {?=} debugLog
* @return {?}
*/
static setFromSelection(pos, selectionAnchorNode, selectionAnchorOffset, debugLog = null) {
if (selectionAnchorNode !== undefined && selectionAnchorNode !== null) {
/** @type {?} */
let node = /** @type {?} */ (selectionAnchorNode);
if (DomDummyNodeManager.IsDummyNode(selectionAnchorNode.parentElement)) {
// clicked inside a dummy node
node = /** @type {?} */ (selectionAnchorNode.parentElement);
if (DomDummyNodeManager.IsBeforeFirstTagDummyNode(node)) {
switch (selectionAnchorOffset) {
case 0:
if (debugLog) {
debugLog.setInputSelection(`(1) cursor here: #<tag> pos 0`);
}
pos.SetCursor(node.parentElement, XMLCursorPositions.CursorOnCompleteNode, 0);
return;
case 1:
if (debugLog) {
debugLog.setInputSelection(`(2) cursor here: #<tag> pos 1`);
}
pos.SetCursor(node.parentElement, XMLCursorPositions.CursorInFrontOfNode, 0);
return;
default:
throw new Error(`selectionAnchorOffset=${selectionAnchorOffset}?!?`);
}
}
if (node.parentElement.childNodes.length === 2) {
if (debugLog) {
debugLog.setInputSelection(`(13, 14) cursor here: <tag>#</tag>`);
}
pos.SetCursor(node.parentElement, XMLCursorPositions.CursorInEmptyNode, 0);
return;
}
if (node === node.parentElement.lastChild && selectionAnchorOffset === 1) {
if (debugLog) {
debugLog.setInputSelection(`(8, 12) cursor here: #</tag>, selecting complete node`);
}
pos.SetCursor(node.parentElement, XMLCursorPositions.CursorOnCompleteNode, 0);
return;
}
/** @type {?} */
const nextSibling = node.nextSibling;
if (nextSibling) {
if (debugLog) {
debugLog.setInputSelection(`(3, 4, 9, 10) cursor here: #Text or here #<tag>`);
}
pos.SetCursor(nextSibling, XMLCursorPositions.CursorInFrontOfNode, 0);
return;
}
/** @type {?} */
const previousSibling = node.previousSibling;
if (previousSibling) {
if (debugLog) {
debugLog.setInputSelection(`(7, 11) cursor here: Text# or here <tag>#`);
}
pos.SetCursor(previousSibling, XMLCursorPositions.CursorBehindNode, 0);
return;
}
}
else {
// is no dummy node
if (selectionAnchorNode.nodeType === Node.TEXT_NODE) {
if (selectionAnchorOffset === 0) {
if (debugLog) {
debugLog.setInputSelection(`(6) cursor here: #Text`);
}
pos.SetCursor(selectionAnchorNode, XMLCursorPositions.CursorInsideTextNode, 0);
return;
}
if (selectionAnchorOffset === selectionAnchorNode.textContent.length) {
if (debugLog) {
debugLog.setInputSelection(`(7 Alternative) cursor here: Text#`);
}
pos.SetCursor(selectionAnchorNode, XMLCursorPositions.CursorInsideTextNode, selectionAnchorNode.textContent.length);
return;
}
if (debugLog) {
debugLog.setInputSelection(`(5) cursor here: Text#Text`);
}
if (selectionAnchorOffset > selectionAnchorNode.textContent.length) {