@@ -262,8 +262,10 @@ export class Xslt {
262262 node . transformedNodeName = name ;
263263
264264 domAppendTransformedChild ( context . outputNodeList [ context . outputPosition ] , node ) ;
265+ // The element becomes the output node of the source node.
266+ context . nodeList [ context . position ] . outputNode = node ;
265267 const clonedContext = context . clone ( undefined , [ node ] , undefined , 0 ) ;
266- await this . xsltChildNodes ( clonedContext , template , node ) ;
268+ await this . xsltChildNodes ( clonedContext , template ) ;
267269 break ;
268270 case 'fallback' :
269271 throw new Error ( `not implemented: ${ template . localName } ` ) ;
@@ -353,7 +355,7 @@ export class Xslt {
353355 node = domCreateTransformedTextNode ( this . outputDocument , value ) ;
354356 node . siblingPosition = context . nodeList [ context . position ] . siblingPosition ;
355357
356- if ( output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
358+ if ( output && output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
357359 output . appendTransformedChild ( node ) ;
358360 } else {
359361 context . outputNodeList [ context . outputPosition ] . appendTransformedChild ( node ) ;
@@ -471,7 +473,7 @@ export class Xslt {
471473 await this . xsltChildNodes ( context , template , documentFragment ) ;
472474 const value = xmlValue2 ( documentFragment ) ;
473475
474- if ( output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
476+ if ( output && output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
475477 domSetTransformedAttribute ( output , name , value ) ;
476478 } else {
477479 let sourceNode = context . nodeList [ context . position ] ;
@@ -507,15 +509,19 @@ export class Xslt {
507509
508510 // If the parent transformation is something like `xsl:element`, we should
509511 // add a copy of the attribute to this element.
510- domSetTransformedAttribute ( output , name , value ) ;
512+ domSetTransformedAttribute ( outputNode , name , value ) ;
513+
514+ if ( sourceNode . nodeType === DOM_ATTRIBUTE_NODE ) {
515+ sourceNode . transformedNodeType = DOM_ATTRIBUTE_NODE ;
516+ sourceNode . transformedNodeName = name ;
517+ sourceNode . transformedNodeValue = value ;
518+ }
511519
512520 // Some operations start by the tag attributes, and not by the tag itself.
513521 // When this is the case, the output node is not set yet, so
514522 // we add the transformed attributes into the original tag.
515523 if ( parentSourceNode && parentSourceNode . outputNode ) {
516524 domSetTransformedAttribute ( parentSourceNode . outputNode , name , value ) ;
517- } else {
518- domSetTransformedAttribute ( parentSourceNode , name , value ) ;
519525 }
520526 }
521527 }
@@ -527,7 +533,7 @@ export class Xslt {
527533 * @param template The template.
528534 * @param output The output. Only used if there's no corresponding output node already defined.
529535 */
530- protected async xsltChoose ( context : ExprContext , template : XNode , output : XNode ) {
536+ protected async xsltChoose ( context : ExprContext , template : XNode , output ? : XNode ) {
531537 for ( const childNode of template . childNodes ) {
532538 if ( childNode . nodeType !== DOM_ELEMENT_NODE ) {
533539 continue ;
@@ -536,13 +542,11 @@ export class Xslt {
536542 if ( this . isXsltElement ( childNode , 'when' ) ) {
537543 const test = xmlGetAttribute ( childNode , 'test' ) ;
538544 if ( this . xPath . xPathEval ( test , context ) . booleanValue ( ) ) {
539- const outputNode = context . outputNodeList [ context . outputPosition ] || output ;
540- await this . xsltChildNodes ( context , childNode , outputNode ) ;
545+ await this . xsltChildNodes ( context , childNode , output ) ;
541546 break ;
542547 }
543548 } else if ( this . isXsltElement ( childNode , 'otherwise' ) ) {
544- const outputNode = context . outputNodeList [ context . outputPosition ] || output ;
545- await this . xsltChildNodes ( context , childNode , outputNode ) ;
549+ await this . xsltChildNodes ( context , childNode , output ) ;
546550 break ;
547551 }
548552 }
@@ -740,9 +744,9 @@ export class Xslt {
740744
741745 const nonAttributeChildren = template . childNodes . filter ( ( n ) => n . nodeType !== DOM_ATTRIBUTE_NODE ) ;
742746 if ( nonAttributeChildren . length > 0 ) {
743- const root = domCreateDocumentFragment ( template . ownerDocument ) ;
744- await this . xsltChildNodes ( context , template , root ) ;
745- value = new NodeSetValue ( [ root ] ) ;
747+ const fragment = domCreateDocumentFragment ( template . ownerDocument ) ;
748+ await this . xsltChildNodes ( context , template , fragment ) ;
749+ value = new NodeSetValue ( [ fragment ] ) ;
746750 } else if ( select ) {
747751 value = this . xPath . xPathEval ( select , context ) ;
748752 } else {
@@ -785,7 +789,7 @@ export class Xslt {
785789 * @param output The output.
786790 */
787791 private commonLogicTextNode ( context : ExprContext , template : XNode , output : XNode ) {
788- if ( output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
792+ if ( output && output . nodeType === DOM_DOCUMENT_FRAGMENT_NODE ) {
789793 let node = domCreateTransformedTextNode ( this . outputDocument , template . nodeValue ) ;
790794 domAppendTransformedChild ( output , node ) ;
791795 } else {
@@ -843,7 +847,16 @@ export class Xslt {
843847 newNode . transformedLocalName = template . localName ;
844848
845849 // The node can have transformed attributes from previous transformations.
846- const transformedAttributes = node . transformedChildNodes . filter ( ( n ) => n . nodeType === DOM_ATTRIBUTE_NODE ) ;
850+ // Case 1: attributes that were created by a transformation without a source attribute.
851+ const transformedChildNodes = node . transformedChildNodes . filter ( ( n ) => n . nodeType === DOM_ATTRIBUTE_NODE ) ;
852+ for ( const previouslyTransformedAttribute of transformedChildNodes ) {
853+ const name = previouslyTransformedAttribute . transformedNodeName ;
854+ const value = previouslyTransformedAttribute . transformedNodeValue ;
855+ domSetTransformedAttribute ( newNode , name , value ) ;
856+ }
857+
858+ // Case 2: attributes that existed as a source attribute and were transformed.
859+ const transformedAttributes = node . childNodes . filter ( ( n ) => n . nodeType === DOM_ATTRIBUTE_NODE && n . transformedNodeName )
847860 for ( const previouslyTransformedAttribute of transformedAttributes ) {
848861 const name = previouslyTransformedAttribute . transformedNodeName ;
849862 const value = previouslyTransformedAttribute . transformedNodeValue ;
@@ -864,7 +877,7 @@ export class Xslt {
864877 outputNode . transformedChildNodes . length - 1 ,
865878 ++ elementContext . outputDepth
866879 ) ;
867- await this . xsltChildNodes ( clonedContext , template , output ) ;
880+ await this . xsltChildNodes ( clonedContext , template ) ;
868881 } else {
869882 // This applies also to the DOCUMENT_NODE of the XSL stylesheet,
870883 // so we don't have to treat it specially.
0 commit comments