the tests ae passing

This commit is contained in:
David Bruant 2025-05-08 21:27:57 +02:00
parent 4672929480
commit 0e7c2ba959
4 changed files with 102 additions and 64 deletions

View File

@ -43,10 +43,10 @@ function findAllMatches(text, pattern) {
*/ */
function findCommonAncestor(node1, node2) { function findCommonAncestor(node1, node2) {
const ancestors1 = getAncestors(node1); const ancestors1 = getAncestors(node1);
const ancestors2 = getAncestors(node2); const ancestors2 = new Set(getAncestors(node2));
for(const ancestor of ancestors1) { for(const ancestor of ancestors1) {
if(ancestors2.includes(ancestor)) { if(ancestors2.has(ancestor)) {
return ancestor; return ancestor;
} }
} }
@ -98,19 +98,14 @@ function getNodeTextPosition(node, containerTextNodes) {
/** /**
* remove nodes between startNode and endNode * remove nodes between startNode and endNode
* but keep startNode and endNode * including startNode and endNode
*
* returns the common ancestor child in start branch
* for the purpose for inserting something between startNode and endNode
* with insertionPoint.parentNode.insertBefore(newBetweenContent, insertionPoint)
* *
* @param {Node} startNode * @param {Node} startNode
* @param {Node} endNode * @param {Node} endNode
* @returns {Node} * @param {string} text
* @returns {void}
*/ */
function removeNodesBetween(startNode, endNode) { function replaceBetweenNodesWithText(startNode, endNode, text) {
let nodesToRemove = new Set();
// find both ancestry branch // find both ancestry branch
const startNodeAncestors = new Set(getAncestors(startNode)) const startNodeAncestors = new Set(getAncestors(startNode))
const endNodeAncestors = new Set(getAncestors(endNode)) const endNodeAncestors = new Set(getAncestors(endNode))
@ -118,43 +113,42 @@ function removeNodesBetween(startNode, endNode) {
// find common ancestor // find common ancestor
const commonAncestor = findCommonAncestor(startNode, endNode) const commonAncestor = findCommonAncestor(startNode, endNode)
// remove everything "on the right" of start branch let remove = false
let currentAncestor = startNode let toRemove = []
let commonAncestorChildInEndNodeBranch let commonAncestorChild = commonAncestor.firstChild
let commonAncestorInsertionChild
while(currentAncestor !== commonAncestor){ while(commonAncestorChild){
let siblingToRemove = currentAncestor.nextSibling if(startNodeAncestors.has(commonAncestorChild)){
remove = true
while(siblingToRemove && !endNodeAncestors.has(siblingToRemove)){
nodesToRemove.add(siblingToRemove)
siblingToRemove = siblingToRemove.nextSibling
}
if(endNodeAncestors.has(siblingToRemove)){
commonAncestorChildInEndNodeBranch = siblingToRemove
} }
currentAncestor = currentAncestor.parentNode; if(remove){
toRemove.push(commonAncestorChild)
if(endNodeAncestors.has(commonAncestorChild)){
commonAncestorInsertionChild = commonAncestorChild.nextSibling
break;
}
}
commonAncestorChild = commonAncestorChild.nextSibling
} }
// remove everything "on the left" of end branch for(const node of toRemove){
currentAncestor = endNode commonAncestor.removeChild(node)
while(currentAncestor !== commonAncestor){
let siblingToRemove = currentAncestor.previousSibling
while(siblingToRemove && !startNodeAncestors.has(siblingToRemove)){
nodesToRemove.add(siblingToRemove)
siblingToRemove = siblingToRemove.previousSibling
} }
currentAncestor = currentAncestor.parentNode; //console.log('replaceBetweenNodesWithText startNode', startNode.textContent)
const newTextNode = commonAncestor.ownerDocument.createTextNode(text)
if(commonAncestorInsertionChild){
commonAncestor.insertBefore(newTextNode, commonAncestorInsertionChild)
}
else{
commonAncestor.appendChild(newTextNode)
} }
for(const node of nodesToRemove){
node.parentNode.removeChild(node)
}
return commonAncestorChildInEndNodeBranch
} }
/** /**
@ -202,8 +196,10 @@ function consolidateMarkers(document){
...findAllMatches(fullText, variableRegex) ...findAllMatches(fullText, variableRegex)
]; ];
/*if(positionedMarkers.length >= 1)
console.log('positionedMarkers', positionedMarkers)*/ //if(positionedMarkers.length >= 1)
// console.log('positionedMarkers', positionedMarkers)
while(consolidatedMarkers.length < positionedMarkers.length) { while(consolidatedMarkers.length < positionedMarkers.length) {
refreshContainerTextNodes() refreshContainerTextNodes()
@ -246,6 +242,18 @@ function consolidateMarkers(document){
// Check if marker spans multiple nodes // Check if marker spans multiple nodes
if(startNode !== endNode) { if(startNode !== endNode) {
const commonAncestor = findCommonAncestor(startNode, endNode)
let commonAncestorStartChild = startNode
while(commonAncestorStartChild.parentNode !== commonAncestor){
commonAncestorStartChild = commonAncestorStartChild.parentNode
}
let commonAncestorEndChild = endNode
while(commonAncestorEndChild.parentNode !== commonAncestor){
commonAncestorEndChild = commonAncestorEndChild.parentNode
}
// Calculate relative positions within the nodes // Calculate relative positions within the nodes
let startNodeTextContent = startNode.textContent || ''; let startNodeTextContent = startNode.textContent || '';
let endNodeTextContent = endNode.textContent || ''; let endNodeTextContent = endNode.textContent || '';
@ -256,47 +264,45 @@ function consolidateMarkers(document){
// Calculate the position within the end node // Calculate the position within the end node
let posInEndNode = (positionedMarker.index + positionedMarker.marker.length) - getNodeTextPosition(endNode, containerTextNodesInTreeOrder); let posInEndNode = (positionedMarker.index + positionedMarker.marker.length) - getNodeTextPosition(endNode, containerTextNodesInTreeOrder);
/** @type {Node} */ let newStartNode = startNode
let beforeStartNode = startNode
// if there is before-text, split // if there is before-text, split
if(posInStartNode > 0) { if(posInStartNode > 0) {
// Text exists before the marker - preserve it // Text exists before the marker - preserve it
// set newStartNode to a Text node containing only the marker beginning // set newStartNode to a Text node containing only the marker beginning
const newStartNode = startNode.splitText(posInStartNode) newStartNode = startNode.splitText(posInStartNode)
// startNode/beforeStartNode now contains only non-marker text // startNode/beforeStartNode now contains only non-marker text
// then, by definition of .splitText(posInStartNode): // then, by definition of .splitText(posInStartNode):
posInStartNode = 0 posInStartNode = 0
// remove the marker beginning part from the tree (since the marker will be inserted in full later) // move the marker beginning part to become a child of commonAncestor
newStartNode.parentNode?.removeChild(newStartNode) newStartNode.parentNode?.removeChild(newStartNode)
commonAncestor.insertBefore(newStartNode, commonAncestorStartChild.nextSibling)
} }
/** @type {Node} */
let afterEndNode
// if there is after-text, split // if there is after-text, split
if(posInEndNode < endNodeTextContent.length) { if(posInEndNode < endNodeTextContent.length) {
// Text exists after the marker - preserve it // Text exists after the marker - preserve it
// set afterEndNode to a Text node containing only non-marker text endNode.splitText(posInEndNode);
afterEndNode = endNode.splitText(posInEndNode);
// endNode now contains only the end of marker text // endNode now contains only the end of marker text
// then, by definition of .splitText(posInEndNode): // then, by definition of .splitText(posInEndNode):
posInEndNode = endNodeTextContent.length posInEndNode = endNodeTextContent.length
// remove the marker ending part from the tree (since the marker will be inserted in full later) // move the marker ending part to become a child of commonAncestor
if(endNode !== commonAncestorEndChild){
endNode.parentNode?.removeChild(endNode) endNode.parentNode?.removeChild(endNode)
commonAncestor.insertBefore(endNode, commonAncestorEndChild)
}
} }
// then, replace all nodes between (new)startNode and (new)endNode with a single textNode in commonAncestor // then, replace all nodes between (new)startNode and (new)endNode with a single textNode in commonAncestor
const insertionPoint = removeNodesBetween(beforeStartNode, afterEndNode) replaceBetweenNodesWithText(newStartNode, endNode, positionedMarker.marker)
const markerTextNode = insertionPoint.ownerDocument.createTextNode(positionedMarker.marker)
insertionPoint.parentNode.insertBefore(markerTextNode, insertionPoint)
// After consolidation, break as the DOM structure has changed // After consolidation, break as the DOM structure has changed
// and containerTextNodesInTreeOrder needs to be refreshed // and containerTextNodesInTreeOrder needs to be refreshed

View File

@ -5,6 +5,7 @@ import {getOdtTemplate} from '../../scripts/odf/odtTemplate-forNode.js'
import {fillOdtTemplate, getOdtTextContent} from '../../exports.js' import {fillOdtTemplate, getOdtTextContent} from '../../exports.js'
test('template filling with several layers of formatting in {#each ...} start marker', async t => { test('template filling with several layers of formatting in {#each ...} start marker', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/formatting-liste-nombres-plusieurs-couches.odt') const templatePath = join(import.meta.dirname, '../fixtures/formatting-liste-nombres-plusieurs-couches.odt')
const templateContent = `Liste de nombres const templateContent = `Liste de nombres
@ -86,7 +87,6 @@ Les nombres : 3 5 8 13  !!
}); });
test('template filling - {/each} and text after partially formatted', async t => { test('template filling - {/each} and text after partially formatted', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/formatting-liste-nombres-each-end-and-after-formatted.odt') const templatePath = join(import.meta.dirname, '../fixtures/formatting-liste-nombres-each-end-and-after-formatted.odt')
const templateContent = `Liste de nombres const templateContent = `Liste de nombres
@ -114,8 +114,6 @@ Les nombres : 5 8 13 21  !!
}); });
test('template filling - partially formatted variable', async t => { test('template filling - partially formatted variable', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/partially-formatted-variable.odt') const templatePath = join(import.meta.dirname, '../fixtures/partially-formatted-variable.odt')
const templateContent = `Nombre const templateContent = `Nombre
@ -129,9 +127,9 @@ Voici le nombre : {nombre} !!!
const templateTextContent = await getOdtTextContent(odtTemplate) const templateTextContent = await getOdtTextContent(odtTemplate)
t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template') t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template')
//try{
const odtResult = await fillOdtTemplate(odtTemplate, data) const odtResult = await fillOdtTemplate(odtTemplate, data)
//}catch(e){console.error(e)}
const odtResultTextContent = await getOdtTextContent(odtResult) const odtResultTextContent = await getOdtTextContent(odtResult)
t.deepEqual(odtResultTextContent, `Nombre t.deepEqual(odtResultTextContent, `Nombre
@ -139,3 +137,31 @@ Voici le nombre : 37 !!!
`) `)
}); });
test('template filling - formatted-start-each-single-paragraph', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/formatted-start-each-single-paragraph.odt')
const templateContent = `
{#each nombres as n}
{n}
{/each}
`
const data = {nombres : [37, 38, 39]}
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(), `
37
38
39
`.trim())
});

Binary file not shown.

View File

@ -2,7 +2,7 @@ import {writeFile} from 'node:fs/promises'
import {join} from 'node:path'; import {join} from 'node:path';
import {getOdtTemplate} from '../scripts/odf/odtTemplate-forNode.js' import {getOdtTemplate} from '../scripts/odf/odtTemplate-forNode.js'
import {fillOdtTemplate} from '../scripts/node.js' import {fillOdtTemplate} from '../exports.js'
/* /*
const templatePath = join(import.meta.dirname, '../tests/data/template-anniversaire.odt') const templatePath = join(import.meta.dirname, '../tests/data/template-anniversaire.odt')
@ -79,7 +79,7 @@ const data = {
} }
*/ */
/*
const templatePath = join(import.meta.dirname, '../tests/data/tableau-simple.odt') const templatePath = join(import.meta.dirname, '../tests/data/tableau-simple.odt')
const data = { const data = {
annéeConsos : [ annéeConsos : [
@ -91,7 +91,7 @@ const data = {
{ année: 2020, conso: 37859.246}, { année: 2020, conso: 37859.246},
] ]
} }
*/
/* /*
@ -102,6 +102,12 @@ const data = {
} }
*/ */
const templatePath = join(import.meta.dirname, '../tests/fixtures/partially-formatted-variable.odt')
const data = {nombre : 37}
const odtTemplate = await getOdtTemplate(templatePath) const odtTemplate = await getOdtTemplate(templatePath)
const odtResult = await fillOdtTemplate(odtTemplate, data) const odtResult = await fillOdtTemplate(odtTemplate, data)