beginning of refactoring - if tests passing
This commit is contained in:
parent
a1e99b4519
commit
1181356953
@ -42,6 +42,51 @@ export function traverse(node, visit) {
|
|||||||
visit(node);
|
visit(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Node} node1
|
||||||
|
* @param {Node} node2
|
||||||
|
* @returns {Node}
|
||||||
|
*/
|
||||||
|
export function findCommonAncestor(node1, node2) {
|
||||||
|
const ancestors1 = getAncestors(node1);
|
||||||
|
const ancestors2 = new Set(getAncestors(node2));
|
||||||
|
|
||||||
|
for(const ancestor of ancestors1) {
|
||||||
|
if(ancestors2.has(ancestor)) {
|
||||||
|
return ancestor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`node1 and node2 do not have a common ancestor`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns ancestors youngest first, oldest last
|
||||||
|
*
|
||||||
|
* @param {Node} node
|
||||||
|
* @param {Node} [until]
|
||||||
|
* @returns {Node[]}
|
||||||
|
*/
|
||||||
|
export function getAncestors(node, until = undefined) {
|
||||||
|
const ancestors = [];
|
||||||
|
let current = node;
|
||||||
|
|
||||||
|
while(current && current !== until) {
|
||||||
|
ancestors.push(current);
|
||||||
|
current = current.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(current === until){
|
||||||
|
ancestors.push(until);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ancestors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DOMParser,
|
DOMParser,
|
||||||
XMLSerializer,
|
XMLSerializer,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {traverse, Node} from '../../DOMUtils.js'
|
import {traverse, Node, getAncestors, findCommonAncestor} from "../../DOMUtils.js";
|
||||||
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js'
|
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,6 +7,181 @@ import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, if
|
|||||||
* @property {() => void} fill
|
* @property {() => void} fill
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class TemplateDOMBranch{
|
||||||
|
/** @type {Node} */
|
||||||
|
#startNode
|
||||||
|
|
||||||
|
/** @type {Node} */
|
||||||
|
#leafNode
|
||||||
|
|
||||||
|
// ancestors with this.#ancestors[0] === this.#startNode and this.#ancestors.at(-1) === this.#leafNode
|
||||||
|
/** @type {Node[]} */
|
||||||
|
#ancestors
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Node} startNode
|
||||||
|
* @param {Node} leafNode
|
||||||
|
*/
|
||||||
|
constructor(startNode, leafNode){
|
||||||
|
this.#startNode = startNode
|
||||||
|
this.#leafNode = leafNode
|
||||||
|
|
||||||
|
this.#ancestors = getAncestors(this.#leafNode, this.#startNode).reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} n
|
||||||
|
* @returns {Node | undefined}
|
||||||
|
*/
|
||||||
|
at(n){
|
||||||
|
return this.#ancestors.at(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeLeafAndEmptyAncestors(){
|
||||||
|
// it may happen (else marker of if/else/endif) that the leaf was already removed as part of another block
|
||||||
|
// so before removing anything, let's update #ancestors and #leaf
|
||||||
|
|
||||||
|
this.#ancestors.every((ancestor, i) => {
|
||||||
|
if(!ancestor.parentNode){
|
||||||
|
// ancestor already removed from tree
|
||||||
|
this.#ancestors = this.#ancestors.slice(0, i)
|
||||||
|
this.#leafNode = this.#ancestors.at(-1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true // continue
|
||||||
|
})
|
||||||
|
|
||||||
|
//console.log('removeLeafAndEmptyAncestors', this.#startNode.textContent)
|
||||||
|
let nextLeaf = this.#leafNode.parentNode
|
||||||
|
//console.log('nextLeaf', !!nextLeaf)
|
||||||
|
nextLeaf.removeChild(this.#leafNode)
|
||||||
|
this.#leafNode = nextLeaf
|
||||||
|
|
||||||
|
while(this.#leafNode !== this.#startNode &&
|
||||||
|
this.#leafNode.textContent && this.#leafNode.textContent.trim() === '')
|
||||||
|
{
|
||||||
|
nextLeaf = this.#leafNode.parentNode
|
||||||
|
this.#leafNode.parentNode.removeChild(this.#leafNode)
|
||||||
|
this.#leafNode = nextLeaf
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#ancestors = getAncestors(this.#startNode, this.#leafNode).reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} [startIndex]
|
||||||
|
*/
|
||||||
|
removeRightContent(startIndex = 0){
|
||||||
|
for(const branchNode of this.#ancestors.slice(startIndex)){
|
||||||
|
let toRemove = branchNode.nextSibling
|
||||||
|
|
||||||
|
while(toRemove){
|
||||||
|
const toRemoveNext = toRemove.nextSibling
|
||||||
|
toRemove.parentNode.removeChild(toRemove)
|
||||||
|
toRemove = toRemoveNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} [startIndex]
|
||||||
|
*/
|
||||||
|
removeLeftContent(startIndex = 0){
|
||||||
|
for(const branchNode of this.#ancestors.slice(startIndex)){
|
||||||
|
let toRemove = branchNode.previousSibling
|
||||||
|
|
||||||
|
while(toRemove){
|
||||||
|
const toRemoveNext = toRemove.previousSibling
|
||||||
|
toRemove.parentNode.removeChild(toRemove)
|
||||||
|
toRemove = toRemoveNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateBlock{
|
||||||
|
/** @type {Element | Document | DocumentFragment} */
|
||||||
|
#commonAncestor;
|
||||||
|
/** @type {TemplateDOMBranch} */
|
||||||
|
#startBranch;
|
||||||
|
/** @type {TemplateDOMBranch} */
|
||||||
|
#endBranch;
|
||||||
|
|
||||||
|
/** @type {Node[]} */
|
||||||
|
#middleContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Node} startMarkerNode
|
||||||
|
* @param {Node} endMarkerNode
|
||||||
|
*/
|
||||||
|
constructor(startMarkerNode, endMarkerNode){
|
||||||
|
// @ts-expect-error xmldom.Node
|
||||||
|
this.#commonAncestor = findCommonAncestor(startMarkerNode, endMarkerNode)
|
||||||
|
|
||||||
|
this.#startBranch = new TemplateDOMBranch(this.#commonAncestor, startMarkerNode)
|
||||||
|
this.#endBranch = new TemplateDOMBranch(this.#commonAncestor, endMarkerNode)
|
||||||
|
|
||||||
|
this.#middleContent = []
|
||||||
|
|
||||||
|
let content = this.#startBranch.at(1).nextSibling
|
||||||
|
while(content && content !== this.#endBranch.at(1)){
|
||||||
|
this.#middleContent.push(content)
|
||||||
|
content = content.nextSibling
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('TemplateBlock')
|
||||||
|
//console.log('startBranch', this.#startBranch.at(1).textContent)
|
||||||
|
//console.log('middleContent', this.#middleContent.map(n => n.textContent).join(''))
|
||||||
|
//console.log('endBranch', this.#endBranch.at(1).textContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeMarkersAndEmptyAncestors(){
|
||||||
|
//console.log('removeMarkersAndEmptyAncestors startBranch')
|
||||||
|
this.#startBranch.removeLeafAndEmptyAncestors()
|
||||||
|
//console.log('removeMarkersAndEmptyAncestors endBranch')
|
||||||
|
this.#endBranch.removeLeafAndEmptyAncestors()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Compartment} compartement
|
||||||
|
*/
|
||||||
|
fillBlockContentTemplate(compartement){
|
||||||
|
const startChild = this.#startBranch.at(1)
|
||||||
|
if(startChild){
|
||||||
|
fillOdtElementTemplate(startChild, compartement)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const content of this.#middleContent){
|
||||||
|
fillOdtElementTemplate(content, compartement)
|
||||||
|
}
|
||||||
|
|
||||||
|
const endChild = this.#endBranch.at(1)
|
||||||
|
if(endChild){
|
||||||
|
fillOdtElementTemplate(endChild, compartement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContent(){
|
||||||
|
this.#startBranch.removeRightContent(2)
|
||||||
|
|
||||||
|
for(const content of this.#middleContent){
|
||||||
|
content.parentNode.removeChild(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#endBranch.removeLeftContent(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
@ -82,7 +257,7 @@ function findPlacesToFillInString(str, compartment) {
|
|||||||
*
|
*
|
||||||
* @param {Node} blockStartNode
|
* @param {Node} blockStartNode
|
||||||
* @param {Node} blockEndNode
|
* @param {Node} blockEndNode
|
||||||
* @returns {{startChild: Node, endChild:Node, content: DocumentFragment}}
|
* @returns {{removeMarkers: () => void, insertContent: (n : Node) => void, content: DocumentFragment}}
|
||||||
*/
|
*/
|
||||||
function extractBlockContent(blockStartNode, blockEndNode) {
|
function extractBlockContent(blockStartNode, blockEndNode) {
|
||||||
//console.log('[extractBlockContent] blockStartNode', blockStartNode.textContent)
|
//console.log('[extractBlockContent] blockStartNode', blockStartNode.textContent)
|
||||||
@ -116,6 +291,8 @@ function extractBlockContent(blockStartNode, blockEndNode) {
|
|||||||
commonAncestor = startAncestor
|
commonAncestor = startAncestor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//console.log('extractBlockContent', commonAncestor.textContent)
|
||||||
|
|
||||||
const startAncestryToCommonAncestor = [...startAncestry].slice(0, [...startAncestry].indexOf(commonAncestor))
|
const startAncestryToCommonAncestor = [...startAncestry].slice(0, [...startAncestry].indexOf(commonAncestor))
|
||||||
const endAncestryToCommonAncestor = [...endAncestry].slice(0, [...endAncestry].indexOf(commonAncestor))
|
const endAncestryToCommonAncestor = [...endAncestry].slice(0, [...endAncestry].indexOf(commonAncestor))
|
||||||
|
|
||||||
@ -123,8 +300,8 @@ function extractBlockContent(blockStartNode, blockEndNode) {
|
|||||||
const startChild = startAncestryToCommonAncestor.at(-1)
|
const startChild = startAncestryToCommonAncestor.at(-1)
|
||||||
const endChild = endAncestryToCommonAncestor.at(-1)
|
const endChild = endAncestryToCommonAncestor.at(-1)
|
||||||
|
|
||||||
//console.log('[extractBlockContent] startChild', startChild.textContent)
|
//console.log('[extractBlockContent] startChild', startChild.childNodes.length, startChild.textContent)
|
||||||
//console.log('[extractBlockContent] endChild', endChild.textContent)
|
//console.log('[extractBlockContent] endChild', endChild.childNodes.length,endChild.textContent)
|
||||||
|
|
||||||
// Extract DOM content in a documentFragment
|
// Extract DOM content in a documentFragment
|
||||||
/** @type {DocumentFragment} */
|
/** @type {DocumentFragment} */
|
||||||
@ -188,9 +365,68 @@ function extractBlockContent(blockStartNode, blockEndNode) {
|
|||||||
|
|
||||||
//console.log('extractBlockContent contentFragment', contentFragment.textContent)
|
//console.log('extractBlockContent contentFragment', contentFragment.textContent)
|
||||||
|
|
||||||
|
let insertionParent;
|
||||||
|
|
||||||
|
if(startAncestryToCommonAncestor.length >= endAncestryToCommonAncestor.length){
|
||||||
|
insertionParent = blockStartNode.parentNode
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
insertionParent = blockEndNode.parentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
let insertionBeforeNodeCandidates
|
||||||
|
if(blockEndNode.nextSibling){
|
||||||
|
insertionBeforeNodeCandidates = [blockEndNode.nextSibling]
|
||||||
|
while(insertionBeforeNodeCandidates.at(-1).nextSibling){
|
||||||
|
insertionBeforeNodeCandidates.push(insertionBeforeNodeCandidates.at(-1).nextSibling)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Node} content
|
||||||
|
*/
|
||||||
|
function insertContent(content){
|
||||||
|
//console.log('insertContent', node.textContent, insertionBeforeNodeCandidates.map(n => `${n.nodeName} - ${n.textContent}`))
|
||||||
|
let insertionBeforeNode
|
||||||
|
|
||||||
|
if(insertionBeforeNodeCandidates){
|
||||||
|
insertionBeforeNode = insertionBeforeNodeCandidates.find(node => node.parentNode === insertionParent)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('insertContent insertionBeforeNode', insertionBeforeNode && insertionBeforeNode.textContent)
|
||||||
|
|
||||||
|
|
||||||
|
if(insertionBeforeNode){
|
||||||
|
insertionParent.insertBefore(content, insertionBeforeNode)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
console.log('insertionParent', insertionParent.nodeName)
|
||||||
|
console.log('insertionParent content before append', insertionParent.textContent)
|
||||||
|
//console.log('insertionParent owner doc', insertionParent.ownerDocument)
|
||||||
|
|
||||||
|
insertionParent.appendChild(content)
|
||||||
|
console.log('insertionParent content after append', insertionParent.textContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('contentFragment',
|
||||||
|
contentFragment.childNodes.length,
|
||||||
|
contentFragment.childNodes[0].nodeName,
|
||||||
|
contentFragment.textContent
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startChild,
|
removeMarkers(){
|
||||||
endChild,
|
for(const marker of [blockStartNode, blockEndNode]){
|
||||||
|
console.log('removing marker', marker.nodeName, marker.textContent)
|
||||||
|
|
||||||
|
try{
|
||||||
|
marker.parentNode.removeChild(marker)
|
||||||
|
}
|
||||||
|
catch(e){}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
insertContent,
|
||||||
content: contentFragment
|
content: contentFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,65 +445,43 @@ function extractBlockContent(blockStartNode, blockEndNode) {
|
|||||||
function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment) {
|
function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment) {
|
||||||
const conditionValue = compartment.evaluate(ifBlockConditionExpression)
|
const conditionValue = compartment.evaluate(ifBlockConditionExpression)
|
||||||
|
|
||||||
let startChild
|
/** @type {TemplateBlock | undefined} */
|
||||||
let endChild
|
let thenTemplateBlock
|
||||||
|
/** @type {TemplateBlock | undefined} */
|
||||||
let markerNodes = new Set()
|
let elseTemplateBlock
|
||||||
|
|
||||||
let chosenFragment
|
|
||||||
|
|
||||||
if(ifElseMarkerNode) {
|
if(ifElseMarkerNode) {
|
||||||
const {
|
/*console.log('before first extract',
|
||||||
startChild: startIfThenChild,
|
ifOpeningMarkerNode.childNodes.length, ifOpeningMarkerNode.textContent,
|
||||||
endChild: endIfThenChild,
|
ifElseMarkerNode.childNodes.length, ifElseMarkerNode.textContent
|
||||||
content: thenFragment
|
)*/
|
||||||
} = extractBlockContent(ifOpeningMarkerNode, ifElseMarkerNode)
|
|
||||||
|
|
||||||
const {
|
thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifElseMarkerNode)
|
||||||
startChild: startIfElseChild,
|
elseTemplateBlock = new TemplateBlock(ifElseMarkerNode, ifClosingMarkerNode)
|
||||||
endChild: endIfElseChild,
|
|
||||||
content: elseFragment
|
|
||||||
} = extractBlockContent(ifElseMarkerNode, ifClosingMarkerNode)
|
|
||||||
|
|
||||||
chosenFragment = conditionValue ? thenFragment : elseFragment
|
|
||||||
startChild = startIfThenChild
|
|
||||||
endChild = endIfElseChild
|
|
||||||
|
|
||||||
markerNodes
|
|
||||||
.add(startIfThenChild).add(endIfThenChild)
|
|
||||||
.add(startIfElseChild).add(endIfElseChild)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const {
|
thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifClosingMarkerNode)
|
||||||
startChild: startIfThenChild,
|
|
||||||
endChild: endIfThenChild,
|
|
||||||
content: thenFragment
|
|
||||||
} = extractBlockContent(ifOpeningMarkerNode, ifClosingMarkerNode)
|
|
||||||
|
|
||||||
chosenFragment = conditionValue ? thenFragment : undefined
|
|
||||||
startChild = startIfThenChild
|
|
||||||
endChild = endIfThenChild
|
|
||||||
|
|
||||||
markerNodes
|
|
||||||
.add(startIfThenChild).add(endIfThenChild)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chosenFragment) {
|
thenTemplateBlock.removeMarkersAndEmptyAncestors()
|
||||||
fillOdtElementTemplate(
|
if(elseTemplateBlock){
|
||||||
chosenFragment,
|
elseTemplateBlock.removeMarkersAndEmptyAncestors()
|
||||||
compartment
|
|
||||||
)
|
|
||||||
|
|
||||||
endChild.parentNode.insertBefore(chosenFragment, endChild)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const markerNode of markerNodes) {
|
|
||||||
try {
|
if(conditionValue) {
|
||||||
// may throw if node already out of tree
|
thenTemplateBlock.fillBlockContentTemplate(compartment)
|
||||||
// might happen if
|
|
||||||
markerNode.parentNode.removeChild(markerNode)
|
if(elseTemplateBlock){
|
||||||
|
elseTemplateBlock.removeContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
thenTemplateBlock.removeContent()
|
||||||
|
|
||||||
|
if(elseTemplateBlock){
|
||||||
|
elseTemplateBlock.fillBlockContentTemplate(compartment)
|
||||||
}
|
}
|
||||||
catch(e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -284,7 +498,9 @@ function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode,
|
|||||||
function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, compartment) {
|
function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, compartment) {
|
||||||
//console.log('fillEachBlock', iterableExpression, itemExpression)
|
//console.log('fillEachBlock', iterableExpression, itemExpression)
|
||||||
|
|
||||||
const {startChild, endChild, content: repeatedFragment} = extractBlockContent(startNode, endNode)
|
const docEl = startNode.ownerDocument.documentElement
|
||||||
|
|
||||||
|
const {removeMarkers, insertContent, content: repeatedFragment} = extractBlockContent(startNode, endNode)
|
||||||
|
|
||||||
// Find the iterable in the data
|
// Find the iterable in the data
|
||||||
// PPP eventually, evaluate the expression as a JS expression
|
// PPP eventually, evaluate the expression as a JS expression
|
||||||
@ -297,6 +513,26 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
|
|||||||
let firstItemFirstChild
|
let firstItemFirstChild
|
||||||
let lastItemLastChild
|
let lastItemLastChild
|
||||||
|
|
||||||
|
// store before-text in startNodePreviousSiblings
|
||||||
|
const startNodePreviousSiblings = []
|
||||||
|
let startNodePreviousSibling = startNode.previousSibling
|
||||||
|
while(startNodePreviousSibling){
|
||||||
|
startNodePreviousSiblings.push(startNodePreviousSibling)
|
||||||
|
startNodePreviousSibling = startNodePreviousSibling.previousSibling
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the array back to tree order
|
||||||
|
startNodePreviousSiblings.reverse()
|
||||||
|
|
||||||
|
|
||||||
|
// store after-text in endNodeNextSiblings
|
||||||
|
const endNodeNextSiblings = []
|
||||||
|
let endNodeNextSibling = endNode.nextSibling
|
||||||
|
while(endNodeNextSibling){
|
||||||
|
endNodeNextSiblings.push(endNodeNextSibling)
|
||||||
|
endNodeNextSibling = endNodeNextSibling.nextSibling
|
||||||
|
}
|
||||||
|
|
||||||
// create each loop result
|
// create each loop result
|
||||||
// using a for-of loop to accept all iterable values
|
// using a for-of loop to accept all iterable values
|
||||||
for(const item of iterable) {
|
for(const item of iterable) {
|
||||||
@ -316,6 +552,9 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
|
|||||||
insideCompartment
|
insideCompartment
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log('itemFragment', itemFragment.textContent)
|
||||||
|
|
||||||
|
|
||||||
if(!firstItemFirstChild){
|
if(!firstItemFirstChild){
|
||||||
firstItemFirstChild = itemFragment.firstChild
|
firstItemFirstChild = itemFragment.firstChild
|
||||||
}
|
}
|
||||||
@ -323,20 +562,11 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
|
|||||||
// eventually, will be set to the last item's last child
|
// eventually, will be set to the last item's last child
|
||||||
lastItemLastChild = itemFragment.lastChild
|
lastItemLastChild = itemFragment.lastChild
|
||||||
|
|
||||||
endChild.parentNode.insertBefore(itemFragment, endChild)
|
insertContent(itemFragment)
|
||||||
}
|
|
||||||
|
|
||||||
// add before-text if any
|
console.log('doc', docEl.textContent)
|
||||||
const startNodePreviousSiblings = []
|
|
||||||
let startNodePreviousSibling = startNode.previousSibling
|
|
||||||
while(startNodePreviousSibling){
|
|
||||||
startNodePreviousSiblings.push(startNodePreviousSibling)
|
|
||||||
startNodePreviousSibling = startNodePreviousSibling.previousSibling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the array back to tree order
|
|
||||||
startNodePreviousSiblings.reverse()
|
|
||||||
|
|
||||||
if(startNodePreviousSiblings.length >= 1){
|
if(startNodePreviousSiblings.length >= 1){
|
||||||
let firstItemFirstestDescendant = firstItemFirstChild
|
let firstItemFirstestDescendant = firstItemFirstChild
|
||||||
while(firstItemFirstestDescendant?.firstChild){
|
while(firstItemFirstestDescendant?.firstChild){
|
||||||
@ -348,13 +578,8 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add after-text if any
|
console.log('doc after add before-text if any', docEl.textContent)
|
||||||
const endNodeNextSiblings = []
|
|
||||||
let endNodeNextSibling = endNode.nextSibling
|
|
||||||
while(endNodeNextSibling){
|
|
||||||
endNodeNextSiblings.push(endNodeNextSibling)
|
|
||||||
endNodeNextSibling = endNodeNextSibling.nextSibling
|
|
||||||
}
|
|
||||||
|
|
||||||
if(endNodeNextSiblings.length >= 1){
|
if(endNodeNextSiblings.length >= 1){
|
||||||
let lastItemLatestDescendant = lastItemLastChild
|
let lastItemLatestDescendant = lastItemLastChild
|
||||||
@ -363,15 +588,16 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(const afterEndNodeElement of endNodeNextSiblings){
|
for(const afterEndNodeElement of endNodeNextSiblings){
|
||||||
|
console.log('doc in for-of', docEl.textContent)
|
||||||
|
console.log('afterEndNodeElement', afterEndNodeElement.textContent)
|
||||||
lastItemLatestDescendant?.parentNode?.appendChild(afterEndNodeElement)
|
lastItemLatestDescendant?.parentNode?.appendChild(afterEndNodeElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('doc before removeMarkers', docEl.textContent)
|
||||||
// remove block marker elements
|
// remove block marker elements
|
||||||
startChild.parentNode.removeChild(startChild)
|
removeMarkers()
|
||||||
endChild.parentNode.removeChild(endChild)
|
console.log('doc after removeMarkers', docEl.textContent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
//@ts-check
|
//@ts-check
|
||||||
|
|
||||||
import {traverse, Node} from "../../DOMUtils.js";
|
import {traverse, Node, getAncestors, findCommonAncestor} from "../../DOMUtils.js";
|
||||||
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js'
|
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js'
|
||||||
|
|
||||||
|
|
||||||
@ -38,41 +38,7 @@ function findAllMatches(text, pattern) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {Node} node1
|
|
||||||
* @param {Node} node2
|
|
||||||
* @returns {Node}
|
|
||||||
*/
|
|
||||||
function findCommonAncestor(node1, node2) {
|
|
||||||
const ancestors1 = getAncestors(node1);
|
|
||||||
const ancestors2 = new Set(getAncestors(node2));
|
|
||||||
|
|
||||||
for(const ancestor of ancestors1) {
|
|
||||||
if(ancestors2.has(ancestor)) {
|
|
||||||
return ancestor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`node1 and node2 do not have a common ancestor`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {Node} node
|
|
||||||
* @returns {Node[]}
|
|
||||||
*/
|
|
||||||
function getAncestors(node) {
|
|
||||||
const ancestors = [];
|
|
||||||
let current = node;
|
|
||||||
|
|
||||||
while(current) {
|
|
||||||
ancestors.push(current);
|
|
||||||
current = current.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ancestors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* text position of a node relative to a text nodes within a container
|
* text position of a node relative to a text nodes within a container
|
||||||
@ -326,6 +292,8 @@ function consolidateMarkers(document){
|
|||||||
consolidatedMarkers.push(positionedMarker)
|
consolidatedMarkers.push(positionedMarker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//console.log('consolidatedMarkers', consolidatedMarkers)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -442,6 +410,8 @@ function isolateMarkerText(document){
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//console.log('markerNodes', [...markerNodes].map(([node, markerType]) => [node.textContent, markerType]))
|
||||||
|
|
||||||
return markerNodes
|
return markerNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -157,10 +157,40 @@ test('template filling - formatted-start-each-single-paragraph', async t => {
|
|||||||
const odtResult = await fillOdtTemplate(odtTemplate, data)
|
const odtResult = await fillOdtTemplate(odtTemplate, data)
|
||||||
|
|
||||||
const odtResultTextContent = await getOdtTextContent(odtResult)
|
const odtResultTextContent = await getOdtTextContent(odtResult)
|
||||||
t.deepEqual(odtResultTextContent.trim(), `
|
console.log('odtResultTextContent', odtResultTextContent)
|
||||||
|
t.deepEqual(odtResultTextContent, `
|
||||||
37
|
37
|
||||||
38
|
38
|
||||||
39
|
39
|
||||||
|
`)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('template filling - formatted ghost if', async t => {
|
||||||
|
const templatePath = join(import.meta.dirname, '../fixtures/reducing.odt')
|
||||||
|
const templateContent = `
|
||||||
|
Utilisation de sources lumineuses : {#if scientifique.source_lumineuses}Oui{:else}Non{/if}
|
||||||
|
`
|
||||||
|
|
||||||
|
const data = {scientifique: {source_lumineuses: true}}
|
||||||
|
|
||||||
|
const odtTemplate = await getOdtTemplate(templatePath)
|
||||||
|
|
||||||
|
const templateTextContent = await getOdtTextContent(odtTemplate)
|
||||||
|
t.deepEqual(templateTextContent.trim(), templateContent.trim(), 'reconnaissance du template')
|
||||||
|
let odtResult
|
||||||
|
try{
|
||||||
|
odtResult = await fillOdtTemplate(odtTemplate, data)
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.error('e', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const odtResultTextContent = await getOdtTextContent(odtResult)
|
||||||
|
t.deepEqual(odtResultTextContent.trim(), `
|
||||||
|
Utilisation de sources lumineuses : Oui
|
||||||
`.trim())
|
`.trim())
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {getOdtTemplate} from '../../scripts/odf/odtTemplate-forNode.js'
|
|||||||
import {fillOdtTemplate, getOdtTextContent} from '../../exports.js'
|
import {fillOdtTemplate, getOdtTextContent} from '../../exports.js'
|
||||||
|
|
||||||
|
|
||||||
test('basic template filling with {#if}', async t => {
|
test('basic template filling with {#if}{:else} - then branch', async t => {
|
||||||
const templatePath = join(import.meta.dirname, '../fixtures/description-nombre.odt')
|
const templatePath = join(import.meta.dirname, '../fixtures/description-nombre.odt')
|
||||||
const templateContent = `Description du nombre {n}
|
const templateContent = `Description du nombre {n}
|
||||||
|
|
||||||
@ -26,22 +26,48 @@ n est un grand nombre
|
|||||||
const odtResult3TextContent = await getOdtTextContent(odtResult3)
|
const odtResult3TextContent = await getOdtTextContent(odtResult3)
|
||||||
t.deepEqual(odtResult3TextContent, `Description du nombre 3
|
t.deepEqual(odtResult3TextContent, `Description du nombre 3
|
||||||
|
|
||||||
n est un petit nombre
|
|
||||||
`)
|
|
||||||
|
|
||||||
// else branch
|
|
||||||
const odtResult8 = await fillOdtTemplate(odtTemplate, {n: 8})
|
|
||||||
const odtResult8TextContent = await getOdtTextContent(odtResult8)
|
|
||||||
t.deepEqual(odtResult8TextContent, `Description du nombre 8
|
|
||||||
|
|
||||||
n est un grand nombre
|
n est un petit nombre
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('weird bug', async t => {
|
test('basic template filling with {#if}{:else} - else branch', async t => {
|
||||||
|
const templatePath = join(import.meta.dirname, '../fixtures/description-nombre.odt')
|
||||||
|
const templateContent = `Description du nombre {n}
|
||||||
|
|
||||||
|
{#if n<5}
|
||||||
|
n est un petit nombre
|
||||||
|
{:else}
|
||||||
|
n est un grand nombre
|
||||||
|
{/if}
|
||||||
|
`
|
||||||
|
|
||||||
|
const odtTemplate = await getOdtTemplate(templatePath)
|
||||||
|
const templateTextContent = await getOdtTextContent(odtTemplate)
|
||||||
|
t.deepEqual(templateTextContent, templateContent, 'reconnaissance du template')
|
||||||
|
|
||||||
|
try{
|
||||||
|
// else branch
|
||||||
|
const odtResult8 = await fillOdtTemplate(odtTemplate, {n: 8})
|
||||||
|
const odtResult8TextContent = await getOdtTextContent(odtResult8)
|
||||||
|
t.deepEqual(odtResult8TextContent, `Description du nombre 8
|
||||||
|
|
||||||
|
|
||||||
|
n est un grand nombre
|
||||||
|
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
catch(e){console.error(e); throw e}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('complex structured if', async t => {
|
||||||
const templatePath = join(import.meta.dirname, '../fixtures/left-branch-content-and-two-consecutive-ifs.odt')
|
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}
|
const templateContent = `Utilisation de sources lumineuses : {#if scientifique.source_lumineuses}Oui{:else}Non{/if}
|
||||||
{#if scientifique.source_lumineuses && scientifique.modalités_source_lumineuses }
|
{#if scientifique.source_lumineuses && scientifique.modalités_source_lumineuses }
|
||||||
|
|||||||
BIN
tests/fixtures/reducing.odt
vendored
Normal file
BIN
tests/fixtures/reducing.odt
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user