xslt-processor 4.1.0 → 4.2.1
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/index.d.mts +85 -0
- package/index.d.ts +85 -0
- package/index.js +372 -21
- package/index.js.map +1 -1
- package/index.mjs +372 -21
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/umd/xslt-processor.global.js +3 -3
- package/umd/xslt-processor.global.js.map +1 -1
package/index.d.mts
CHANGED
|
@@ -581,6 +581,11 @@ declare class Xslt {
|
|
|
581
581
|
* preserve-space takes precedence over strip-space for conflicting patterns.
|
|
582
582
|
*/
|
|
583
583
|
preserveSpacePatterns: string[];
|
|
584
|
+
/**
|
|
585
|
+
* Namespace aliases from xsl:namespace-alias declarations.
|
|
586
|
+
* Maps stylesheet namespace prefixes to result namespace prefixes.
|
|
587
|
+
*/
|
|
588
|
+
namespaceAliases: Map<string, string>;
|
|
584
589
|
constructor(options?: Partial<XsltOptions>);
|
|
585
590
|
/**
|
|
586
591
|
* The exported entry point of the XSL-T processor.
|
|
@@ -705,6 +710,86 @@ declare class Xslt {
|
|
|
705
710
|
* @param template The template.
|
|
706
711
|
*/
|
|
707
712
|
protected xsltKey(context: ExprContext, template: XNode): void;
|
|
713
|
+
/**
|
|
714
|
+
* Implements `xsl:message`.
|
|
715
|
+
* Outputs a message to the console. If terminate="yes", throws an error to stop processing.
|
|
716
|
+
* @param context The Expression Context.
|
|
717
|
+
* @param template The `<xsl:message>` node.
|
|
718
|
+
*/
|
|
719
|
+
protected xsltMessage(context: ExprContext, template: XNode): Promise<void>;
|
|
720
|
+
/**
|
|
721
|
+
* Implements `xsl:namespace-alias`.
|
|
722
|
+
* Declares that a namespace URI in the stylesheet should be replaced by a different
|
|
723
|
+
* namespace URI in the output.
|
|
724
|
+
* @param template The `<xsl:namespace-alias>` node.
|
|
725
|
+
*/
|
|
726
|
+
protected xsltNamespaceAlias(template: XNode): void;
|
|
727
|
+
/**
|
|
728
|
+
* Implements `xsl:number`.
|
|
729
|
+
* Inserts a formatted number into the result tree.
|
|
730
|
+
* @param context The Expression Context.
|
|
731
|
+
* @param template The `<xsl:number>` node.
|
|
732
|
+
* @param output The output node.
|
|
733
|
+
*/
|
|
734
|
+
protected xsltNumber(context: ExprContext, template: XNode, output?: XNode): void;
|
|
735
|
+
/**
|
|
736
|
+
* Counts nodes for xsl:number based on level, count, and from attributes.
|
|
737
|
+
* @param context The Expression Context.
|
|
738
|
+
* @param level The counting level: 'single', 'multiple', or 'any'.
|
|
739
|
+
* @param count Pattern to match nodes to count.
|
|
740
|
+
* @param from Pattern to start counting from.
|
|
741
|
+
* @returns The count value.
|
|
742
|
+
*/
|
|
743
|
+
protected xsltNumberCount(context: ExprContext, level: string, count: string | null, from: string | null): number;
|
|
744
|
+
/**
|
|
745
|
+
* Checks if a node matches a simple name pattern.
|
|
746
|
+
* @param node The node to check.
|
|
747
|
+
* @param pattern The pattern (node name) to match.
|
|
748
|
+
* @returns True if the node matches.
|
|
749
|
+
*/
|
|
750
|
+
protected nodeMatchesPattern(node: XNode, pattern: string): boolean;
|
|
751
|
+
/**
|
|
752
|
+
* Gets all nodes preceding the given node in document order.
|
|
753
|
+
* @param node The reference node.
|
|
754
|
+
* @returns Array of preceding nodes.
|
|
755
|
+
*/
|
|
756
|
+
protected getAllPrecedingNodes(node: XNode): XNode[];
|
|
757
|
+
/**
|
|
758
|
+
* Collects all descendant nodes of a given node.
|
|
759
|
+
* @param node The parent node.
|
|
760
|
+
* @param result The array to collect into.
|
|
761
|
+
*/
|
|
762
|
+
protected collectDescendants(node: XNode, result: XNode[]): void;
|
|
763
|
+
/**
|
|
764
|
+
* Formats a number according to the format string.
|
|
765
|
+
* @param number The number to format.
|
|
766
|
+
* @param format The format string (e.g., "1", "01", "a", "A", "i", "I").
|
|
767
|
+
* @param groupingSeparator Optional grouping separator.
|
|
768
|
+
* @param groupingSize Optional grouping size.
|
|
769
|
+
* @returns The formatted number string.
|
|
770
|
+
*/
|
|
771
|
+
protected xsltFormatNumber(number: number, format: string, groupingSeparator: string | null, groupingSize: string | null): string;
|
|
772
|
+
/**
|
|
773
|
+
* Converts a number to alphabetic representation.
|
|
774
|
+
* @param number The number to convert.
|
|
775
|
+
* @param uppercase Whether to use uppercase letters.
|
|
776
|
+
* @returns The alphabetic representation.
|
|
777
|
+
*/
|
|
778
|
+
protected numberToAlpha(number: number, uppercase: boolean): string;
|
|
779
|
+
/**
|
|
780
|
+
* Converts a number to Roman numeral representation.
|
|
781
|
+
* @param number The number to convert.
|
|
782
|
+
* @returns The Roman numeral string.
|
|
783
|
+
*/
|
|
784
|
+
protected numberToRoman(number: number): string;
|
|
785
|
+
/**
|
|
786
|
+
* Applies grouping separators to a numeric string.
|
|
787
|
+
* @param numStr The numeric string.
|
|
788
|
+
* @param separator The grouping separator.
|
|
789
|
+
* @param size The grouping size.
|
|
790
|
+
* @returns The grouped string.
|
|
791
|
+
*/
|
|
792
|
+
protected applyGrouping(numStr: string, separator: string, size: number): string;
|
|
708
793
|
/**
|
|
709
794
|
* Orders the current node list in the input context according to the
|
|
710
795
|
* sort order specified by xsl:sort child nodes of the current
|
package/index.d.ts
CHANGED
|
@@ -581,6 +581,11 @@ declare class Xslt {
|
|
|
581
581
|
* preserve-space takes precedence over strip-space for conflicting patterns.
|
|
582
582
|
*/
|
|
583
583
|
preserveSpacePatterns: string[];
|
|
584
|
+
/**
|
|
585
|
+
* Namespace aliases from xsl:namespace-alias declarations.
|
|
586
|
+
* Maps stylesheet namespace prefixes to result namespace prefixes.
|
|
587
|
+
*/
|
|
588
|
+
namespaceAliases: Map<string, string>;
|
|
584
589
|
constructor(options?: Partial<XsltOptions>);
|
|
585
590
|
/**
|
|
586
591
|
* The exported entry point of the XSL-T processor.
|
|
@@ -705,6 +710,86 @@ declare class Xslt {
|
|
|
705
710
|
* @param template The template.
|
|
706
711
|
*/
|
|
707
712
|
protected xsltKey(context: ExprContext, template: XNode): void;
|
|
713
|
+
/**
|
|
714
|
+
* Implements `xsl:message`.
|
|
715
|
+
* Outputs a message to the console. If terminate="yes", throws an error to stop processing.
|
|
716
|
+
* @param context The Expression Context.
|
|
717
|
+
* @param template The `<xsl:message>` node.
|
|
718
|
+
*/
|
|
719
|
+
protected xsltMessage(context: ExprContext, template: XNode): Promise<void>;
|
|
720
|
+
/**
|
|
721
|
+
* Implements `xsl:namespace-alias`.
|
|
722
|
+
* Declares that a namespace URI in the stylesheet should be replaced by a different
|
|
723
|
+
* namespace URI in the output.
|
|
724
|
+
* @param template The `<xsl:namespace-alias>` node.
|
|
725
|
+
*/
|
|
726
|
+
protected xsltNamespaceAlias(template: XNode): void;
|
|
727
|
+
/**
|
|
728
|
+
* Implements `xsl:number`.
|
|
729
|
+
* Inserts a formatted number into the result tree.
|
|
730
|
+
* @param context The Expression Context.
|
|
731
|
+
* @param template The `<xsl:number>` node.
|
|
732
|
+
* @param output The output node.
|
|
733
|
+
*/
|
|
734
|
+
protected xsltNumber(context: ExprContext, template: XNode, output?: XNode): void;
|
|
735
|
+
/**
|
|
736
|
+
* Counts nodes for xsl:number based on level, count, and from attributes.
|
|
737
|
+
* @param context The Expression Context.
|
|
738
|
+
* @param level The counting level: 'single', 'multiple', or 'any'.
|
|
739
|
+
* @param count Pattern to match nodes to count.
|
|
740
|
+
* @param from Pattern to start counting from.
|
|
741
|
+
* @returns The count value.
|
|
742
|
+
*/
|
|
743
|
+
protected xsltNumberCount(context: ExprContext, level: string, count: string | null, from: string | null): number;
|
|
744
|
+
/**
|
|
745
|
+
* Checks if a node matches a simple name pattern.
|
|
746
|
+
* @param node The node to check.
|
|
747
|
+
* @param pattern The pattern (node name) to match.
|
|
748
|
+
* @returns True if the node matches.
|
|
749
|
+
*/
|
|
750
|
+
protected nodeMatchesPattern(node: XNode, pattern: string): boolean;
|
|
751
|
+
/**
|
|
752
|
+
* Gets all nodes preceding the given node in document order.
|
|
753
|
+
* @param node The reference node.
|
|
754
|
+
* @returns Array of preceding nodes.
|
|
755
|
+
*/
|
|
756
|
+
protected getAllPrecedingNodes(node: XNode): XNode[];
|
|
757
|
+
/**
|
|
758
|
+
* Collects all descendant nodes of a given node.
|
|
759
|
+
* @param node The parent node.
|
|
760
|
+
* @param result The array to collect into.
|
|
761
|
+
*/
|
|
762
|
+
protected collectDescendants(node: XNode, result: XNode[]): void;
|
|
763
|
+
/**
|
|
764
|
+
* Formats a number according to the format string.
|
|
765
|
+
* @param number The number to format.
|
|
766
|
+
* @param format The format string (e.g., "1", "01", "a", "A", "i", "I").
|
|
767
|
+
* @param groupingSeparator Optional grouping separator.
|
|
768
|
+
* @param groupingSize Optional grouping size.
|
|
769
|
+
* @returns The formatted number string.
|
|
770
|
+
*/
|
|
771
|
+
protected xsltFormatNumber(number: number, format: string, groupingSeparator: string | null, groupingSize: string | null): string;
|
|
772
|
+
/**
|
|
773
|
+
* Converts a number to alphabetic representation.
|
|
774
|
+
* @param number The number to convert.
|
|
775
|
+
* @param uppercase Whether to use uppercase letters.
|
|
776
|
+
* @returns The alphabetic representation.
|
|
777
|
+
*/
|
|
778
|
+
protected numberToAlpha(number: number, uppercase: boolean): string;
|
|
779
|
+
/**
|
|
780
|
+
* Converts a number to Roman numeral representation.
|
|
781
|
+
* @param number The number to convert.
|
|
782
|
+
* @returns The Roman numeral string.
|
|
783
|
+
*/
|
|
784
|
+
protected numberToRoman(number: number): string;
|
|
785
|
+
/**
|
|
786
|
+
* Applies grouping separators to a numeric string.
|
|
787
|
+
* @param numStr The numeric string.
|
|
788
|
+
* @param separator The grouping separator.
|
|
789
|
+
* @param size The grouping size.
|
|
790
|
+
* @returns The grouped string.
|
|
791
|
+
*/
|
|
792
|
+
protected applyGrouping(numStr: string, separator: string, size: number): string;
|
|
708
793
|
/**
|
|
709
794
|
* Orders the current node list in the input context according to the
|
|
710
795
|
* sort order specified by xsl:sort child nodes of the current
|
package/index.js
CHANGED
|
@@ -3446,24 +3446,31 @@ function nodeMatchesSinglePattern(node, pattern, context, matchResolver, xPath)
|
|
|
3446
3446
|
const patternLocalName = attrPattern.includes(":") ? attrPattern.substring(attrPattern.indexOf(":") + 1) : attrPattern;
|
|
3447
3447
|
return attrName === patternLocalName || node.nodeName === attrPattern;
|
|
3448
3448
|
}
|
|
3449
|
-
const nodeContext = context.clone([node], 0);
|
|
3450
|
-
try {
|
|
3451
|
-
const expr = xPath.xPathParse(pattern, "self-and-siblings");
|
|
3452
|
-
const nodes = matchResolver.expressionMatch(expr, nodeContext);
|
|
3453
|
-
if (nodes.some((n) => n.id === node.id)) {
|
|
3454
|
-
return true;
|
|
3455
|
-
}
|
|
3456
|
-
} catch (e) {
|
|
3457
|
-
}
|
|
3458
3449
|
if (pattern === "*" && node.nodeType === DOM_ELEMENT_NODE) {
|
|
3459
3450
|
return true;
|
|
3460
3451
|
}
|
|
3461
|
-
if (pattern.includes("
|
|
3452
|
+
if (!pattern.includes("/") && !pattern.includes("[") && !pattern.startsWith("@")) {
|
|
3453
|
+
if (pattern === node.nodeName || pattern === node.localName) {
|
|
3454
|
+
return true;
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
if (pattern.includes("/") || pattern.includes("[")) {
|
|
3462
3458
|
try {
|
|
3459
|
+
const evaluationPattern = pattern.startsWith("/") ? pattern : "//" + pattern;
|
|
3463
3460
|
const rootContext = context.clone([context.root], 0);
|
|
3464
|
-
const
|
|
3465
|
-
const
|
|
3466
|
-
|
|
3461
|
+
const evalResult = xPath.xPathEval(evaluationPattern, rootContext);
|
|
3462
|
+
const nodes = evalResult.nodeSetValue();
|
|
3463
|
+
if (nodes.some((n) => n.id === node.id)) {
|
|
3464
|
+
return true;
|
|
3465
|
+
}
|
|
3466
|
+
} catch (e) {
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
if (!pattern.includes("/") && !pattern.includes("[") && !pattern.startsWith("@")) {
|
|
3470
|
+
try {
|
|
3471
|
+
const nodeContext = context.clone([node], 0);
|
|
3472
|
+
const expr = xPath.xPathParse(pattern, "self-and-siblings");
|
|
3473
|
+
const nodes = matchResolver.expressionMatch(expr, nodeContext);
|
|
3467
3474
|
if (nodes.some((n) => n.id === node.id)) {
|
|
3468
3475
|
return true;
|
|
3469
3476
|
}
|
|
@@ -3550,6 +3557,7 @@ var Xslt = class {
|
|
|
3550
3557
|
this.outputOmitXmlDeclaration = "no";
|
|
3551
3558
|
this.stripSpacePatterns = [];
|
|
3552
3559
|
this.preserveSpacePatterns = [];
|
|
3560
|
+
this.namespaceAliases = /* @__PURE__ */ new Map();
|
|
3553
3561
|
this.decimalFormatSettings = {
|
|
3554
3562
|
decimalSeparator: ".",
|
|
3555
3563
|
groupingSeparator: ",",
|
|
@@ -3667,13 +3675,16 @@ var Xslt = class {
|
|
|
3667
3675
|
this.xsltKey(context, template);
|
|
3668
3676
|
break;
|
|
3669
3677
|
case "message":
|
|
3670
|
-
|
|
3678
|
+
yield this.xsltMessage(context, template);
|
|
3679
|
+
break;
|
|
3671
3680
|
case "namespace-alias":
|
|
3672
|
-
|
|
3681
|
+
this.xsltNamespaceAlias(template);
|
|
3682
|
+
break;
|
|
3673
3683
|
case "number":
|
|
3674
|
-
|
|
3684
|
+
this.xsltNumber(context, template, output);
|
|
3685
|
+
break;
|
|
3675
3686
|
case "otherwise":
|
|
3676
|
-
throw new Error(
|
|
3687
|
+
throw new Error(`<xsl:otherwise> must be a child of <xsl:choose>.`);
|
|
3677
3688
|
case "output":
|
|
3678
3689
|
this.outputMethod = xmlGetAttribute(template, "method");
|
|
3679
3690
|
this.outputOmitXmlDeclaration = xmlGetAttribute(template, "omit-xml-declaration");
|
|
@@ -3709,9 +3720,9 @@ var Xslt = class {
|
|
|
3709
3720
|
yield this.xsltVariable(context, template, true);
|
|
3710
3721
|
break;
|
|
3711
3722
|
case "when":
|
|
3712
|
-
throw new Error(
|
|
3723
|
+
throw new Error(`<xsl:when> must be a child of <xsl:choose>.`);
|
|
3713
3724
|
case "with-param":
|
|
3714
|
-
throw new Error(
|
|
3725
|
+
throw new Error(`<xsl:with-param> must be a child of <xsl:call-template> or <xsl:apply-templates>.`);
|
|
3715
3726
|
default:
|
|
3716
3727
|
throw new Error(`error if here: ${template.localName}`);
|
|
3717
3728
|
}
|
|
@@ -3737,7 +3748,9 @@ var Xslt = class {
|
|
|
3737
3748
|
const mode = xmlGetAttribute(template, "mode");
|
|
3738
3749
|
const top = template.ownerDocument.documentElement;
|
|
3739
3750
|
const expandedTemplates = collectAndExpandTemplates(top, mode, this.xPath);
|
|
3740
|
-
const
|
|
3751
|
+
const paramContext = context.clone();
|
|
3752
|
+
yield this.xsltWithParam(paramContext, template);
|
|
3753
|
+
const modifiedContext = paramContext.clone(nodes);
|
|
3741
3754
|
for (let j = 0; j < modifiedContext.contextSize(); ++j) {
|
|
3742
3755
|
const currentNode = modifiedContext.nodeList[j];
|
|
3743
3756
|
if (currentNode.nodeType === DOM_TEXT_NODE) {
|
|
@@ -4087,6 +4100,281 @@ var Xslt = class {
|
|
|
4087
4100
|
context.keys[name][attributeValue] = new NodeSetValue([node]);
|
|
4088
4101
|
}
|
|
4089
4102
|
}
|
|
4103
|
+
/**
|
|
4104
|
+
* Implements `xsl:message`.
|
|
4105
|
+
* Outputs a message to the console. If terminate="yes", throws an error to stop processing.
|
|
4106
|
+
* @param context The Expression Context.
|
|
4107
|
+
* @param template The `<xsl:message>` node.
|
|
4108
|
+
*/
|
|
4109
|
+
xsltMessage(context, template) {
|
|
4110
|
+
return __async(this, null, function* () {
|
|
4111
|
+
const documentFragment = domCreateDocumentFragment(this.outputDocument);
|
|
4112
|
+
yield this.xsltChildNodes(context, template, documentFragment);
|
|
4113
|
+
const messageText = xmlValue(documentFragment);
|
|
4114
|
+
const terminate = xmlGetAttribute(template, "terminate") || "no";
|
|
4115
|
+
console.log(`[xsl:message] ${messageText}`);
|
|
4116
|
+
if (terminate === "yes") {
|
|
4117
|
+
throw new Error(`xsl:message terminated: ${messageText}`);
|
|
4118
|
+
}
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
/**
|
|
4122
|
+
* Implements `xsl:namespace-alias`.
|
|
4123
|
+
* Declares that a namespace URI in the stylesheet should be replaced by a different
|
|
4124
|
+
* namespace URI in the output.
|
|
4125
|
+
* @param template The `<xsl:namespace-alias>` node.
|
|
4126
|
+
*/
|
|
4127
|
+
xsltNamespaceAlias(template) {
|
|
4128
|
+
const stylesheetPrefix = xmlGetAttribute(template, "stylesheet-prefix");
|
|
4129
|
+
const resultPrefix = xmlGetAttribute(template, "result-prefix");
|
|
4130
|
+
if (!stylesheetPrefix || !resultPrefix) {
|
|
4131
|
+
throw new Error("<xsl:namespace-alias> requires both stylesheet-prefix and result-prefix attributes.");
|
|
4132
|
+
}
|
|
4133
|
+
this.namespaceAliases.set(stylesheetPrefix, resultPrefix);
|
|
4134
|
+
}
|
|
4135
|
+
/**
|
|
4136
|
+
* Implements `xsl:number`.
|
|
4137
|
+
* Inserts a formatted number into the result tree.
|
|
4138
|
+
* @param context The Expression Context.
|
|
4139
|
+
* @param template The `<xsl:number>` node.
|
|
4140
|
+
* @param output The output node.
|
|
4141
|
+
*/
|
|
4142
|
+
xsltNumber(context, template, output) {
|
|
4143
|
+
const value = xmlGetAttribute(template, "value");
|
|
4144
|
+
const level = xmlGetAttribute(template, "level") || "single";
|
|
4145
|
+
const count = xmlGetAttribute(template, "count");
|
|
4146
|
+
const from = xmlGetAttribute(template, "from");
|
|
4147
|
+
const format = xmlGetAttribute(template, "format") || "1";
|
|
4148
|
+
const lang = xmlGetAttribute(template, "lang");
|
|
4149
|
+
const letterValue = xmlGetAttribute(template, "letter-value");
|
|
4150
|
+
const groupingSeparator = xmlGetAttribute(template, "grouping-separator");
|
|
4151
|
+
const groupingSize = xmlGetAttribute(template, "grouping-size");
|
|
4152
|
+
let number;
|
|
4153
|
+
if (value) {
|
|
4154
|
+
const result = this.xPath.xPathEval(value, context);
|
|
4155
|
+
number = Math.round(result.numberValue());
|
|
4156
|
+
} else {
|
|
4157
|
+
number = this.xsltNumberCount(context, level, count, from);
|
|
4158
|
+
}
|
|
4159
|
+
const formattedNumber = this.xsltFormatNumber(number, format, groupingSeparator, groupingSize);
|
|
4160
|
+
const textNode = domCreateTextNode(this.outputDocument, formattedNumber);
|
|
4161
|
+
const targetOutput = output || this.outputDocument;
|
|
4162
|
+
textNode.siblingPosition = targetOutput.childNodes.length;
|
|
4163
|
+
domAppendChild(targetOutput, textNode);
|
|
4164
|
+
}
|
|
4165
|
+
/**
|
|
4166
|
+
* Counts nodes for xsl:number based on level, count, and from attributes.
|
|
4167
|
+
* @param context The Expression Context.
|
|
4168
|
+
* @param level The counting level: 'single', 'multiple', or 'any'.
|
|
4169
|
+
* @param count Pattern to match nodes to count.
|
|
4170
|
+
* @param from Pattern to start counting from.
|
|
4171
|
+
* @returns The count value.
|
|
4172
|
+
*/
|
|
4173
|
+
xsltNumberCount(context, level, count, from) {
|
|
4174
|
+
const currentNode = context.nodeList[context.position];
|
|
4175
|
+
const countPattern = count || currentNode.nodeName;
|
|
4176
|
+
switch (level) {
|
|
4177
|
+
case "single": {
|
|
4178
|
+
let num = 1;
|
|
4179
|
+
let sibling = currentNode.previousSibling;
|
|
4180
|
+
while (sibling) {
|
|
4181
|
+
if (sibling.nodeType === currentNode.nodeType) {
|
|
4182
|
+
if (this.nodeMatchesPattern(sibling, countPattern)) {
|
|
4183
|
+
num++;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
sibling = sibling.previousSibling;
|
|
4187
|
+
}
|
|
4188
|
+
return num;
|
|
4189
|
+
}
|
|
4190
|
+
case "multiple": {
|
|
4191
|
+
let num = 1;
|
|
4192
|
+
let sibling = currentNode.previousSibling;
|
|
4193
|
+
while (sibling) {
|
|
4194
|
+
if (sibling.nodeType === currentNode.nodeType) {
|
|
4195
|
+
if (this.nodeMatchesPattern(sibling, countPattern)) {
|
|
4196
|
+
num++;
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
sibling = sibling.previousSibling;
|
|
4200
|
+
}
|
|
4201
|
+
return num;
|
|
4202
|
+
}
|
|
4203
|
+
case "any": {
|
|
4204
|
+
let num = 1;
|
|
4205
|
+
const allNodes = this.getAllPrecedingNodes(currentNode);
|
|
4206
|
+
for (const node of allNodes) {
|
|
4207
|
+
if (this.nodeMatchesPattern(node, countPattern)) {
|
|
4208
|
+
num++;
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
return num;
|
|
4212
|
+
}
|
|
4213
|
+
default:
|
|
4214
|
+
return 1;
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
/**
|
|
4218
|
+
* Checks if a node matches a simple name pattern.
|
|
4219
|
+
* @param node The node to check.
|
|
4220
|
+
* @param pattern The pattern (node name) to match.
|
|
4221
|
+
* @returns True if the node matches.
|
|
4222
|
+
*/
|
|
4223
|
+
nodeMatchesPattern(node, pattern) {
|
|
4224
|
+
if (pattern === "*") {
|
|
4225
|
+
return node.nodeType === DOM_ELEMENT_NODE;
|
|
4226
|
+
}
|
|
4227
|
+
return node.nodeName === pattern || node.localName === pattern;
|
|
4228
|
+
}
|
|
4229
|
+
/**
|
|
4230
|
+
* Gets all nodes preceding the given node in document order.
|
|
4231
|
+
* @param node The reference node.
|
|
4232
|
+
* @returns Array of preceding nodes.
|
|
4233
|
+
*/
|
|
4234
|
+
getAllPrecedingNodes(node) {
|
|
4235
|
+
const result = [];
|
|
4236
|
+
let sibling = node.previousSibling;
|
|
4237
|
+
while (sibling) {
|
|
4238
|
+
result.push(sibling);
|
|
4239
|
+
this.collectDescendants(sibling, result);
|
|
4240
|
+
sibling = sibling.previousSibling;
|
|
4241
|
+
}
|
|
4242
|
+
let parent = node.parentNode;
|
|
4243
|
+
while (parent) {
|
|
4244
|
+
let parentSibling = parent.previousSibling;
|
|
4245
|
+
while (parentSibling) {
|
|
4246
|
+
result.push(parentSibling);
|
|
4247
|
+
this.collectDescendants(parentSibling, result);
|
|
4248
|
+
parentSibling = parentSibling.previousSibling;
|
|
4249
|
+
}
|
|
4250
|
+
parent = parent.parentNode;
|
|
4251
|
+
}
|
|
4252
|
+
return result;
|
|
4253
|
+
}
|
|
4254
|
+
/**
|
|
4255
|
+
* Collects all descendant nodes of a given node.
|
|
4256
|
+
* @param node The parent node.
|
|
4257
|
+
* @param result The array to collect into.
|
|
4258
|
+
*/
|
|
4259
|
+
collectDescendants(node, result) {
|
|
4260
|
+
for (const child of node.childNodes) {
|
|
4261
|
+
if (child.nodeType === DOM_ELEMENT_NODE) {
|
|
4262
|
+
result.push(child);
|
|
4263
|
+
this.collectDescendants(child, result);
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
/**
|
|
4268
|
+
* Formats a number according to the format string.
|
|
4269
|
+
* @param number The number to format.
|
|
4270
|
+
* @param format The format string (e.g., "1", "01", "a", "A", "i", "I").
|
|
4271
|
+
* @param groupingSeparator Optional grouping separator.
|
|
4272
|
+
* @param groupingSize Optional grouping size.
|
|
4273
|
+
* @returns The formatted number string.
|
|
4274
|
+
*/
|
|
4275
|
+
xsltFormatNumber(number, format, groupingSeparator, groupingSize) {
|
|
4276
|
+
const formatChar = format.charAt(0);
|
|
4277
|
+
let result;
|
|
4278
|
+
switch (formatChar) {
|
|
4279
|
+
case "1":
|
|
4280
|
+
result = number.toString();
|
|
4281
|
+
if (format.length > 1 && format.match(/^0+1$/)) {
|
|
4282
|
+
const width = format.length;
|
|
4283
|
+
result = number.toString().padStart(width, "0");
|
|
4284
|
+
}
|
|
4285
|
+
break;
|
|
4286
|
+
case "a":
|
|
4287
|
+
result = this.numberToAlpha(number, false);
|
|
4288
|
+
break;
|
|
4289
|
+
case "A":
|
|
4290
|
+
result = this.numberToAlpha(number, true);
|
|
4291
|
+
break;
|
|
4292
|
+
case "i":
|
|
4293
|
+
result = this.numberToRoman(number).toLowerCase();
|
|
4294
|
+
break;
|
|
4295
|
+
case "I":
|
|
4296
|
+
result = this.numberToRoman(number);
|
|
4297
|
+
break;
|
|
4298
|
+
default:
|
|
4299
|
+
result = number.toString();
|
|
4300
|
+
}
|
|
4301
|
+
if (groupingSeparator && groupingSize) {
|
|
4302
|
+
const size = parseInt(groupingSize, 10);
|
|
4303
|
+
if (size > 0 && !isNaN(size)) {
|
|
4304
|
+
result = this.applyGrouping(result, groupingSeparator, size);
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
return result;
|
|
4308
|
+
}
|
|
4309
|
+
/**
|
|
4310
|
+
* Converts a number to alphabetic representation.
|
|
4311
|
+
* @param number The number to convert.
|
|
4312
|
+
* @param uppercase Whether to use uppercase letters.
|
|
4313
|
+
* @returns The alphabetic representation.
|
|
4314
|
+
*/
|
|
4315
|
+
numberToAlpha(number, uppercase) {
|
|
4316
|
+
if (number <= 0) return "";
|
|
4317
|
+
let result = "";
|
|
4318
|
+
while (number > 0) {
|
|
4319
|
+
number--;
|
|
4320
|
+
result = String.fromCharCode(number % 26 + (uppercase ? 65 : 97)) + result;
|
|
4321
|
+
number = Math.floor(number / 26);
|
|
4322
|
+
}
|
|
4323
|
+
return result;
|
|
4324
|
+
}
|
|
4325
|
+
/**
|
|
4326
|
+
* Converts a number to Roman numeral representation.
|
|
4327
|
+
* @param number The number to convert.
|
|
4328
|
+
* @returns The Roman numeral string.
|
|
4329
|
+
*/
|
|
4330
|
+
numberToRoman(number) {
|
|
4331
|
+
if (number <= 0 || number > 3999) return number.toString();
|
|
4332
|
+
const romanNumerals = [
|
|
4333
|
+
[1e3, "M"],
|
|
4334
|
+
[900, "CM"],
|
|
4335
|
+
[500, "D"],
|
|
4336
|
+
[400, "CD"],
|
|
4337
|
+
[100, "C"],
|
|
4338
|
+
[90, "XC"],
|
|
4339
|
+
[50, "L"],
|
|
4340
|
+
[40, "XL"],
|
|
4341
|
+
[10, "X"],
|
|
4342
|
+
[9, "IX"],
|
|
4343
|
+
[5, "V"],
|
|
4344
|
+
[4, "IV"],
|
|
4345
|
+
[1, "I"]
|
|
4346
|
+
];
|
|
4347
|
+
let result = "";
|
|
4348
|
+
for (const [value, numeral] of romanNumerals) {
|
|
4349
|
+
while (number >= value) {
|
|
4350
|
+
result += numeral;
|
|
4351
|
+
number -= value;
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
return result;
|
|
4355
|
+
}
|
|
4356
|
+
/**
|
|
4357
|
+
* Applies grouping separators to a numeric string.
|
|
4358
|
+
* @param numStr The numeric string.
|
|
4359
|
+
* @param separator The grouping separator.
|
|
4360
|
+
* @param size The grouping size.
|
|
4361
|
+
* @returns The grouped string.
|
|
4362
|
+
*/
|
|
4363
|
+
applyGrouping(numStr, separator, size) {
|
|
4364
|
+
const parts = numStr.split(".");
|
|
4365
|
+
let intPart = parts[0];
|
|
4366
|
+
const decPart = parts[1];
|
|
4367
|
+
let result = "";
|
|
4368
|
+
let count = 0;
|
|
4369
|
+
for (let i = intPart.length - 1; i >= 0; i--) {
|
|
4370
|
+
if (count > 0 && count % size === 0) {
|
|
4371
|
+
result = separator + result;
|
|
4372
|
+
}
|
|
4373
|
+
result = intPart[i] + result;
|
|
4374
|
+
count++;
|
|
4375
|
+
}
|
|
4376
|
+
return decPart ? result + "." + decPart : result;
|
|
4377
|
+
}
|
|
4090
4378
|
/**
|
|
4091
4379
|
* Orders the current node list in the input context according to the
|
|
4092
4380
|
* sort order specified by xsl:sort child nodes of the current
|
|
@@ -4333,13 +4621,76 @@ var Xslt = class {
|
|
|
4333
4621
|
contextClone.baseTemplateMatched = true;
|
|
4334
4622
|
const templateContext = contextClone.clone(winner.matchedNodes, 0);
|
|
4335
4623
|
yield this.xsltChildNodes(templateContext, winner.priority.template, output);
|
|
4624
|
+
} else {
|
|
4625
|
+
const rootNode = context.nodeList[context.position];
|
|
4626
|
+
if (rootNode && rootNode.childNodes && rootNode.childNodes.length > 0) {
|
|
4627
|
+
const childNodes = rootNode.childNodes.filter((n) => n.nodeName !== "#dtd-section");
|
|
4628
|
+
if (childNodes.length > 0) {
|
|
4629
|
+
const childContext = context.clone(childNodes);
|
|
4630
|
+
for (let j = 0; j < childContext.contextSize(); ++j) {
|
|
4631
|
+
const currentNode = childContext.nodeList[j];
|
|
4632
|
+
if (currentNode.nodeType === DOM_TEXT_NODE) {
|
|
4633
|
+
const textNodeContext = context.clone([currentNode], 0);
|
|
4634
|
+
this.commonLogicTextNode(textNodeContext, currentNode, output);
|
|
4635
|
+
} else {
|
|
4636
|
+
const clonedContext = childContext.clone([currentNode], 0);
|
|
4637
|
+
const selection = selectBestTemplate(
|
|
4638
|
+
expandedTemplates,
|
|
4639
|
+
clonedContext,
|
|
4640
|
+
this.matchResolver,
|
|
4641
|
+
this.xPath
|
|
4642
|
+
);
|
|
4643
|
+
if (selection.selectedTemplate) {
|
|
4644
|
+
const templateContext = clonedContext.clone([currentNode], 0);
|
|
4645
|
+
templateContext.inApplyTemplates = true;
|
|
4646
|
+
yield this.xsltChildNodes(templateContext, selection.selectedTemplate, output);
|
|
4647
|
+
} else {
|
|
4648
|
+
if (currentNode.childNodes && currentNode.childNodes.length > 0) {
|
|
4649
|
+
const grandchildNodes = currentNode.childNodes.filter((n) => n.nodeName !== "#dtd-section");
|
|
4650
|
+
if (grandchildNodes.length > 0) {
|
|
4651
|
+
const grandchildContext = context.clone(grandchildNodes);
|
|
4652
|
+
for (let k = 0; k < grandchildContext.contextSize(); ++k) {
|
|
4653
|
+
const grandchildNode = grandchildContext.nodeList[k];
|
|
4654
|
+
if (grandchildNode.nodeType === DOM_TEXT_NODE) {
|
|
4655
|
+
const textNodeContext = context.clone([grandchildNode], 0);
|
|
4656
|
+
this.commonLogicTextNode(textNodeContext, grandchildNode, output);
|
|
4657
|
+
} else {
|
|
4658
|
+
const grandchildClonedContext = grandchildContext.clone([grandchildNode], 0);
|
|
4659
|
+
const grandchildSelection = selectBestTemplate(
|
|
4660
|
+
expandedTemplates,
|
|
4661
|
+
grandchildClonedContext,
|
|
4662
|
+
this.matchResolver,
|
|
4663
|
+
this.xPath
|
|
4664
|
+
);
|
|
4665
|
+
if (grandchildSelection.selectedTemplate) {
|
|
4666
|
+
const grandchildTemplateContext = grandchildClonedContext.clone([grandchildNode], 0);
|
|
4667
|
+
grandchildTemplateContext.inApplyTemplates = true;
|
|
4668
|
+
yield this.xsltChildNodes(grandchildTemplateContext, grandchildSelection.selectedTemplate, output);
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4336
4679
|
}
|
|
4337
4680
|
}
|
|
4338
4681
|
});
|
|
4339
4682
|
}
|
|
4340
4683
|
xsltValueOf(context, template, output) {
|
|
4341
4684
|
const select = xmlGetAttribute(template, "select");
|
|
4342
|
-
const
|
|
4685
|
+
const current = context.nodeList[context.position];
|
|
4686
|
+
let attribute = this.xPath.xPathEval(select, context);
|
|
4687
|
+
if (current && current.nodeName === "#document" && (attribute.stringValue() === "" || attribute instanceof NodeSetValue && attribute.nodeSetValue().length === 0)) {
|
|
4688
|
+
const docChild = current.childNodes.find((c) => c.nodeName !== "#dtd-section");
|
|
4689
|
+
if (docChild) {
|
|
4690
|
+
const fallbackContext = context.clone([docChild], 0);
|
|
4691
|
+
attribute = this.xPath.xPathEval(select, fallbackContext);
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4343
4694
|
const value = attribute.stringValue();
|
|
4344
4695
|
const node = domCreateTextNode(this.outputDocument, value);
|
|
4345
4696
|
const targetOutput = output || this.outputDocument;
|