diff --git a/ts/adaptors/HTMLAdaptor.ts b/ts/adaptors/HTMLAdaptor.ts index d2d0f6cb6..d6e8794b3 100644 --- a/ts/adaptors/HTMLAdaptor.ts +++ b/ts/adaptors/HTMLAdaptor.ts @@ -113,13 +113,22 @@ export interface MinText { /** * The minimum fields needed for a DOMParser * - * @template N The HTMLElement node class - * @template T The Text node class + * @template D The Document class */ export interface MinDOMParser { parseFromString(text: string, format?: string): D; } +/*****************************************************************/ +/** + * The minimum fields needed for a DOMParser + * + * @template N The HTMLElement node class + */ +export interface MinXMLSerializer { + serializeToString(node: N): string; +} + /*****************************************************************/ /** * The minimum fields needed for a Window @@ -132,6 +141,9 @@ export interface MinWindow { DOMParser: { new(): MinDOMParser }; + XMLSerializer: { + new(): MinXMLSerializer; + }; NodeList: any; HTMLCollection: any; HTMLElement: any; @@ -399,6 +411,11 @@ AbstractDOMAdaptor implements MinHTMLAdaptor { return node.outerHTML; } + public serializeXML(node: N) { + const serializer = new this.window.XMLSerializer(); + return serializer.serializeToString(node) as string; + } + /** * @override */ diff --git a/ts/adaptors/browserAdaptor.ts b/ts/adaptors/browserAdaptor.ts index 3be113bb1..b86a914a9 100644 --- a/ts/adaptors/browserAdaptor.ts +++ b/ts/adaptors/browserAdaptor.ts @@ -30,6 +30,7 @@ declare global { interface Window { Document: typeof Document; DOMParser: typeof DOMParser; + XMLSerializer: typeof XMLSerializer; HTMLElement: typeof HTMLElement; HTMLCollection: typeof HTMLCollection; NodeList: typeof NodeList; diff --git a/ts/adaptors/lite/Parser.ts b/ts/adaptors/lite/Parser.ts index 90ac57310..e47412f85 100644 --- a/ts/adaptors/lite/Parser.ts +++ b/ts/adaptors/lite/Parser.ts @@ -345,18 +345,20 @@ export class LiteParser implements MinDOMParser { /** * @param {LiteAdaptor} adaptor The adaptor for managing nodes * @param {LiteElement} node The node to serialize + * @param {boolean} xml True when producing XML, false for HTML * @return {string} The serialized element (like outerHTML) */ - public serialize(adaptor: LiteAdaptor, node: LiteElement): string { + public serialize(adaptor: LiteAdaptor, node: LiteElement, xml: boolean = false): string { const SELF_CLOSING = (this.constructor as typeof LiteParser).SELF_CLOSING; const CDATA = (this.constructor as typeof LiteParser).CDATA_ATTR; const tag = adaptor.kind(node); const attributes = adaptor.allAttributes(node).map( (x: AttributeData) => x.name + '="' + (CDATA[x.name] ? x.value : this.protectAttribute(x.value)) + '"' ).join(' '); + const content = this.serializeInner(adaptor, node, xml); const html = - '<' + tag + (attributes ? ' ' + attributes : '') + '>' - + (SELF_CLOSING[tag] ? '' : adaptor.innerHTML(node) + ''); + '<' + tag + (attributes ? ' ' + attributes : '') + + (content && !SELF_CLOSING[tag] ? `>${content}` : xml ? '/>' : '>'); return html; } @@ -365,7 +367,7 @@ export class LiteParser implements MinDOMParser { * @param {LiteElement} node The node whose innerHTML is needed * @return {string} The serialized element (like innerHTML) */ - public serializeInner(adaptor: LiteAdaptor, node: LiteElement): string { + public serializeInner(adaptor: LiteAdaptor, node: LiteElement, xml: boolean = false): string { const PCDATA = (this.constructor as typeof LiteParser).PCDATA; if (PCDATA.hasOwnProperty(node.kind)) { return adaptor.childNodes(node).map(x => adaptor.value(x)).join(''); @@ -374,7 +376,7 @@ export class LiteParser implements MinDOMParser { const kind = adaptor.kind(x); return (kind === '#text' ? this.protectHTML(adaptor.value(x)) : kind === '#comment' ? (x as LiteComment).value : - this.serialize(adaptor, x as LiteElement)); + this.serialize(adaptor, x as LiteElement, xml)); }).join(''); } diff --git a/ts/adaptors/liteAdaptor.ts b/ts/adaptors/liteAdaptor.ts index ed02de3af..ed828f1ff 100644 --- a/ts/adaptors/liteAdaptor.ts +++ b/ts/adaptors/liteAdaptor.ts @@ -463,6 +463,13 @@ export class LiteAdaptor extends AbstractDOMAdaptor { */ outerHTML(node: N): string; + /** + * @param {N} node The HTML node whose serialized string is to be obtained + * @return {string} The serialized node and its content + */ + serializeXML(node: N): string; + /** * @param {N} node The HTML node whose attribute is to be set * @param {string|number} name The name of the attribute to set @@ -558,6 +564,11 @@ export abstract class AbstractDOMAdaptor implements DOMAdaptor */ public abstract outerHTML(node: N): string; + /** + * @override + */ + public abstract serializeXML(node: N): string; + /** * @override */ diff --git a/ts/core/MmlTree/MmlNode.ts b/ts/core/MmlTree/MmlNode.ts index 46d88963c..8bdda1168 100644 --- a/ts/core/MmlTree/MmlNode.ts +++ b/ts/core/MmlTree/MmlNode.ts @@ -1231,7 +1231,7 @@ export class XMLNode extends AbstractMmlEmptyNode { * @return {string} The serialized XML content */ public getSerializedXML(): string { - return this.adaptor.outerHTML(this.xml); + return this.adaptor.serializeXML(this.xml); } /**