diff --git a/scripts/odf/fillOdtTemplate.js b/scripts/odf/fillOdtTemplate.js index 9d5c1e5..0c4a744 100644 --- a/scripts/odf/fillOdtTemplate.js +++ b/scripts/odf/fillOdtTemplate.js @@ -293,6 +293,15 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c const IF = 'IF' const EACH = 'EACH' +// the regexps below are shared, so they shoudn't have state (no 'g' flag) +const ifStartRegex = /{#if\s+([^}]+?)\s*}/; +const elseMarker = '{:else}' +const closingIfMarker = '{/if}' + +const eachStartMarkerRegex = /{#each\s+([^}]+?)\s+as\s+([^}]+?)\s*}/; +const eachClosingBlockString = '{/each}' + + /** * @@ -334,8 +343,7 @@ function fillTemplatedOdtElement(rootElement, compartment){ /** * looking for {#each x as y} */ - const eachStartRegex = /{#each\s+([^}]+?)\s+as\s+([^}]+?)\s*}/; - const eachStartMatch = text.match(eachStartRegex); + const eachStartMatch = text.match(eachStartMarkerRegex); if(eachStartMatch){ //console.log('startMatch', startMatch) @@ -358,7 +366,6 @@ function fillTemplatedOdtElement(rootElement, compartment){ /** * Looking for {/each} */ - const eachClosingBlockString = '{/each}' const isEachClosingBlock = text.includes(eachClosingBlockString) if(isEachClosingBlock){ @@ -394,7 +401,6 @@ function fillTemplatedOdtElement(rootElement, compartment){ /** * Looking for {#if ...} */ - const ifStartRegex = /{#if\s+([^}]+?)\s*}/; const ifStartMatch = text.match(ifStartRegex); if(ifStartMatch){ @@ -415,7 +421,6 @@ function fillTemplatedOdtElement(rootElement, compartment){ /** * Looking for {:else} */ - const elseMarker = '{:else}' const hasElseMarker = text.includes(elseMarker); if(hasElseMarker){ @@ -438,7 +443,6 @@ function fillTemplatedOdtElement(rootElement, compartment){ /** * Looking for {/if} */ - const closingIfMarker = '{/if}' const hasClosingMarker = text.includes(closingIfMarker); if(hasClosingMarker){ @@ -529,28 +533,42 @@ function fillTemplatedOdtDocument(document, compartment){ let remainingText = currentNode.textContent || '' while(remainingText.length >= 1){ - let match; + let matchText; + let matchIndex; - // looking for opening {#each ...} block - const eachBlockOpeningRegex = /{#each\s+([^}]+?)\s+as\s+([^}]+?)\s*}/; - const eachBlockClosingRegex = /{\/each}/; + // looking for a block marker + for(const marker of [ifStartRegex, elseMarker, closingIfMarker, eachStartMarkerRegex, eachClosingBlockString]){ + if(typeof marker === 'string'){ + const index = remainingText.indexOf(marker) + + if(index !== -1){ + matchText = marker + matchIndex = index - for(const regexp of [eachBlockOpeningRegex, eachBlockClosingRegex]){ - let thisMatch = remainingText.match(regexp) + // found the first match + break; // get out of loop + } + } + else{ + // marker is a RegExp + const match = remainingText.match(marker) - // trying to find only the first match in remainingText string - // @ts-ignore - if(thisMatch && (!match || match.index > thisMatch.index)){ - match = thisMatch + if(match){ + matchText = match[0] + matchIndex = match.index + + // found the first match + break; // get out of loop + } } } - if(match){ + if(matchText){ // split 3-way : before-match, match and after-match - if(match[0].length < remainingText.length){ + if(matchText.length < remainingText.length){ // @ts-ignore - let afterMatchTextNode = currentNode.splitText(match.index + match[0].length) + let afterMatchTextNode = currentNode.splitText(matchIndex + matchText.length) if(afterMatchTextNode.textContent && afterMatchTextNode.textContent.length >= 1){ remainingText = afterMatchTextNode.textContent } @@ -561,9 +579,9 @@ function fillTemplatedOdtDocument(document, compartment){ // per spec, currentNode now contains before-match and match text // @ts-ignore - if(match.index > 0){ + if(matchIndex > 0){ // @ts-ignore - currentNode.splitText(match.index) + currentNode.splitText(matchIndex) } if(afterMatchTextNode){ diff --git a/tests/fill-odt-template/each.js b/tests/fill-odt-template/each.js index c39de73..8c9cc43 100644 --- a/tests/fill-odt-template/each.js +++ b/tests/fill-odt-template/each.js @@ -247,33 +247,6 @@ Hiver }); -test('template filling {#each ...}{/each} within a single text node', async t => { - const templatePath = join(import.meta.dirname, '../fixtures/liste-nombres.odt') - const templateContent = `Liste de nombres - -Les nombres : {#each nombres as n}{n} {/each} !! -` - - const data = { - nombres : [1,1,2,3,5,8,13,21] - } - - const odtTemplate = await getOdtTemplate(templatePath) - - const templateTextContent = await getOdtTextContent(odtTemplate) - t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template') - - const odtResult = await fillOdtTemplate(odtTemplate, data) - - const odtResultTextContent = await getOdtTextContent(odtResult) - t.deepEqual(odtResultTextContent, `Liste de nombres - -Les nombres : 1 1 2 3 5 8 13 21  !! -`) - -}); - - test('template filling of a table', async t => { const templatePath = join(import.meta.dirname, '../fixtures/tableau-simple.odt') const templateContent = `Évolution énergie en kWh par personne en France diff --git a/tests/fill-odt-template/in-text-node.js b/tests/fill-odt-template/in-text-node.js new file mode 100644 index 0000000..1928d3f --- /dev/null +++ b/tests/fill-odt-template/in-text-node.js @@ -0,0 +1,65 @@ +import test from 'ava'; +import {join} from 'node:path'; + +import {getOdtTemplate} from '../../scripts/odf/odtTemplate-forNode.js' + +import {fillOdtTemplate, getOdtTextContent} from '../../exports.js' + + +test('template filling {#if ...}{/if} within a single text node', async t => { + const templatePath = join(import.meta.dirname, '../fixtures/inline-if-nombres.odt') + const templateContent = `Taille de nombre + +Le nombre {n} est {#if n<5}petit{:else}grand{/if}. +` + + const odtTemplate = await getOdtTemplate(templatePath) + + const templateTextContent = await getOdtTextContent(odtTemplate) + t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template') + + const odtResult3 = await fillOdtTemplate(odtTemplate, {n : 3}) + + const odtResult3TextContent = await getOdtTextContent(odtResult3) + t.deepEqual(odtResult3TextContent, `Taille de nombre + +Le nombre 3 est petit. +`) + + const odtResult9 = await fillOdtTemplate(odtTemplate, {n : 9}) + + const odtResult9TextContent = await getOdtTextContent(odtResult9) + t.deepEqual(odtResult9TextContent, `Taille de nombre + +Le nombre 9 est grand. +`) + +}); + + +test('template filling {#each ...}{/each} within a single text node', async t => { + const templatePath = join(import.meta.dirname, '../fixtures/liste-nombres.odt') + const templateContent = `Liste de nombres + +Les nombres : {#each nombres as n}{n} {/each} !! +` + + const data = { + nombres : [1,1,2,3,5,8,13,21] + } + + const odtTemplate = await getOdtTemplate(templatePath) + + const templateTextContent = await getOdtTextContent(odtTemplate) + t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template') + + const odtResult = await fillOdtTemplate(odtTemplate, data) + + const odtResultTextContent = await getOdtTextContent(odtResult) + t.deepEqual(odtResultTextContent, `Liste de nombres + +Les nombres : 1 1 2 3 5 8 13 21  !! +`) + +}); + diff --git a/tests/fixtures/inline-if-nombres.odt b/tests/fixtures/inline-if-nombres.odt new file mode 100644 index 0000000..66559cd Binary files /dev/null and b/tests/fixtures/inline-if-nombres.odt differ