@tradik/xslt3 0.0.0-stage → 1.0.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 +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers of the markup writer about content: the children of an element
|
|
3
|
+
* as written, whitespace, namespace URIs in declarations, and the check
|
|
4
|
+
* that a document type or standalone declaration has a document to go
|
|
5
|
+
* with.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/serialize/markup/content
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../../errors.js";
|
|
11
|
+
import { isContentTypeMeta } from "./prolog.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} text
|
|
15
|
+
* @returns {boolean} whether the text is XML whitespace only
|
|
16
|
+
*/
|
|
17
|
+
export const isWhitespace = (text) => /^[ \t\r\n]*$/.test(text);
|
|
18
|
+
|
|
19
|
+
const URI_ESCAPES = { "&": "&", "<": "<", '"': """ };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} uri - A namespace URI
|
|
23
|
+
* @returns {string} the URI escaped for an attribute value
|
|
24
|
+
*/
|
|
25
|
+
export const escapeNamespaceUri = (uri) =>
|
|
26
|
+
uri.replace(/[&<"]/g, (c) => URI_ESCAPES[c]);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The children of an element as written: adjacent character data merged
|
|
30
|
+
* into strings, other nodes kept.
|
|
31
|
+
* @param {Element} element
|
|
32
|
+
* @param {boolean} dropMeta - Leave out content type meta elements
|
|
33
|
+
* @returns {Array<string|Node>}
|
|
34
|
+
*/
|
|
35
|
+
export function childList(element, dropMeta) {
|
|
36
|
+
const list = [];
|
|
37
|
+
for (let child = element.firstChild; child; child = child.nextSibling) {
|
|
38
|
+
const type = child.nodeType;
|
|
39
|
+
if (type === 3 || type === 4) {
|
|
40
|
+
const last = list.length - 1;
|
|
41
|
+
if (typeof list[last] === "string") list[last] += child.nodeValue;
|
|
42
|
+
else if (child.nodeValue) list.push(child.nodeValue);
|
|
43
|
+
} else if (type === 1 || type === 7 || type === 8) {
|
|
44
|
+
if (!(dropMeta && isContentTypeMeta(child))) list.push(child);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return list;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A document type declaration or a standalone declaration needs a
|
|
52
|
+
* well-formed document: one element and no text at the top level.
|
|
53
|
+
* @param {Array<string|Node>} entries
|
|
54
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
55
|
+
* @throws {XPathError} SEPM0004 when the content is not a document
|
|
56
|
+
*/
|
|
57
|
+
export function checkDocument(entries, settings) {
|
|
58
|
+
const { method, doctypeSystem, standalone } = settings;
|
|
59
|
+
if (
|
|
60
|
+
method === "html" ||
|
|
61
|
+
(doctypeSystem === undefined && standalone === "omit")
|
|
62
|
+
) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const elements = entries.filter(
|
|
66
|
+
(e) => typeof e !== "string" && e.nodeType === 1,
|
|
67
|
+
);
|
|
68
|
+
if (elements.length > 1 || entries.some((e) => typeof e === "string")) {
|
|
69
|
+
throw new XPathError(
|
|
70
|
+
"SEPM0004",
|
|
71
|
+
"doctype-system and standalone need a single element and no text at the top level",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The differences between the xml, xhtml and html output methods
|
|
3
|
+
* (Serialization 3.1 sections 5, 6 and 7) for the markup writer: which
|
|
4
|
+
* elements are HTML elements, how empty elements, attributes and
|
|
5
|
+
* processing instructions are written, where CDATA sections, raw text and
|
|
6
|
+
* indentation are allowed.
|
|
7
|
+
*
|
|
8
|
+
* HTML elements are, for the html method, those in no namespace (and with
|
|
9
|
+
* HTML5 those in the XHTML namespace); for the xhtml method, those in the
|
|
10
|
+
* XHTML namespace (and with HTML5 those in no namespace). HTML5 output
|
|
11
|
+
* writes XHTML, SVG and MathML elements without a prefix.
|
|
12
|
+
*
|
|
13
|
+
* @module @tradik/xslt3/serialize/markup/dialect
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { XPathError } from "../../errors.js";
|
|
17
|
+
import { percentEncode } from "../../functions/uri.js";
|
|
18
|
+
import { clarkName } from "../params/values.js";
|
|
19
|
+
import {
|
|
20
|
+
BOOLEAN_ATTRIBUTES,
|
|
21
|
+
HTML5_UNPREFIXED,
|
|
22
|
+
INLINE_ELEMENTS,
|
|
23
|
+
isUriAttribute,
|
|
24
|
+
PRESERVING_ELEMENTS,
|
|
25
|
+
RAW_TEXT_ELEMENTS,
|
|
26
|
+
VOID_ELEMENTS,
|
|
27
|
+
XHTML_NAMESPACE,
|
|
28
|
+
} from "./htmlData.js";
|
|
29
|
+
|
|
30
|
+
/** escape-html-uri: %-escapes all but printable ASCII. */
|
|
31
|
+
const escapeUri = (value) =>
|
|
32
|
+
percentEncode(value.normalize("NFC"), (cp) => cp >= 32 && cp < 127);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lower-cases the local names of a set of Clark names.
|
|
36
|
+
* @param {Set<string>} names
|
|
37
|
+
* @returns {Set<string>}
|
|
38
|
+
*/
|
|
39
|
+
const lowerCased = (names) =>
|
|
40
|
+
new Set([...names].map((n) => n.replace(/\}.*$/, (s) => s.toLowerCase())));
|
|
41
|
+
|
|
42
|
+
/** Output method rules for the markup writer. */
|
|
43
|
+
export class Dialect {
|
|
44
|
+
/**
|
|
45
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
46
|
+
*/
|
|
47
|
+
constructor(settings) {
|
|
48
|
+
this.settings = settings;
|
|
49
|
+
this.method = settings.method;
|
|
50
|
+
this.html = this.method === "html";
|
|
51
|
+
this.htmlLike = this.html || this.method === "xhtml";
|
|
52
|
+
this.html5 = this.htmlLike && settings.htmlVersion === 5;
|
|
53
|
+
this.unprefixed = this.html5 ? HTML5_UNPREFIXED : new Set();
|
|
54
|
+
this.voids = VOID_ELEMENTS[settings.htmlVersion];
|
|
55
|
+
const names = (set) => (this.html ? lowerCased(set) : set);
|
|
56
|
+
this.cdataElements = names(settings.cdataSectionElements);
|
|
57
|
+
this.suppressed = names(settings.suppressIndentation);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Describes an element for the other hooks.
|
|
62
|
+
* @param {Element} element
|
|
63
|
+
* @param {import("./namespaces.js").FixedElement} fixed
|
|
64
|
+
* @returns {{fixed: object, isHtml: boolean, lower: string, key: string}}
|
|
65
|
+
*/
|
|
66
|
+
describe(element, fixed) {
|
|
67
|
+
const ns = fixed.namespaceURI;
|
|
68
|
+
const local = element.localName ?? element.nodeName;
|
|
69
|
+
const lower = local.toLowerCase();
|
|
70
|
+
const primary = this.html ? "" : XHTML_NAMESPACE;
|
|
71
|
+
const secondary = this.html ? XHTML_NAMESPACE : "";
|
|
72
|
+
const isHtml =
|
|
73
|
+
this.htmlLike && (ns === primary || (this.html5 && ns === secondary));
|
|
74
|
+
const key = clarkName(ns, this.html ? lower : local);
|
|
75
|
+
return { fixed, isHtml, lower, key };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {object} info - From {@link Dialect#describe}
|
|
80
|
+
* @returns {string} the markup closing an empty element's start tag
|
|
81
|
+
*/
|
|
82
|
+
emptyElement(info) {
|
|
83
|
+
if (!this.htmlLike) return "/>";
|
|
84
|
+
const isVoid = info.isHtml && this.voids.has(info.lower);
|
|
85
|
+
if (this.html) {
|
|
86
|
+
if (isVoid) return ">";
|
|
87
|
+
return info.isHtml ? `></${info.fixed.name}>` : "/>";
|
|
88
|
+
}
|
|
89
|
+
return isVoid ? " />" : `></${info.fixed.name}>`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {object} info - The element
|
|
94
|
+
* @param {{name: string, localName: string, namespaceURI: string, value: string}} attribute
|
|
95
|
+
* @param {import("../output/expander.js").Expander} expander
|
|
96
|
+
* @returns {string} the attribute markup, with its leading space
|
|
97
|
+
*/
|
|
98
|
+
attribute(info, attribute, expander) {
|
|
99
|
+
let { value } = attribute;
|
|
100
|
+
if (info.isHtml && attribute.namespaceURI === "") {
|
|
101
|
+
const lower = attribute.localName.toLowerCase();
|
|
102
|
+
if (
|
|
103
|
+
this.html &&
|
|
104
|
+
BOOLEAN_ATTRIBUTES.has(lower) &&
|
|
105
|
+
value.toLowerCase() === lower
|
|
106
|
+
) {
|
|
107
|
+
return ` ${attribute.name}`;
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
this.settings.escapeUriAttributes &&
|
|
111
|
+
isUriAttribute(info.lower, lower)
|
|
112
|
+
) {
|
|
113
|
+
value = escapeUri(value);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return ` ${attribute.name}="${expander.attribute(value)}"`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* How the text children of an element are written.
|
|
121
|
+
* @param {object} info
|
|
122
|
+
* @returns {"escape"|"cdata"|"raw"}
|
|
123
|
+
*/
|
|
124
|
+
textMode(info) {
|
|
125
|
+
if (this.html && info.isHtml) {
|
|
126
|
+
return RAW_TEXT_ELEMENTS.has(info.lower) ? "raw" : "escape";
|
|
127
|
+
}
|
|
128
|
+
return this.cdataElements.has(info.key) ? "cdata" : "escape";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether indentation is suppressed inside an element and its
|
|
133
|
+
* descendants.
|
|
134
|
+
* @param {object} info
|
|
135
|
+
* @returns {boolean}
|
|
136
|
+
*/
|
|
137
|
+
suppressesIndent(info) {
|
|
138
|
+
return (
|
|
139
|
+
this.suppressed.has(info.key) ||
|
|
140
|
+
(info.isHtml && PRESERVING_ELEMENTS.has(info.lower))
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Whether the children (elements, comments, processing instructions)
|
|
146
|
+
* of an element may be indented: not when one is an inline element.
|
|
147
|
+
* @param {Array<string|Node>} children
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
allowsIndent(children) {
|
|
151
|
+
if (!this.htmlLike) return true;
|
|
152
|
+
return !children.some(
|
|
153
|
+
(child) =>
|
|
154
|
+
child.nodeType === 1 &&
|
|
155
|
+
INLINE_ELEMENTS.has((child.localName ?? child.nodeName).toLowerCase()),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {ProcessingInstruction} node
|
|
161
|
+
* @param {import("../output/expander.js").Expander} expander
|
|
162
|
+
* @returns {string} the processing instruction markup
|
|
163
|
+
* @throws {XPathError} SERE0015 for ">" in HTML processing instructions
|
|
164
|
+
*/
|
|
165
|
+
processingInstruction(node, expander) {
|
|
166
|
+
const data = expander.unescaped(node.nodeValue ?? "");
|
|
167
|
+
const target = node.target ?? node.nodeName;
|
|
168
|
+
const space = data ? " " : "";
|
|
169
|
+
if (!this.html) return `<?${target}${space}${data}?>`;
|
|
170
|
+
if (data.includes(">")) {
|
|
171
|
+
throw new XPathError(
|
|
172
|
+
"SERE0015",
|
|
173
|
+
"A processing instruction in HTML output cannot contain >",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return `<?${target}${space}${data}>`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the html and xhtml output methods know about HTML elements and
|
|
3
|
+
* attributes (Serialization 3.1 sections 6 and 7, HTML 4.01 and the HTML
|
|
4
|
+
* Living Standard). Names are compared in lower case.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/serialize/markup/htmlData
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** The XHTML namespace. */
|
|
10
|
+
export const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
11
|
+
/** Namespaces whose elements HTML5 output writes without a prefix. */
|
|
12
|
+
export const HTML5_UNPREFIXED = new Set([
|
|
13
|
+
XHTML_NAMESPACE,
|
|
14
|
+
"http://www.w3.org/2000/svg",
|
|
15
|
+
"http://www.w3.org/1998/Math/MathML",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const words = (text) => new Set(text.split(" "));
|
|
19
|
+
|
|
20
|
+
/** Elements with no content (void elements), by HTML version. */
|
|
21
|
+
export const VOID_ELEMENTS = {
|
|
22
|
+
4: words(
|
|
23
|
+
"area base basefont br col embed frame hr img input isindex link meta param",
|
|
24
|
+
),
|
|
25
|
+
5: words(
|
|
26
|
+
"area base br col embed hr img input keygen link meta param source track wbr",
|
|
27
|
+
),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Elements whose content is written without escaping (html method). */
|
|
31
|
+
export const RAW_TEXT_ELEMENTS = words("script style");
|
|
32
|
+
|
|
33
|
+
/** Elements inside which no whitespace is added. */
|
|
34
|
+
export const PRESERVING_ELEMENTS = words("pre script style textarea title");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Phrasing (inline) elements: whitespace added around them would show, so
|
|
38
|
+
* their parents are not indented.
|
|
39
|
+
*/
|
|
40
|
+
export const INLINE_ELEMENTS = words(
|
|
41
|
+
"a abbr acronym audio b bdi bdo big br button canvas cite code data " +
|
|
42
|
+
"datalist del dfn em embed font i iframe img input ins kbd label map " +
|
|
43
|
+
"mark math meter noscript object output picture progress q ruby s samp " +
|
|
44
|
+
"select slot small span strike strong sub sup svg template textarea " +
|
|
45
|
+
"time tt u var video wbr",
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/** Attributes written minimized when their value is their name. */
|
|
49
|
+
export const BOOLEAN_ATTRIBUTES = words(
|
|
50
|
+
"allowfullscreen async autofocus autoplay checked compact controls " +
|
|
51
|
+
"declare default defer disabled formnovalidate hidden inert ismap " +
|
|
52
|
+
"itemscope loop multiple muted nohref noresize noshade novalidate " +
|
|
53
|
+
"nowrap open playsinline readonly required reversed scoped seamless " +
|
|
54
|
+
"selected typemustmatch",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/** Attributes holding URIs, %-escaped with escape-uri-attributes. */
|
|
58
|
+
export const URI_ATTRIBUTES = words(
|
|
59
|
+
"action archive background cite classid codebase data datasrc " +
|
|
60
|
+
"formaction href icon longdesc manifest poster profile src usemap",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Whether an attribute holds a URI (`name` only on `a`).
|
|
65
|
+
* @param {string} element - Element local name, lower case
|
|
66
|
+
* @param {string} attribute - Attribute local name, lower case
|
|
67
|
+
* @returns {boolean}
|
|
68
|
+
*/
|
|
69
|
+
export const isUriAttribute = (element, attribute) =>
|
|
70
|
+
URI_ATTRIBUTES.has(attribute) || (attribute === "name" && element === "a");
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace fixup: the namespace declarations written on an element so
|
|
3
|
+
* that its name, its attributes' names and its in-scope namespaces are
|
|
4
|
+
* declared, whether or not the DOM holds `xmlns` attributes for them
|
|
5
|
+
* (createElementNS and setAttributeNS add none). A namespaced attribute
|
|
6
|
+
* without a usable prefix gets one (an existing one for its namespace, or
|
|
7
|
+
* ns0, ns1, ...).
|
|
8
|
+
*
|
|
9
|
+
* A scope is a Map from prefix ("" for the default namespace) to URI,
|
|
10
|
+
* shared by the elements that declare nothing and copied on change.
|
|
11
|
+
*
|
|
12
|
+
* @module @tradik/xslt3/serialize/markup/namespaces
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
isNamespaceDeclaration,
|
|
17
|
+
XMLNS_NAMESPACE,
|
|
18
|
+
} from "../../xpath/eval/domNodes.js";
|
|
19
|
+
import {
|
|
20
|
+
inScopeNamespaces,
|
|
21
|
+
XML_NAMESPACE,
|
|
22
|
+
} from "../../xpath/eval/namespaceNodes.js";
|
|
23
|
+
|
|
24
|
+
export { XML_NAMESPACE };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} FixedElement
|
|
28
|
+
* @property {string} name - Output name of the element
|
|
29
|
+
* @property {string} namespaceURI - Its namespace, "" for none
|
|
30
|
+
* @property {Array<[string, string]>} declarations - [prefix, URI] to declare
|
|
31
|
+
* @property {Array<{name: string, localName: string, namespaceURI: string, value: string}>} attributes
|
|
32
|
+
* @property {Map<string, string>} scope - Scope of the children
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {object} FixupOptions
|
|
37
|
+
* @property {boolean} top - The element is the first one written of its
|
|
38
|
+
* tree: its inherited namespaces are declared too
|
|
39
|
+
* @property {Set<string>} unprefixed - Namespaces whose elements are
|
|
40
|
+
* written without a prefix (HTML5 prefix normalization)
|
|
41
|
+
* @property {boolean} undeclare - Write `xmlns:p=""` undeclarations (XML
|
|
42
|
+
* 1.1 with undeclare-prefixes)
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Bindings an element asks for, in order: inherited ones (top element),
|
|
47
|
+
* its own declarations, its name.
|
|
48
|
+
* @param {Element} element
|
|
49
|
+
* @param {string} prefix - Output prefix of the element
|
|
50
|
+
* @param {FixupOptions} options
|
|
51
|
+
* @returns {Map<string, string>}
|
|
52
|
+
*/
|
|
53
|
+
function wantedBindings(element, prefix, options) {
|
|
54
|
+
const wanted = new Map();
|
|
55
|
+
const add = (p, uri) => {
|
|
56
|
+
if (p === "xml") return;
|
|
57
|
+
if (p !== "" && options.unprefixed.has(uri)) return;
|
|
58
|
+
wanted.set(p, uri);
|
|
59
|
+
};
|
|
60
|
+
if (options.top) {
|
|
61
|
+
for (const [p, uri] of inScopeNamespaces(element)) {
|
|
62
|
+
if (uri !== "" || p === "") add(p, uri);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const attribute of element.attributes) {
|
|
66
|
+
if (isNamespaceDeclaration(attribute)) {
|
|
67
|
+
const name = attribute.name;
|
|
68
|
+
add(name === "xmlns" ? "" : name.slice(6), attribute.value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
wanted.set(prefix, element.namespaceURI ?? "");
|
|
72
|
+
return wanted;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Chooses the prefix of a namespaced attribute.
|
|
77
|
+
* @param {Attr} attribute
|
|
78
|
+
* @param {string} uri - Its namespace
|
|
79
|
+
* @param {(prefix: string) => string|undefined} bound - Current binding
|
|
80
|
+
* @param {Map<string, string>} wanted
|
|
81
|
+
* @param {Map<string, string>} scope - Scope of the parent
|
|
82
|
+
* @returns {string} the prefix
|
|
83
|
+
*/
|
|
84
|
+
function attributePrefix(attribute, uri, bound, wanted, scope) {
|
|
85
|
+
const own = attribute.prefix ?? "";
|
|
86
|
+
if (own !== "" && (bound(own) === undefined || bound(own) === uri)) {
|
|
87
|
+
return own;
|
|
88
|
+
}
|
|
89
|
+
for (const map of [wanted, scope]) {
|
|
90
|
+
for (const [p, u] of map) {
|
|
91
|
+
if (p !== "" && u === uri && bound(p) === uri) return p;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
let n = 0;
|
|
95
|
+
while (bound(`ns${n}`) !== undefined) n++;
|
|
96
|
+
return `ns${n}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Computes the names and the namespace declarations of an element.
|
|
101
|
+
* @param {Element} element
|
|
102
|
+
* @param {Map<string, string>} scope - Scope of the parent
|
|
103
|
+
* @param {FixupOptions} options
|
|
104
|
+
* @returns {FixedElement}
|
|
105
|
+
*/
|
|
106
|
+
export function fixupElement(element, scope, options) {
|
|
107
|
+
const namespaceURI = element.namespaceURI ?? "";
|
|
108
|
+
const localName = element.localName ?? element.nodeName;
|
|
109
|
+
const prefix = options.unprefixed.has(namespaceURI)
|
|
110
|
+
? ""
|
|
111
|
+
: (element.prefix ?? "");
|
|
112
|
+
const wanted = wantedBindings(element, prefix, options);
|
|
113
|
+
const bound = (p) =>
|
|
114
|
+
wanted.has(p) ? wanted.get(p) : (scope.get(p) ?? (p ? undefined : ""));
|
|
115
|
+
const attributes = [];
|
|
116
|
+
for (const attribute of element.attributes) {
|
|
117
|
+
if (isNamespaceDeclaration(attribute)) continue;
|
|
118
|
+
const uri = attribute.namespaceURI ?? "";
|
|
119
|
+
const local = attribute.localName ?? attribute.name;
|
|
120
|
+
let name = local;
|
|
121
|
+
if (uri === XML_NAMESPACE) {
|
|
122
|
+
name = `xml:${local}`;
|
|
123
|
+
} else if (uri !== "" && uri !== XMLNS_NAMESPACE) {
|
|
124
|
+
const p = attributePrefix(attribute, uri, bound, wanted, scope);
|
|
125
|
+
if (bound(p) !== uri) wanted.set(p, uri);
|
|
126
|
+
name = `${p}:${local}`;
|
|
127
|
+
}
|
|
128
|
+
const value = attribute.value;
|
|
129
|
+
attributes.push({ name, localName: local, namespaceURI: uri, value });
|
|
130
|
+
}
|
|
131
|
+
const declarations = [];
|
|
132
|
+
let childScope = scope;
|
|
133
|
+
for (const [p, uri] of wanted) {
|
|
134
|
+
const current = scope.get(p) ?? (p ? undefined : "");
|
|
135
|
+
if (current === uri) continue;
|
|
136
|
+
if (uri === "" && p !== "") {
|
|
137
|
+
if (!options.undeclare || current === undefined) continue;
|
|
138
|
+
}
|
|
139
|
+
declarations.push([p, uri]);
|
|
140
|
+
if (childScope === scope) childScope = new Map(scope);
|
|
141
|
+
childScope.set(p, uri);
|
|
142
|
+
}
|
|
143
|
+
const name = prefix ? `${prefix}:${localName}` : localName;
|
|
144
|
+
return { name, namespaceURI, declarations, attributes, scope: childScope };
|
|
145
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markup the serializer adds on its own: the XML declaration, the
|
|
3
|
+
* document type declaration and the content type `meta` element of
|
|
4
|
+
* include-content-type (Serialization 3.1 sections 5.1, 6, 7.1 and 7.4).
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/serialize/markup/prolog
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
11
|
+
* @returns {string} the XML declaration, "" when omitted or not XML
|
|
12
|
+
*/
|
|
13
|
+
export function xmlDeclaration(settings) {
|
|
14
|
+
const { method, omitXmlDeclaration, version, encoding, standalone } =
|
|
15
|
+
settings;
|
|
16
|
+
if (omitXmlDeclaration || (method !== "xml" && method !== "xhtml")) {
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
const standalonePart =
|
|
20
|
+
standalone === "omit" ? "" : ` standalone="${standalone}"`;
|
|
21
|
+
return `<?xml version="${version}" encoding="${encoding.name}"${standalonePart}?>`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A system literal: in double quotes, or single quotes when it holds one.
|
|
26
|
+
* @param {string} value
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
29
|
+
const quoted = (value) => (value.includes('"') ? `'${value}'` : `"${value}"`);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The document type declaration written before the first element.
|
|
33
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
34
|
+
* @param {string} name - Output name of the first element
|
|
35
|
+
* @returns {string} the declaration, "" for none
|
|
36
|
+
*/
|
|
37
|
+
export function doctype(settings, name) {
|
|
38
|
+
const { method, htmlVersion } = settings;
|
|
39
|
+
// a zero-length doctype-system or doctype-public is absent (erratum E31)
|
|
40
|
+
const doctypePublic = settings.doctypePublic || undefined;
|
|
41
|
+
const doctypeSystem =
|
|
42
|
+
settings.doctypeSystem === undefined || settings.doctypeSystem === ""
|
|
43
|
+
? undefined
|
|
44
|
+
: quoted(settings.doctypeSystem);
|
|
45
|
+
const html5Default =
|
|
46
|
+
htmlVersion === 5 && name.replace(/^.*:/, "").toLowerCase() === "html";
|
|
47
|
+
if (method === "html") {
|
|
48
|
+
if (doctypePublic !== undefined) {
|
|
49
|
+
const system = doctypeSystem === undefined ? "" : ` ${doctypeSystem}`;
|
|
50
|
+
return `<!DOCTYPE html PUBLIC "${doctypePublic}"${system}>`;
|
|
51
|
+
}
|
|
52
|
+
if (doctypeSystem !== undefined) {
|
|
53
|
+
return `<!DOCTYPE html SYSTEM ${doctypeSystem}>`;
|
|
54
|
+
}
|
|
55
|
+
return html5Default ? "<!DOCTYPE html>" : "";
|
|
56
|
+
}
|
|
57
|
+
if (doctypeSystem !== undefined) {
|
|
58
|
+
const external =
|
|
59
|
+
doctypePublic === undefined
|
|
60
|
+
? `SYSTEM ${doctypeSystem}`
|
|
61
|
+
: `PUBLIC "${doctypePublic}" ${doctypeSystem}`;
|
|
62
|
+
return `<!DOCTYPE ${name} ${external}>`;
|
|
63
|
+
}
|
|
64
|
+
// xhtml with HTML5: the element name as written
|
|
65
|
+
return method === "xhtml" && html5Default
|
|
66
|
+
? `<!DOCTYPE ${name.replace(/^.*:/, "")}>`
|
|
67
|
+
: "";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
72
|
+
* @returns {string} the meta element declaring the content type
|
|
73
|
+
*/
|
|
74
|
+
export function contentTypeMeta(settings) {
|
|
75
|
+
const end = settings.method === "html" ? ">" : " />";
|
|
76
|
+
const content = `${settings.mediaType}; charset=${settings.encoding.name}`;
|
|
77
|
+
return `<meta http-equiv="Content-Type" content="${content}"${end}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Whether a node is a meta element declaring the content type (replaced
|
|
82
|
+
* by the serializer's own with include-content-type).
|
|
83
|
+
* @param {Node} node
|
|
84
|
+
* @returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
export function isContentTypeMeta(node) {
|
|
87
|
+
if (node.nodeType !== 1) return false;
|
|
88
|
+
if ((node.localName ?? node.nodeName).toLowerCase() !== "meta") return false;
|
|
89
|
+
const value = node.getAttribute("http-equiv");
|
|
90
|
+
return typeof value === "string" && value.toLowerCase() === "content-type";
|
|
91
|
+
}
|