@tradik/xslt-processor 1.0.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +110 -520
- package/bin/lib/decode.js +15 -0
- package/bin/lib/dom.js +177 -0
- package/bin/lib/loaders.js +127 -0
- package/bin/lib/options.js +131 -0
- package/bin/lib/output.js +114 -0
- package/bin/lib/paths.js +186 -0
- package/bin/lib/transform.js +206 -0
- package/bin/xslt.js +73 -168
- package/dist/xslt-processor.browser.js +9564 -1585
- package/dist/xslt-processor.browser.js.map +4 -4
- package/dist/xslt-processor.browser.min.js +13 -2
- package/dist/xslt-processor.browser.min.js.map +4 -4
- package/dist/xslt-processor.cjs +9572 -1586
- package/dist/xslt-processor.cjs.map +4 -4
- package/dist/xslt-processor.d.cts +658 -0
- package/dist/xslt-processor.d.ts +459 -12
- package/dist/xslt-processor.js +9546 -1582
- package/dist/xslt-processor.js.map +4 -4
- package/package.json +71 -20
- package/src/XSLTProcessor.js +494 -48
- package/src/async/abort.js +63 -0
- package/src/async/documentUris.js +128 -0
- package/src/async/loaders.js +134 -0
- package/src/async/preload.js +159 -0
- package/src/async/processor.js +206 -0
- package/src/async/stream.js +125 -0
- package/src/bridge/engine.js +221 -0
- package/src/bridge/loader.js +78 -0
- package/src/bridge/results.js +75 -0
- package/src/bridge/version.js +63 -0
- package/src/index.js +26 -8
- package/src/io/decode.js +140 -0
- package/src/io/readSource.js +167 -0
- package/src/xpath/axes.js +562 -0
- package/src/xpath/documentOrder.js +270 -0
- package/src/xpath/evaluator.js +518 -357
- package/src/xpath/index.js +8 -2
- package/src/xpath/namespaceNodes.js +172 -0
- package/src/xpath/nodeSetFunctions.js +169 -0
- package/src/xpath/parser.js +30 -5
- package/src/xpath/strings.js +183 -0
- package/src/xpath/tokenizer.js +37 -23
- package/src/xslt/attributeSets.js +95 -0
- package/src/xslt/avt.js +103 -0
- package/src/xslt/computedNames.js +91 -0
- package/src/xslt/copying.js +212 -0
- package/src/xslt/declarationNames.js +80 -0
- package/src/xslt/domParsing.js +95 -0
- package/src/xslt/elements.js +57 -0
- package/src/xslt/engine/bindings.js +195 -0
- package/src/xslt/engine/context.js +105 -0
- package/src/xslt/engine/controlFlow.js +145 -0
- package/src/xslt/engine/copyInstructions.js +133 -0
- package/src/xslt/engine/declarations.js +233 -0
- package/src/xslt/engine/functionSupport.js +103 -0
- package/src/xslt/engine/methods.js +33 -0
- package/src/xslt/engine/nodeConstruction.js +187 -0
- package/src/xslt/engine/numbering.js +104 -0
- package/src/xslt/engine/outputDeclaration.js +77 -0
- package/src/xslt/engine/sequenceConstructor.js +228 -0
- package/src/xslt/engine/stylesheetLoading.js +208 -0
- package/src/xslt/engine/templateInvocation.js +253 -0
- package/src/xslt/engine/templateRules.js +243 -0
- package/src/xslt/engine/textInstructions.js +171 -0
- package/src/xslt/engine/topLevel.js +130 -0
- package/src/xslt/engine/transformation.js +263 -0
- package/src/xslt/engine/workStack.js +245 -0
- package/src/xslt/engine.js +184 -1736
- package/src/xslt/exslt/arguments.js +99 -0
- package/src/xslt/exslt/calendar.js +120 -0
- package/src/xslt/exslt/common.js +44 -0
- package/src/xslt/exslt/dateCalc.js +261 -0
- package/src/xslt/exslt/dateFormat.js +150 -0
- package/src/xslt/exslt/dateParse.js +265 -0
- package/src/xslt/exslt/dates.js +259 -0
- package/src/xslt/exslt/duration.js +207 -0
- package/src/xslt/exslt/dynamic.js +59 -0
- package/src/xslt/exslt/index.js +59 -0
- package/src/xslt/exslt/math.js +177 -0
- package/src/xslt/exslt/sets.js +96 -0
- package/src/xslt/exslt/stringOps.js +163 -0
- package/src/xslt/exslt/strings.js +147 -0
- package/src/xslt/exslt/uri.js +92 -0
- package/src/xslt/formatNumber.js +233 -0
- package/src/xslt/forwardsCompatible.js +75 -0
- package/src/xslt/functions.js +270 -0
- package/src/xslt/index.js +38 -1
- package/src/xslt/keys.js +164 -0
- package/src/xslt/literalResult.js +223 -0
- package/src/xslt/matchScope.js +116 -0
- package/src/xslt/number.js +271 -0
- package/src/xslt/numberFormat.js +253 -0
- package/src/xslt/outputNames.js +58 -0
- package/src/xslt/patternCompiler.js +175 -0
- package/src/xslt/patterns.js +324 -0
- package/src/xslt/qname.js +90 -0
- package/src/xslt/resultDocument.js +98 -0
- package/src/xslt/resultNamespaces.js +219 -0
- package/src/xslt/resultTree.js +211 -0
- package/src/xslt/serializer/baseWriter.js +390 -0
- package/src/xslt/serializer/chunks.js +120 -0
- package/src/xslt/serializer/constants.js +92 -0
- package/src/xslt/serializer/encoding.js +327 -0
- package/src/xslt/serializer/escape.js +135 -0
- package/src/xslt/serializer/frames.js +168 -0
- package/src/xslt/serializer/htmlDoctype.js +102 -0
- package/src/xslt/serializer/htmlEntities.js +77 -0
- package/src/xslt/serializer/htmlSerializer.js +239 -0
- package/src/xslt/serializer/indent.js +51 -0
- package/src/xslt/serializer/namespaces.js +68 -0
- package/src/xslt/serializer/rawText.js +41 -0
- package/src/xslt/serializer/settings.js +179 -0
- package/src/xslt/serializer/textSerializer.js +77 -0
- package/src/xslt/serializer/xhtmlDocument.js +103 -0
- package/src/xslt/serializer/xmlSerializer.js +227 -0
- package/src/xslt/serializer.js +90 -0
- package/src/xslt/sort.js +151 -0
- package/src/xslt/spaceNameTests.js +115 -0
- package/src/xslt/stylesheetChecks.js +206 -0
- package/src/xslt/stylesheetNamespaces.js +266 -0
- package/src/xslt/templatePriority.js +45 -0
- package/src/xslt/uri.js +68 -0
- package/src/xslt/variables.js +152 -0
- package/src/xslt/whitespace.js +200 -0
- package/LICENSE +0 -29
- package/src/XSLTProcessor.test.js +0 -930
- package/src/xpath/evaluator.test.js +0 -1852
- package/src/xpath/tokenizer.test.js +0 -224
- package/src/xslt/engine.test.js +0 -3130
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document type declaration of the html output method (XSLT 1.0 section
|
|
3
|
+
* 16.2), as libxslt and libxml2 (and so Chrome) write it.
|
|
4
|
+
*
|
|
5
|
+
* Without `doctype-public` and `doctype-system`, libxslt derives the
|
|
6
|
+
* identifiers from the `version` attribute of `xsl:output` (its table
|
|
7
|
+
* below): `version="5"` gives `<!DOCTYPE html>`, `version="4.01"` the HTML
|
|
8
|
+
* 4.01 Transitional DTD. The system identifier `about:legacy-compat` is not
|
|
9
|
+
* written (HTML5's legacy doctype string).
|
|
10
|
+
*
|
|
11
|
+
* @module xslt/serializer/htmlDoctype
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Doctype identifiers by html `version`, libxslt's xsltHTMLVersions. */
|
|
15
|
+
const HTML_VERSIONS = new Map([
|
|
16
|
+
["5", [null, "about:legacy-compat"]],
|
|
17
|
+
[
|
|
18
|
+
"4.01frame",
|
|
19
|
+
[
|
|
20
|
+
"-//W3C//DTD HTML 4.01 Frameset//EN",
|
|
21
|
+
"http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd",
|
|
22
|
+
],
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"4.01strict",
|
|
26
|
+
[
|
|
27
|
+
"-//W3C//DTD HTML 4.01//EN",
|
|
28
|
+
"http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd",
|
|
29
|
+
],
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
"4.01trans",
|
|
33
|
+
[
|
|
34
|
+
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
|
35
|
+
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd",
|
|
36
|
+
],
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"4.01",
|
|
40
|
+
[
|
|
41
|
+
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
|
42
|
+
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd",
|
|
43
|
+
],
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"4.0strict",
|
|
47
|
+
["-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd"],
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
"4.0trans",
|
|
51
|
+
[
|
|
52
|
+
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
|
53
|
+
"http://www.w3.org/TR/html4/loose.dtd",
|
|
54
|
+
],
|
|
55
|
+
],
|
|
56
|
+
[
|
|
57
|
+
"4.0frame",
|
|
58
|
+
[
|
|
59
|
+
"-//W3C//DTD HTML 4.01 Frameset//EN",
|
|
60
|
+
"http://www.w3.org/TR/html4/frameset.dtd",
|
|
61
|
+
],
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"4.0",
|
|
65
|
+
[
|
|
66
|
+
"-//W3C//DTD HTML 4.01 Transitional//EN",
|
|
67
|
+
"http://www.w3.org/TR/html4/loose.dtd",
|
|
68
|
+
],
|
|
69
|
+
],
|
|
70
|
+
["3.2", ["-//W3C//DTD HTML 3.2//EN", null]],
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
/** System identifier libxml2 leaves out of an HTML doctype. */
|
|
74
|
+
const LEGACY_COMPAT = "about:legacy-compat";
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build the doctype of html output.
|
|
78
|
+
*
|
|
79
|
+
* @param {{doctypePublic?: string|null, doctypeSystem?: string|null,
|
|
80
|
+
* version?: string|null}} settings - Normalized output settings
|
|
81
|
+
* @param {string} name - Name of the document element
|
|
82
|
+
* @returns {string} The doctype markup, or "" when there is none
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* htmlDoctypeMarkup({ version: "5" }, "html"); // "<!DOCTYPE html>"
|
|
86
|
+
*/
|
|
87
|
+
export function htmlDoctypeMarkup(settings, name) {
|
|
88
|
+
let publicId = settings.doctypePublic || null;
|
|
89
|
+
let systemId = settings.doctypeSystem || null;
|
|
90
|
+
if (!publicId && !systemId) {
|
|
91
|
+
const ids = HTML_VERSIONS.get(String(settings.version).toLowerCase());
|
|
92
|
+
if (!ids) return "";
|
|
93
|
+
[publicId, systemId] = ids;
|
|
94
|
+
}
|
|
95
|
+
if (publicId) {
|
|
96
|
+
const system = systemId ? ` "${systemId}"` : "";
|
|
97
|
+
return `<!DOCTYPE ${name} PUBLIC "${publicId}"${system}>`;
|
|
98
|
+
}
|
|
99
|
+
return systemId === LEGACY_COMPAT
|
|
100
|
+
? `<!DOCTYPE ${name}>`
|
|
101
|
+
: `<!DOCTYPE ${name} SYSTEM "${systemId}">`;
|
|
102
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML 4.01 Character Entity References
|
|
3
|
+
*
|
|
4
|
+
* The html output method writes a character the output encoding cannot
|
|
5
|
+
* represent as an entity reference when HTML 4.01 defines one (`€` in
|
|
6
|
+
* ISO-8859-1 output) and as a numeric character reference otherwise, as
|
|
7
|
+
* libxslt does (XSLT 1.0 section 16.2).
|
|
8
|
+
*
|
|
9
|
+
* @module xslt/serializer/htmlEntities
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { characterReference } from "./encoding.js";
|
|
13
|
+
|
|
14
|
+
/** Entity names of U+00A0 to U+00FF, in code point order. */
|
|
15
|
+
const LATIN1_NAMES =
|
|
16
|
+
"nbsp iexcl cent pound curren yen brvbar sect uml copy ordf laquo not shy " +
|
|
17
|
+
"reg macr deg plusmn sup2 sup3 acute micro para middot cedil sup1 ordm " +
|
|
18
|
+
"raquo frac14 frac12 frac34 iquest Agrave Aacute Acirc Atilde Auml Aring " +
|
|
19
|
+
"AElig Ccedil Egrave Eacute Ecirc Euml Igrave Iacute Icirc Iuml ETH Ntilde " +
|
|
20
|
+
"Ograve Oacute Ocirc Otilde Ouml times Oslash Ugrave Uacute Ucirc Uuml " +
|
|
21
|
+
"Yacute THORN szlig agrave aacute acirc atilde auml aring aelig ccedil " +
|
|
22
|
+
"egrave eacute ecirc euml igrave iacute icirc iuml eth ntilde ograve " +
|
|
23
|
+
"oacute ocirc otilde ouml divide oslash ugrave uacute ucirc uuml yacute " +
|
|
24
|
+
"thorn yuml";
|
|
25
|
+
|
|
26
|
+
/** The other entities, as `name codePoint` pairs. */
|
|
27
|
+
const OTHER_ENTITIES =
|
|
28
|
+
"OElig 338 oelig 339 Scaron 352 scaron 353 Yuml 376 fnof 402 circ 710 " +
|
|
29
|
+
"tilde 732 Alpha 913 Beta 914 Gamma 915 Delta 916 Epsilon 917 Zeta 918 " +
|
|
30
|
+
"Eta 919 Theta 920 Iota 921 Kappa 922 Lambda 923 Mu 924 Nu 925 Xi 926 " +
|
|
31
|
+
"Omicron 927 Pi 928 Rho 929 Sigma 931 Tau 932 Upsilon 933 Phi 934 Chi 935 " +
|
|
32
|
+
"Psi 936 Omega 937 alpha 945 beta 946 gamma 947 delta 948 epsilon 949 " +
|
|
33
|
+
"zeta 950 eta 951 theta 952 iota 953 kappa 954 lambda 955 mu 956 nu 957 " +
|
|
34
|
+
"xi 958 omicron 959 pi 960 rho 961 sigmaf 962 sigma 963 tau 964 " +
|
|
35
|
+
"upsilon 965 phi 966 chi 967 psi 968 omega 969 thetasym 977 upsih 978 " +
|
|
36
|
+
"piv 982 ensp 8194 emsp 8195 thinsp 8201 zwnj 8204 zwj 8205 lrm 8206 " +
|
|
37
|
+
"rlm 8207 ndash 8211 mdash 8212 lsquo 8216 rsquo 8217 sbquo 8218 " +
|
|
38
|
+
"ldquo 8220 rdquo 8221 bdquo 8222 dagger 8224 Dagger 8225 bull 8226 " +
|
|
39
|
+
"hellip 8230 permil 8240 prime 8242 Prime 8243 lsaquo 8249 rsaquo 8250 " +
|
|
40
|
+
"oline 8254 frasl 8260 euro 8364 image 8465 weierp 8472 real 8476 " +
|
|
41
|
+
"trade 8482 alefsym 8501 larr 8592 uarr 8593 rarr 8594 darr 8595 " +
|
|
42
|
+
"harr 8596 crarr 8629 lArr 8656 uArr 8657 rArr 8658 dArr 8659 hArr 8660 " +
|
|
43
|
+
"forall 8704 part 8706 exist 8707 empty 8709 nabla 8711 isin 8712 " +
|
|
44
|
+
"notin 8713 ni 8715 prod 8719 sum 8721 minus 8722 lowast 8727 radic 8730 " +
|
|
45
|
+
"prop 8733 infin 8734 ang 8736 and 8743 or 8744 cap 8745 cup 8746 " +
|
|
46
|
+
"int 8747 there4 8756 sim 8764 cong 8773 asymp 8776 ne 8800 equiv 8801 " +
|
|
47
|
+
"le 8804 ge 8805 sub 8834 sup 8835 nsub 8836 sube 8838 supe 8839 " +
|
|
48
|
+
"oplus 8853 otimes 8855 perp 8869 sdot 8901 lceil 8968 rceil 8969 " +
|
|
49
|
+
"lfloor 8970 rfloor 8971 lang 9001 rang 9002 loz 9674 spades 9824 " +
|
|
50
|
+
"clubs 9827 hearts 9829 diams 9830";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Entity name by code point.
|
|
54
|
+
* @type {Map<number, string>}
|
|
55
|
+
*/
|
|
56
|
+
const ENTITY_NAMES = new Map(
|
|
57
|
+
LATIN1_NAMES.split(" ").map((name, index) => [0xa0 + index, name]),
|
|
58
|
+
);
|
|
59
|
+
const otherParts = OTHER_ENTITIES.split(" ");
|
|
60
|
+
for (let i = 0; i < otherParts.length; i += 2) {
|
|
61
|
+
ENTITY_NAMES.set(Number(otherParts[i + 1]), otherParts[i]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reference to a character the output encoding cannot represent: the HTML
|
|
66
|
+
* 4.01 entity reference when there is one, a numeric reference otherwise.
|
|
67
|
+
*
|
|
68
|
+
* @param {number} codePoint - The code point
|
|
69
|
+
* @returns {string} `&name;` or `&#N;`
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* htmlCharacterReference(0x20ac); // "€"
|
|
73
|
+
*/
|
|
74
|
+
export function htmlCharacterReference(codePoint) {
|
|
75
|
+
const name = ENTITY_NAMES.get(codePoint);
|
|
76
|
+
return name ? `&${name};` : characterReference(codePoint);
|
|
77
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML Output Serializer
|
|
3
|
+
*
|
|
4
|
+
* Implements the `html` output method of XSLT 1.0 section 16.2 on top of the
|
|
5
|
+
* XML writer: no XML declaration, void elements without a closing slash,
|
|
6
|
+
* minimized boolean attributes and unescaped script/style content. Like
|
|
7
|
+
* libxml2 (and so Chrome), namespace declarations of the result tree are
|
|
8
|
+
* written, and an element in a namespace other than XHTML is not an HTML
|
|
9
|
+
* element (`<s:img></s:img>`). Unlike libxml2, XHTML elements keep the HTML
|
|
10
|
+
* rules, so `<br/>` in the XHTML namespace is not written as `<br></br>`
|
|
11
|
+
* (which HTML parsers read as two line breaks).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
NODE_TYPE,
|
|
16
|
+
PRESERVE_SPACE_ELEMENTS,
|
|
17
|
+
RAW_TEXT_ELEMENTS,
|
|
18
|
+
TEXT_MODE,
|
|
19
|
+
URI_ATTRIBUTES,
|
|
20
|
+
URI_ATTRIBUTES_OF_A,
|
|
21
|
+
XHTML_NAMESPACE,
|
|
22
|
+
} from "./constants.js";
|
|
23
|
+
import {
|
|
24
|
+
escapeHtmlAttribute,
|
|
25
|
+
escapeHtmlText,
|
|
26
|
+
escapeUriAttribute,
|
|
27
|
+
} from "./escape.js";
|
|
28
|
+
import { htmlCharacterReference } from "./htmlEntities.js";
|
|
29
|
+
import { htmlDoctypeMarkup } from "./htmlDoctype.js";
|
|
30
|
+
import { XmlWriter } from "./xmlSerializer.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether a `head` element already declares the content type or character
|
|
34
|
+
* set: a `meta` child with `http-equiv="Content-Type"` or a `charset`
|
|
35
|
+
* attribute (names and the http-equiv value compared case-insensitively).
|
|
36
|
+
*
|
|
37
|
+
* @param {Element} head - The head element
|
|
38
|
+
* @returns {boolean} True when no meta element has to be added
|
|
39
|
+
*/
|
|
40
|
+
function hasContentTypeMeta(head) {
|
|
41
|
+
for (const child of head.childNodes) {
|
|
42
|
+
if (
|
|
43
|
+
child.nodeType !== NODE_TYPE.ELEMENT ||
|
|
44
|
+
child.localName.toLowerCase() !== "meta"
|
|
45
|
+
) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
for (const { name, value } of child.attributes) {
|
|
49
|
+
const lower = name.toLowerCase();
|
|
50
|
+
if (lower === "charset") return true;
|
|
51
|
+
if (lower === "http-equiv" && value.toLowerCase() === "content-type") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether the html output method %-escapes an attribute, as libxml2 does:
|
|
61
|
+
* `href`, `action` and `src`, and `name` on `a` (names compared
|
|
62
|
+
* case-insensitively), when neither the attribute nor its element is in a
|
|
63
|
+
* namespace.
|
|
64
|
+
*
|
|
65
|
+
* @param {Attr} attribute - Attribute being written
|
|
66
|
+
* @returns {boolean} True for URI attributes
|
|
67
|
+
*/
|
|
68
|
+
function isUriAttribute(attribute) {
|
|
69
|
+
const element = attribute.ownerElement;
|
|
70
|
+
if (attribute.namespaceURI || element.namespaceURI) return false;
|
|
71
|
+
const name = attribute.localName.toLowerCase();
|
|
72
|
+
return (
|
|
73
|
+
URI_ATTRIBUTES.has(name) ||
|
|
74
|
+
(URI_ATTRIBUTES_OF_A.has(name) && element.localName.toLowerCase() === "a")
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class HtmlWriter extends XmlWriter {
|
|
79
|
+
/**
|
|
80
|
+
* The html output method never writes an XML declaration.
|
|
81
|
+
* @returns {boolean} Always false
|
|
82
|
+
*/
|
|
83
|
+
get emitsXmlDeclaration() {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* No line breaks are added between the top-level nodes of html output.
|
|
89
|
+
* @returns {boolean} Always false
|
|
90
|
+
*/
|
|
91
|
+
get topLevelLineBreaks() {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* HTML output is never an XHTML document.
|
|
97
|
+
* @returns {boolean} Always false
|
|
98
|
+
*/
|
|
99
|
+
get isXhtmlDocument() {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* HTML processing instructions are terminated by `>` alone.
|
|
105
|
+
* @returns {string} The HTML processing instruction terminator
|
|
106
|
+
*/
|
|
107
|
+
get piTerminator() {
|
|
108
|
+
return ">";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* HTML has no CDATA sections, so such nodes are escaped as text.
|
|
113
|
+
* @returns {string} A {@link TEXT_MODE} value
|
|
114
|
+
*/
|
|
115
|
+
get cdataNodeMode() {
|
|
116
|
+
return TEXT_MODE.ESCAPE;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build the document type declaration for the html output method (see
|
|
121
|
+
* htmlDoctype.js: declared identifiers, else derived from `version`).
|
|
122
|
+
*
|
|
123
|
+
* @param {Element|null} rootElement - Result document element
|
|
124
|
+
* @returns {string} Doctype markup, or an empty string when not applicable
|
|
125
|
+
*/
|
|
126
|
+
doctypeMarkup(rootElement) {
|
|
127
|
+
return htmlDoctypeMarkup(
|
|
128
|
+
this.settings,
|
|
129
|
+
rootElement ? rootElement.nodeName : "html",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Script and style content is written verbatim.
|
|
135
|
+
*
|
|
136
|
+
* @param {Element} element - Parent element
|
|
137
|
+
* @returns {string} A {@link TEXT_MODE} value
|
|
138
|
+
*/
|
|
139
|
+
childTextMode(element) {
|
|
140
|
+
return RAW_TEXT_ELEMENTS.has(String(element.localName).toLowerCase())
|
|
141
|
+
? TEXT_MODE.RAW
|
|
142
|
+
: TEXT_MODE.ESCAPE;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Content of `pre`, `script`, `style` and `textarea` is never re-indented.
|
|
147
|
+
*
|
|
148
|
+
* @param {Element} element - Element being inspected
|
|
149
|
+
* @returns {boolean} True when the content may be indented
|
|
150
|
+
*/
|
|
151
|
+
allowsIndentInside(element) {
|
|
152
|
+
return !PRESERVE_SPACE_ELEMENTS.has(
|
|
153
|
+
String(element.localName).toLowerCase(),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Void elements have no end tag; every other element gets one.
|
|
159
|
+
*
|
|
160
|
+
* @param {Element} element - Empty element
|
|
161
|
+
* @param {string} name - Element name as written
|
|
162
|
+
* @returns {string} Markup terminating the start tag
|
|
163
|
+
*/
|
|
164
|
+
emptyElementMarkup(element, name) {
|
|
165
|
+
const namespace = element.namespaceURI;
|
|
166
|
+
const isHtml = !namespace || namespace === XHTML_NAMESPACE;
|
|
167
|
+
return isHtml && this.isVoidElement(element) ? ">" : `></${name}>`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Boolean attributes are minimized to their name alone; URI attributes
|
|
172
|
+
* are %-escaped (see isUriAttribute).
|
|
173
|
+
*
|
|
174
|
+
* @param {Attr} attribute - Attribute to write
|
|
175
|
+
* @returns {string} Attribute markup, starting with a space
|
|
176
|
+
*/
|
|
177
|
+
attributeMarkup(attribute) {
|
|
178
|
+
const { name, value } = attribute;
|
|
179
|
+
if (String(value).toLowerCase() === name.toLowerCase()) {
|
|
180
|
+
return ` ${name}`;
|
|
181
|
+
}
|
|
182
|
+
const written = isUriAttribute(attribute)
|
|
183
|
+
? escapeUriAttribute(value)
|
|
184
|
+
: value;
|
|
185
|
+
return ` ${name}="${this.escapeAttribute(written)}"`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The content type `meta` element libxslt (and so Chrome) writes as the
|
|
190
|
+
* first child of an HTML `head` element that does not already have one
|
|
191
|
+
* (XSLT 1.0 section 16.2 recommends it). Firefox does not add it.
|
|
192
|
+
*
|
|
193
|
+
* @param {Element} element - Element being written
|
|
194
|
+
* @returns {string} The meta element markup, or an empty string
|
|
195
|
+
*/
|
|
196
|
+
leadingChildMarkup(element) {
|
|
197
|
+
// libxml2 finds the head element by name, whatever its namespace
|
|
198
|
+
if (
|
|
199
|
+
element.localName.toLowerCase() !== "head" ||
|
|
200
|
+
hasContentTypeMeta(element)
|
|
201
|
+
) {
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
const { mediaType, encoding } = this.settings;
|
|
205
|
+
const content = `${mediaType || "text/html"}; charset=${encoding}`;
|
|
206
|
+
return `<meta http-equiv="Content-Type" content="${this.escapeAttribute(content)}">`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Escape character data for HTML.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} value - Text content
|
|
213
|
+
* @returns {string} Escaped text
|
|
214
|
+
*/
|
|
215
|
+
escapeText(value) {
|
|
216
|
+
return this.encodeReferences(escapeHtmlText(value));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Escape an attribute value for HTML.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} value - Attribute value
|
|
223
|
+
* @returns {string} Escaped value
|
|
224
|
+
*/
|
|
225
|
+
escapeAttribute(value) {
|
|
226
|
+
return this.encodeReferences(escapeHtmlAttribute(value));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Reference to a character the output encoding cannot represent: an HTML
|
|
231
|
+
* entity reference where HTML 4.01 has one, as libxslt writes.
|
|
232
|
+
*
|
|
233
|
+
* @param {number} codePoint - The code point
|
|
234
|
+
* @returns {string} An entity or numeric character reference
|
|
235
|
+
*/
|
|
236
|
+
characterReference(codePoint) {
|
|
237
|
+
return htmlCharacterReference(codePoint);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indentation Rules
|
|
3
|
+
*
|
|
4
|
+
* Decides which elements may be pretty printed when `indent="yes"` is set.
|
|
5
|
+
* Only element-only content is indented; mixed content is left untouched so
|
|
6
|
+
* that the transformation result stays character-for-character faithful.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { NODE_TYPE } from "./constants.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Test whether a character data node holds only whitespace.
|
|
13
|
+
*
|
|
14
|
+
* @param {Node} node - Text node to test
|
|
15
|
+
* @returns {boolean} True when the node contains no non-whitespace character
|
|
16
|
+
*/
|
|
17
|
+
export function isWhitespaceOnlyText(node) {
|
|
18
|
+
return !/\S/.test(node.nodeValue || "");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Collect the children to write when indenting an element.
|
|
23
|
+
*
|
|
24
|
+
* @param {Element} element - Element whose children are inspected
|
|
25
|
+
* @returns {Node[]|null} Children to indent, or null when the element must be
|
|
26
|
+
* serialized without any added whitespace
|
|
27
|
+
*/
|
|
28
|
+
export function getIndentableChildren(element) {
|
|
29
|
+
const indentable = [];
|
|
30
|
+
|
|
31
|
+
for (const child of element.childNodes) {
|
|
32
|
+
if (child.nodeType === NODE_TYPE.TEXT) {
|
|
33
|
+
if (isWhitespaceOnlyText(child)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
child.nodeType !== NODE_TYPE.ELEMENT &&
|
|
41
|
+
child.nodeType !== NODE_TYPE.COMMENT &&
|
|
42
|
+
child.nodeType !== NODE_TYPE.PROCESSING_INSTRUCTION
|
|
43
|
+
) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
indentable.push(child);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return indentable.length > 0 ? indentable : null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace Declaration Tracking
|
|
3
|
+
*
|
|
4
|
+
* Computes the `xmlns` declarations an element has to carry so that every
|
|
5
|
+
* namespace is declared where it is first used and never twice.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { XMLNS_NAMESPACE, XML_NAMESPACE } from "./constants.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create the namespace scope in effect above the result tree root.
|
|
12
|
+
*
|
|
13
|
+
* @returns {Map<string, string>} Prefix (empty string for the default) to URI
|
|
14
|
+
*/
|
|
15
|
+
export function createNamespaceScope() {
|
|
16
|
+
return new Map([
|
|
17
|
+
["", ""],
|
|
18
|
+
["xml", XML_NAMESPACE],
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Collect the namespace declarations an element must emit.
|
|
24
|
+
*
|
|
25
|
+
* @param {Element} element - Element being serialized
|
|
26
|
+
* @param {Map<string, string>} scope - Namespace scope inherited from the parent
|
|
27
|
+
* @returns {{declarations: Array<{prefix: string, uri: string}>, scope: Map<string, string>}}
|
|
28
|
+
* The declarations to write and the scope in effect for the children
|
|
29
|
+
*/
|
|
30
|
+
export function collectNamespaceDeclarations(element, scope) {
|
|
31
|
+
const declarations = [];
|
|
32
|
+
let next = scope;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Record a declaration when the binding is not already in scope.
|
|
36
|
+
* @param {string} prefix - Namespace prefix, empty for the default namespace
|
|
37
|
+
* @param {string} uri - Namespace URI
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
const declare = (prefix, uri) => {
|
|
41
|
+
if (next.get(prefix) === uri) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (next === scope) {
|
|
45
|
+
next = new Map(scope);
|
|
46
|
+
}
|
|
47
|
+
next.set(prefix, uri);
|
|
48
|
+
declarations.push({ prefix, uri });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
declare(element.prefix || "", element.namespaceURI || "");
|
|
52
|
+
|
|
53
|
+
const attributes = Array.from(element.attributes || []);
|
|
54
|
+
|
|
55
|
+
for (const attribute of attributes) {
|
|
56
|
+
if (attribute.namespaceURI !== XMLNS_NAMESPACE && attribute.prefix) {
|
|
57
|
+
declare(attribute.prefix, attribute.namespaceURI || "");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const attribute of attributes) {
|
|
62
|
+
if (attribute.namespaceURI === XMLNS_NAMESPACE) {
|
|
63
|
+
declare(attribute.prefix ? attribute.localName : "", attribute.value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { declarations, scope: next };
|
|
68
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw Text Registry
|
|
3
|
+
*
|
|
4
|
+
* Tracks the text nodes produced with `disable-output-escaping="yes"`
|
|
5
|
+
* (XSLT 1.0 section 16.4) so the serializers can emit them verbatim.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Text nodes whose content must be written without escaping.
|
|
10
|
+
* @type {WeakSet<Node>}
|
|
11
|
+
*/
|
|
12
|
+
export const rawTextNodes = new WeakSet();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Mark a text node as produced with `disable-output-escaping="yes"`.
|
|
16
|
+
*
|
|
17
|
+
* @param {Node|null} node - Text node to mark
|
|
18
|
+
* @returns {Node|null} The same node, for chaining
|
|
19
|
+
*/
|
|
20
|
+
export function markRawText(node) {
|
|
21
|
+
if (node) {
|
|
22
|
+
rawTextNodes.add(node);
|
|
23
|
+
}
|
|
24
|
+
return node;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check whether a node must be serialized without output escaping.
|
|
29
|
+
*
|
|
30
|
+
* Both the registry and the legacy `_disableOutputEscaping` flag set by the
|
|
31
|
+
* XSLT engine are honored so that either marking mechanism works.
|
|
32
|
+
*
|
|
33
|
+
* @param {Node|null} node - Node to test
|
|
34
|
+
* @returns {boolean} True when the node content must be emitted raw
|
|
35
|
+
*/
|
|
36
|
+
export function isRawText(node) {
|
|
37
|
+
if (!node) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return rawTextNodes.has(node) || node._disableOutputEscaping === true;
|
|
41
|
+
}
|