@@ -125,7 +125,7 @@ export class Xslt {
125125 }
126126 }
127127
128- this . xsltProcessContext ( expressionContext , stylesheet , outputDocument ) ;
128+ this . xsltProcessContext ( expressionContext , stylesheet ) ;
129129 const transformedOutputXml = xmlTransformedText ( outputDocument , {
130130 cData : false ,
131131 escape : this . options . escape ,
@@ -136,11 +136,11 @@ export class Xslt {
136136
137137 /**
138138 * The main entry point of the XSL-T processor, as explained on the top of the file.
139- * @param context The input document root, as XPath ExprContext.
139+ * @param context The input document root, as XPath ` ExprContext` .
140140 * @param template The stylesheet document root, as DOM node.
141- * @param output the root of the generated output, as DOM node .
141+ * @param output If set, the output where the transformation should occur .
142142 */
143- protected xsltProcessContext ( context : ExprContext , template : XNode , output : XNode ) {
143+ protected xsltProcessContext ( context : ExprContext , template : XNode , output ? : XNode ) {
144144 if ( ! this . isXsltElement ( template ) ) {
145145 this . xsltPassThrough ( context , template , output ) ;
146146 } else {
@@ -228,10 +228,28 @@ export class Xslt {
228228 case 'attribute' :
229229 nameExpr = xmlGetAttribute ( template , 'name' ) ;
230230 name = this . xsltAttributeValue ( nameExpr , context ) ;
231- node = domCreateDocumentFragment ( this . outputDocument ) ;
232- this . xsltChildNodes ( context , template , node ) ;
233- value = xmlValue2 ( node ) ;
234- domSetTransformedAttribute ( output , name , value ) ;
231+ const documentFragment = domCreateDocumentFragment ( this . outputDocument ) ;
232+ this . xsltChildNodes ( context , template , documentFragment ) ;
233+ value = xmlValue2 ( documentFragment ) ;
234+ if ( output !== null && output !== undefined ) {
235+ domSetTransformedAttribute ( output , name , value ) ;
236+ } else {
237+ let outputNode = context . outputNodeList [ context . outputPosition ] ;
238+ // Corner case:
239+ // It can happen here that we don't have the root node set.
240+ // In this case we need to append a copy of the root
241+ // source node to receive the attribute.
242+ if ( outputNode . localName === "#document" ) {
243+ const sourceRootNode = context . root . childNodes [ 0 ] ;
244+ const newRootNode = domCreateElement ( this . outputDocument , sourceRootNode . nodeName ) ;
245+ newRootNode . transformedNodeName = sourceRootNode . nodeName ;
246+ newRootNode . transformedLocalName = sourceRootNode . localName ;
247+ domAppendTransformedChild ( outputNode , newRootNode ) ;
248+ outputNode = newRootNode ;
249+ }
250+ domSetTransformedAttribute ( outputNode , name , value ) ;
251+ }
252+
235253 break ;
236254 case 'attribute-set' :
237255 throw new Error ( `not implemented: ${ template . localName } ` ) ;
@@ -265,22 +283,24 @@ export class Xslt {
265283 output . appendChild ( commentNode ) ;
266284 break ;
267285 case 'copy' :
268- node = this . xsltCopy ( output , context . nodeList [ context . position ] ) ;
286+ const destinationCopyNode = output || context . outputNodeList [ context . outputPosition ] ;
287+ node = this . xsltCopy ( destinationCopyNode , context . nodeList [ context . position ] ) ;
269288 if ( node ) {
270289 this . xsltChildNodes ( context , template , node ) ;
271290 }
272291 break ;
273292 case 'copy-of' :
274293 select = xmlGetAttribute ( template , 'select' ) ;
275294 value = this . xPath . xPathEval ( select , context ) ;
295+ const destinationNode = output || context . outputNodeList [ context . outputPosition ] ;
276296 if ( value . type === 'node-set' ) {
277297 nodes = value . nodeSetValue ( ) ;
278298 for ( let i = 0 ; i < nodes . length ; ++ i ) {
279- this . xsltCopyOf ( output , nodes [ i ] ) ;
299+ this . xsltCopyOf ( destinationNode , nodes [ i ] ) ;
280300 }
281301 } else {
282302 let node = domCreateTextNode ( this . outputDocument , value . stringValue ( ) ) ;
283- domAppendChild ( output , node ) ;
303+ domAppendChild ( destinationNode , node ) ;
284304 }
285305 break ;
286306 case 'decimal-format' :
@@ -397,15 +417,21 @@ export class Xslt {
397417 if ( disableOutputEscaping . length > 0 && disableOutputEscaping [ 0 ] . nodeValue === 'yes' ) {
398418 node . escape = false ;
399419 }
400- output . appendTransformedChild ( node ) ;
420+ const destinationTextNode = output || context . outputNodeList [ context . outputPosition ] ;
421+ destinationTextNode . appendTransformedChild ( node ) ;
401422 break ;
402423 case 'value-of' :
403424 select = xmlGetAttribute ( template , 'select' ) ;
404425 const attribute = this . xPath . xPathEval ( select , context ) ;
405426 value = attribute . stringValue ( ) ;
406427 node = domCreateTransformedTextNode ( this . outputDocument , value ) ;
407428 node . siblingPosition = context . nodeList [ context . position ] . siblingPosition ;
408- context . outputNodeList [ context . outputPosition ] . appendTransformedChild ( node ) ;
429+ if ( output !== null && output !== undefined ) {
430+ output . appendTransformedChild ( node ) ;
431+ } else {
432+ context . outputNodeList [ context . outputPosition ] . appendTransformedChild ( node ) ;
433+ }
434+
409435 break ;
410436 case 'variable' :
411437 this . xsltVariable ( context , template , true ) ;
@@ -483,25 +509,25 @@ export class Xslt {
483509 * Implements `xsl:copy-of` for node-set values of the select
484510 * expression. Recurses down the source node tree, which is part of
485511 * the input document.
486- * @param {XNode } dst the node being copied to, part of output document.
487- * @param {XNode } src the node being copied, part in input document.
512+ * @param {XNode } destination the node being copied to, part of output document.
513+ * @param {XNode } source the node being copied, part in input document.
488514 */
489- protected xsltCopyOf ( dst : XNode , src : XNode ) : void {
490- if ( src . nodeType == DOM_DOCUMENT_FRAGMENT_NODE || src . nodeType == DOM_DOCUMENT_NODE ) {
491- for ( let i = 0 ; i < src . childNodes . length ; ++ i ) {
492- this . xsltCopyOf ( dst , src . childNodes [ i ] ) ;
515+ protected xsltCopyOf ( destination : XNode , source : XNode ) : void {
516+ if ( source . nodeType == DOM_DOCUMENT_FRAGMENT_NODE || source . nodeType == DOM_DOCUMENT_NODE ) {
517+ for ( let i = 0 ; i < source . childNodes . length ; ++ i ) {
518+ this . xsltCopyOf ( destination , source . childNodes [ i ] ) ;
493519 }
494520 } else {
495- const node = this . xsltCopy ( dst , src ) ;
521+ const node = this . xsltCopy ( destination , source ) ;
496522 if ( node ) {
497523 // This was an element node -- recurse to attributes and
498524 // children.
499- for ( let i = 0 ; i < src . attributes . length ; ++ i ) {
500- this . xsltCopyOf ( node , src . attributes [ i ] ) ;
525+ for ( let i = 0 ; i < source . attributes . length ; ++ i ) {
526+ this . xsltCopyOf ( node , source . attributes [ i ] ) ;
501527 }
502528
503- for ( let i = 0 ; i < src . childNodes . length ; ++ i ) {
504- this . xsltCopyOf ( node , src . childNodes [ i ] ) ;
529+ for ( let i = 0 ; i < source . childNodes . length ; ++ i ) {
530+ this . xsltCopyOf ( node , source . childNodes [ i ] ) ;
505531 }
506532 }
507533 }
@@ -633,9 +659,9 @@ export class Xslt {
633659 * current template node.
634660 * @param context Normally the Expression Context.
635661 * @param template The XSL-T definition.
636- * @param output The XML output.
662+ * @param output If set, the output where the transformation should occur .
637663 */
638- protected xsltChildNodes ( context : ExprContext , template : XNode , output : XNode ) {
664+ protected xsltChildNodes ( context : ExprContext , template : XNode , output ? : XNode ) {
639665 // Clone input context to keep variables declared here local to the
640666 // siblings of the children.
641667 const contextClone = context . clone ( ) ;
@@ -720,7 +746,7 @@ export class Xslt {
720746 outputNode . transformedChildNodes . length - 1 ,
721747 ++ elementContext . outputDepth
722748 ) ;
723- this . xsltChildNodes ( clonedContext , template , node ) ;
749+ this . xsltChildNodes ( clonedContext , template ) ;
724750 } else {
725751 // This applies also to the DOCUMENT_NODE of the XSL stylesheet,
726752 // so we don't have to treat it specially.
0 commit comments