Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// Based on <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
// core.html#ID-1950641247>
// Based on <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247>
export const DOM_ELEMENT_NODE = 1;
export const DOM_ATTRIBUTE_NODE = 2;
export const DOM_TEXT_NODE = 3;
export const DOM_CDATA_SECTION_NODE = 4;
// const DOM_ENTITY_REFERENCE_NODE = 5;
// const DOM_ENTITY_NODE = 6;
export const DOM_ENTITY_REFERENCE_NODE = 5;
export const DOM_ENTITY_NODE = 6;
export const DOM_PROCESSING_INSTRUCTION_NODE = 7;
export const DOM_COMMENT_NODE = 8;
export const DOM_DOCUMENT_NODE = 9;
// const DOM_DOCUMENT_TYPE_NODE = 10;
export const DOM_DOCUMENT_TYPE_NODE = 10;
export const DOM_DOCUMENT_FRAGMENT_NODE = 11;
// const DOM_NOTATION_NODE = 12;
export const DOM_NOTATION_NODE = 12;
31 changes: 26 additions & 5 deletions src/dom/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function domCreateElement(doc: any, name: any) {
return doc.createElement(name);
}

export function domCreateCDATASection(doc: any, data: any) {
export function domCreateCDATASection(doc: XDocument, data: any) {
return doc.createCDATASection(data);
}

Expand All @@ -76,6 +76,10 @@ export function domCreateDocumentFragment(doc: XDocument): XNode {
return doc.createDocumentFragment();
}

export function domCreateDTDSection(doc: XDocument, data: any) {
return doc.createDTDSection(data);
}

// Traverses the element nodes in the DOM section underneath the given
// node and invokes the given callbacks as methods on every element
// node encountered. Function opt_pre is invoked before a node's
Expand Down Expand Up @@ -111,7 +115,7 @@ export function domTraverseElements(node: any, opt_pre: any, opt_post: any) {

// Parses the given XML string with our custom, JavaScript XML parser. Written
// by Steffen Meschkat (mesch@google.com).
export function xmlParse(xml: any) {
export function xmlParse(xml: string) {
const regex_empty = /\/$/;

let regex_tagname;
Expand Down Expand Up @@ -158,10 +162,11 @@ export function xmlParse(xml: any) {
if (text.charAt(0) == '/') {
stack.pop();
parent = stack[stack.length - 1];
} else if (text.charAt(0) == '?') {
} else if (text.charAt(0) === '?') {
// Ignore XML declaration and processing instructions
} else if (text.charAt(0) == '!') {
// Ignore malformed notation and comments
} else if (text.charAt(0) === '!') {
// Ignore comments
// console.log(`Ignored ${text}`);
} else {
const empty = text.match(regex_empty);
const tagname = regex_tagname.exec(text)[1];
Expand Down Expand Up @@ -219,6 +224,22 @@ export function xmlParse(xml: any) {
domAppendChild(parent, node);
i += endTagIndex + 11;
}
} else if (xml.slice(i + 1, i + 9) === '!DOCTYPE') {
let endTagIndex = xml.slice(i + 9).indexOf('>');
if (endTagIndex) {
const dtdValue = xml.slice(i + 9, i + endTagIndex + 9).trimStart();
// TODO: Not sure if this is a good solution.
// Trying to implement this: https://github.com/DesignLiquido/xslt-processor/issues/30
let node;
if (parent.nodeName === 'xsl:text') {
node = domCreateTextNode(xmldoc, `<!DOCTYPE ${dtdValue}>`);
} else {
node = domCreateDTDSection(xmldoc, dtdValue);
}

domAppendChild(parent, node);
i += endTagIndex + dtdValue.length + 5;
}
} else {
tag = true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/dom/xdocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DOM_COMMENT_NODE,
DOM_DOCUMENT_FRAGMENT_NODE,
DOM_DOCUMENT_NODE,
DOM_DOCUMENT_TYPE_NODE,
DOM_ELEMENT_NODE,
DOM_TEXT_NODE
} from '../constants';
Expand Down Expand Up @@ -66,4 +67,8 @@ export class XDocument extends XNode {
createCDATASection(data: any) {
return XNode.create(DOM_CDATA_SECTION_NODE, '#cdata-section', data, this);
}

createDTDSection(data: any) {
return XNode.create(DOM_DOCUMENT_TYPE_NODE, '#dtd-section', data, this);
}
}
5 changes: 4 additions & 1 deletion src/dom/xml-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ function xmlTransformedTextRecursive(node: XNode, buffer: any[], cdata: boolean)
const nodeValue = node.transformedNodeValue || node.nodeValue;
if (nodeType == DOM_TEXT_NODE) {
if (node.transformedNodeValue && node.transformedNodeValue.trim() !== '') {
buffer.push(xmlEscapeText(node.transformedNodeValue));
const finalText = node.escape ?
xmlEscapeText(node.transformedNodeValue) :
node.transformedNodeValue;
buffer.push(finalText);
}
} else if (nodeType == DOM_CDATA_SECTION_NODE) {
if (cdata) {
Expand Down
2 changes: 2 additions & 0 deletions src/dom/xnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class XNode {
transformedParentNode: XNode;

printed: boolean;
escape: boolean;

static _unusedXNodes: any[] = [];

Expand All @@ -52,6 +53,7 @@ export class XNode {
this.transformedAttributes = [];
this.transformedChildNodes = [];
this.printed = false;
this.escape = true;

this.init(type, name, opt_value, opt_owner, opt_namespace);
}
Expand Down
4 changes: 4 additions & 0 deletions src/xslt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ export class Xslt {
case 'text':
text = xmlValue(template);
node = domCreateTransformedTextNode(outputDocument, text);
const disableOutputEscaping = template.attributes.filter(a => a.nodeName === 'disable-output-escaping');
if (disableOutputEscaping.length > 0 && disableOutputEscaping[0].nodeValue === 'yes') {
node.escape = false;
}
output.appendTransformedChild(node);
break;
case 'value-of':
Expand Down
2 changes: 1 addition & 1 deletion tests/escape.test.tsx → tests/xml/escape.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'assert';

import { xmlParse, xmlText } from '../src/dom';
import { xmlParse, xmlText } from '../../src/dom';

describe('escape', () => {
it('accepts already escaped ampersand', () => {
Expand Down
4 changes: 2 additions & 2 deletions tests/xml-to-json.test.tsx → tests/xml/xml-to-json.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable no-useless-escape */
import assert from 'assert';

import { Xslt } from '../src/xslt';
import { xmlParse } from '../src/dom';
import { Xslt } from '../../src/xslt';
import { xmlParse } from '../../src/dom';

describe('xml-to-json', () => {
it('xml-to-json() without namespace test', () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/xmltoken.test.tsx → tests/xml/xmltoken.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
XML11_ENTITY_REF,
XML11_ATT_VALUE,
XML_NC_NAME
} from '../src/dom/xmltoken';
} from '../../src/dom/xmltoken';

// Test if regexp matches the str and RegExp.exec returns exactly the match.
const assertOk = (comment: any, regexp: any, str: any, match: any) => {
Expand Down
122 changes: 71 additions & 51 deletions tests/xslt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,61 +28,81 @@ const xmlString = (
);

describe('xslt', () => {
it('handles for-each sort', () => {
const xsltForEachSort = (
<xsl:stylesheet>
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="@pos" />
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSort);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'CAB');
});

it('handles for-each sort ascending', () => {
const xsltForEachSortAscending = (
<xsl:stylesheet>
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="." order="ascending" />
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSortAscending);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'ABC');
describe('xsl:for-each', () => {
it('handles for-each sort', () => {
const xsltForEachSort = (
<xsl:stylesheet>
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="@pos" />
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSort);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'CAB');
});

it('handles for-each sort ascending', () => {
const xsltForEachSortAscending = (
<xsl:stylesheet>
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="." order="ascending" />
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSortAscending);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'ABC');
});

it('handles for-each sort descending', () => {
const xsltForEachSortDescending = (
<xsl:stylesheet>
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="." order="descending" />
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSortDescending);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'CBA');
});
});

it('handles for-each sort descending', () => {
const xsltForEachSortDescending = (
<xsl:stylesheet>
describe('xsl:text', () => {
it('disable-output-escaping', () => {
const xml = <anything></anything>;
const xslt = <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:for-each select="//item">
<xsl:sort select="." order="descending" />
<xsl:value-of select="." />
</xsl:for-each>
<xsl:text disable-output-escaping="yes">&lt;!DOCTYPE html&gt;</xsl:text>
</xsl:template>
</xsl:stylesheet>
);

const xsltClass = new Xslt();
const xml = xmlParse(xmlString);
const xslt = xmlParse(xsltForEachSortDescending);
const html = xsltClass.xsltProcess(xml, xslt);
assert.equal(html, 'CBA');
</xsl:stylesheet>;

const xsltClass = new Xslt();
const parsedXml = xmlParse(xml);
const parsedXslt = xmlParse(xslt);
const html = xsltClass.xsltProcess(parsedXml, parsedXslt);
assert.equal(html, '<!DOCTYPE html>');
});
});

it('applies templates', () => {
Expand Down