diff --git a/.gitignore b/.gitignore index 08d407f..5bd2365 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ node_modules/ build/* .~lock* +**/*(Copie)* +**/*(Copy)* stats.html \ No newline at end of file diff --git a/scripts/odf/templating/fillOdtElementTemplate.js b/scripts/odf/templating/fillOdtElementTemplate.js index 723398e..a23043c 100644 --- a/scripts/odf/templating/fillOdtElementTemplate.js +++ b/scripts/odf/templating/fillOdtElementTemplate.js @@ -85,6 +85,7 @@ function findPlacesToFillInString(str, compartment) { * @returns {{startChild: Node, endChild:Node, content: DocumentFragment}} */ function extractBlockContent(blockStartNode, blockEndNode) { + //console.log('[extractBlockContent] blockStartNode', blockStartNode.textContent) //console.log('[extractBlockContent] blockEndNode', blockEndNode.textContent) // find common ancestor of blockStartNode and blockEndNode @@ -93,6 +94,7 @@ function extractBlockContent(blockStartNode, blockEndNode) { let startAncestor = blockStartNode let endAncestor = blockEndNode + // ancestries in order of deepest first, closest to root last const startAncestry = new Set([startAncestor]) const endAncestry = new Set([endAncestor]) @@ -117,9 +119,11 @@ function extractBlockContent(blockStartNode, blockEndNode) { const startAncestryToCommonAncestor = [...startAncestry].slice(0, [...startAncestry].indexOf(commonAncestor)) const endAncestryToCommonAncestor = [...endAncestry].slice(0, [...endAncestry].indexOf(commonAncestor)) + // direct children of commonAncestor in the branch or blockStartNode and blockEndNode respectively const startChild = startAncestryToCommonAncestor.at(-1) const endChild = endAncestryToCommonAncestor.at(-1) + //console.log('[extractBlockContent] startChild', startChild.textContent) //console.log('[extractBlockContent] endChild', endChild.textContent) // Extract DOM content in a documentFragment @@ -127,15 +131,57 @@ function extractBlockContent(blockStartNode, blockEndNode) { const contentFragment = blockStartNode.ownerDocument.createDocumentFragment() /** @type {Element[]} */ - const repeatedPatternArray = [] + const blockContent = [] + + // get start branch "right" content + for(const startAncestor of startAncestryToCommonAncestor){ + if(startAncestor === startChild) + break; + + let sibling = startAncestor.nextSibling + + while(sibling) { + blockContent.push(sibling) + sibling = sibling.nextSibling; + } + } + + let sibling = startChild.nextSibling while(sibling !== endChild) { - repeatedPatternArray.push(sibling) + blockContent.push(sibling) sibling = sibling.nextSibling; } - for(const sibling of repeatedPatternArray) { + + // get end branch "left" content + for(const endAncestor of [...endAncestryToCommonAncestor].reverse()){ + if(endAncestor === endChild) + continue; // already taken care of + + let sibling = endAncestor.previousSibling + + const reversedBlockContentContribution = [] + + while(sibling) { + reversedBlockContentContribution.push(sibling) + sibling = sibling.previousSibling; + } + + const blockContentContribution = reversedBlockContentContribution.reverse() + + blockContent.push(...blockContentContribution) + + if(endAncestor === blockEndNode) + break; + } + + + //console.log('blockContent', blockContent.map(n => n.textContent)) + + + for(const sibling of blockContent) { sibling.parentNode?.removeChild(sibling) contentFragment.appendChild(sibling) } @@ -206,7 +252,6 @@ function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, .add(startIfThenChild).add(endIfThenChild) } - if(chosenFragment) { fillOdtElementTemplate( chosenFragment, @@ -340,7 +385,8 @@ const EACH = eachStartMarkerRegex.source * @returns {void} */ export default function fillOdtElementTemplate(rootElement, compartment) { - //console.log('fillTemplatedOdtElement', rootElement.nodeType, rootElement.nodeName) + //console.log('fillTemplatedOdtElement', rootElement.nodeType, rootElement.nodeName, rootElement.textContent) + //console.log('fillTemplatedOdtElement', rootElement.childNodes[0].childNodes.length) let currentlyOpenBlocks = [] @@ -362,9 +408,11 @@ export default function fillOdtElementTemplate(rootElement, compartment) { let ifBlockConditionExpression // Traverse "in document order" + // @ts-ignore traverse(rootElement, currentNode => { - //console.log('currentlyUnclosedBlocks', currentlyUnclosedBlocks) + //console.log('currentlyOpenBlocks', currentlyOpenBlocks) + const insideAnOpenBlock = currentlyOpenBlocks.length >= 1 if(currentNode.nodeType === Node.TEXT_NODE) { @@ -473,9 +521,9 @@ export default function fillOdtElementTemplate(rootElement, compartment) { /** * Looking for {/if} */ - const hasClosingMarker = text.includes(closingIfMarker); + const ifClosingMarker = text.includes(closingIfMarker); - if(hasClosingMarker) { + if(ifClosingMarker) { if(!insideAnOpenBlock) throw new Error('{/if} without a corresponding {#if}') @@ -498,6 +546,8 @@ export default function fillOdtElementTemplate(rootElement, compartment) { else { // do nothing because the marker is too deep } + + currentlyOpenBlocks.pop() } diff --git a/scripts/odf/templating/markers.js b/scripts/odf/templating/markers.js index 1a09f1e..f2cceb5 100644 --- a/scripts/odf/templating/markers.js +++ b/scripts/odf/templating/markers.js @@ -1,5 +1,5 @@ // the regexps below are shared, so they shoudn't have state (no 'g' flag) -export const variableRegex = /\{([^{#\/]+?)\}/ +export const variableRegex = /\{([^{#\/:]+?)\}/ export const ifStartMarkerRegex = /{#if\s+([^}]+?)\s*}/; export const elseMarker = '{:else}' diff --git a/scripts/odf/templating/prepareTemplateDOMTree.js b/scripts/odf/templating/prepareTemplateDOMTree.js index 698e823..8c75d9e 100644 --- a/scripts/odf/templating/prepareTemplateDOMTree.js +++ b/scripts/odf/templating/prepareTemplateDOMTree.js @@ -3,6 +3,7 @@ import {traverse, Node} from "../../DOMUtils.js"; import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js' + /** * * @param {string} text @@ -166,6 +167,7 @@ function consolidateMarkers(document){ ] for(const potentialMarkersContainer of potentialMarkersContainers) { + /** @type {{marker: string, index: number}[]} */ const consolidatedMarkers = [] /** @type {Text[]} */ @@ -244,13 +246,16 @@ function consolidateMarkers(document){ // Check if marker spans multiple nodes if(startNode !== endNode) { + //console.log('startNode !== endNode', startNode.textContent, endNode.textContent) const commonAncestor = findCommonAncestor(startNode, endNode) + /** @type {Node} */ let commonAncestorStartChild = startNode while(commonAncestorStartChild.parentNode !== commonAncestor){ commonAncestorStartChild = commonAncestorStartChild.parentNode } + /** @type {Node} */ let commonAncestorEndChild = endNode while(commonAncestorEndChild.parentNode !== commonAncestor){ commonAncestorEndChild = commonAncestorEndChild.parentNode @@ -322,6 +327,7 @@ function consolidateMarkers(document){ } } } + } /** @@ -551,5 +557,4 @@ export default function prepareTemplateDOMTree(document){ isolateMarkerText(document) // after isolateMarkerText, each marker is in exactly one text node // (markers are separated from text that was before or after in the same text node) - } \ No newline at end of file diff --git a/tests/fill-odt-template/each.js b/tests/fill-odt-template/each.js index f0ede37..515e57b 100644 --- a/tests/fill-odt-template/each.js +++ b/tests/fill-odt-template/each.js @@ -327,16 +327,22 @@ Année Année Énergie par personne + 1970 36252.637 + 1980 43328.78 + 1990 46971.94 + 2000 53147.277 + 2010 48062.32 + 2020 37859.246 `.trim()) diff --git a/tests/fill-odt-template/if.js b/tests/fill-odt-template/if.js index 729f0ca..c934313 100644 --- a/tests/fill-odt-template/if.js +++ b/tests/fill-odt-template/if.js @@ -40,3 +40,30 @@ n est un grand nombre }); + +test('weird bug', async t => { + const templatePath = join(import.meta.dirname, '../fixtures/left-branch-content-and-two-consecutive-ifs.odt') + const templateContent = `Utilisation de sources lumineuses : {#if scientifique.source_lumineuses}Oui{:else}Non{/if} +{#if scientifique.source_lumineuses && scientifique.modalités_source_lumineuses } +Modalités d’utilisation de sources lumineuses : {scientifique.modalités_source_lumineuses} +{/if} +` + + const data = { + scientifique: { + source_lumineuses: false, + //modalités_source_lumineuses: 'lampes torches' + } + } + + + const odtTemplate = await getOdtTemplate(templatePath) + const templateTextContent = await getOdtTextContent(odtTemplate) + t.deepEqual(templateTextContent.trim(), templateContent.trim(), 'reconnaissance du template') + + const odtResult = await fillOdtTemplate(odtTemplate, data) + const odtResultTextContent = await getOdtTextContent(odtResult) + t.deepEqual(odtResultTextContent.trim(), `Utilisation de sources lumineuses : Non`) + +}); + diff --git a/tests/fixtures/left-branch-content-and-two-consecutive-ifs.odt b/tests/fixtures/left-branch-content-and-two-consecutive-ifs.odt new file mode 100644 index 0000000..0630676 Binary files /dev/null and b/tests/fixtures/left-branch-content-and-two-consecutive-ifs.odt differ