Skip to content

Commit 58551c9

Browse files
authored
feat(markdown-docx): add conditional transformer - #397 (#453)
Signed-off-by: k-kumar-01 <[email protected]>
1 parent de694a2 commit 58551c9

File tree

6 files changed

+116
-11
lines changed

6 files changed

+116
-11
lines changed

packages/markdown-docx/src/ToCiceroMarkVisitor.js

+55-4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ class ToCiceroMarkVisitor {
230230
ciceroMarkNode.whenSome = [];
231231
}
232232
}
233+
if (nodeInformation.properties[nodePropertyIndex] === TRANSFORMED_NODES.conditional) {
234+
let currentNodes = [...ciceroMarkNode.nodes];
235+
ciceroMarkNode = {
236+
$class: TRANSFORMED_NODES.conditional,
237+
...nodeInformation.optionalProperties,
238+
isTrue: nodeInformation.hasSome,
239+
nodes: currentNodes,
240+
};
241+
if (nodeInformation.whenSomeType) {
242+
ciceroMarkNode.whenTrue = currentNodes;
243+
ciceroMarkNode.whenFalse = [];
244+
} else {
245+
ciceroMarkNode.whenFalse = currentNodes;
246+
ciceroMarkNode.whenTrue = [];
247+
}
248+
}
233249
if (nodeInformation.properties[nodePropertyIndex] === TRANSFORMED_NODES.link) {
234250
ciceroMarkNode.title = '';
235251
for (const relationshipElement of this.relationshipXML) {
@@ -312,6 +328,18 @@ class ToCiceroMarkVisitor {
312328
} else {
313329
eval(`${nestedExpression}nodes=${nestedExpression}whenNone`);
314330
}
331+
} else if (propertiesCurrent[commonPropertiesLength - 1] === TRANSFORMED_NODES.conditional) {
332+
// equivalent to whenTrue
333+
if (this.JSONXML[nodeIndex].whenSomeType) {
334+
eval(`${nestedExpression}whenTrue=[...${nestedExpression}whenTrue, constructedNode]`);
335+
} else {
336+
eval(`${nestedExpression}whenFalse=[...${nestedExpression}whenFalse, constructedNode]`);
337+
}
338+
if (eval(`${nestedExpression}isTrue`)) {
339+
eval(`${nestedExpression}nodes=${nestedExpression}whenTrue`);
340+
} else {
341+
eval(`${nestedExpression}nodes=${nestedExpression}whenFalse`);
342+
}
315343
} else {
316344
eval(`${nestedExpression}nodes=[...${nestedExpression}nodes, constructedNode]`);
317345
}
@@ -375,6 +403,15 @@ class ToCiceroMarkVisitor {
375403
if (colorCodePresent && shadeCodePresent) {
376404
nodeInformation.nodeType = TRANSFORMED_NODES.code;
377405
}
406+
/**
407+
* for optional hasSome and whenSome stand for same as would be in the ciceromark.
408+
* for conditional, the following relations with optional can be made
409+
* optional | conditional
410+
* hasSome | isTrue
411+
* whenSome | whenTrue
412+
* whenFalse | whenFalse
413+
* The properties for conditional are not added therefore.
414+
*/
378415
if (vanishPropertyPresent) {
379416
if (fontFamilyPresent) {
380417
nodeInformation.whenSomeType = false;
@@ -395,7 +432,7 @@ class ToCiceroMarkVisitor {
395432
} else if (runTimeNodes.name === 'w:t') {
396433
if (calledBy === TRANSFORMED_NODES.codeBlock) {
397434
ooxmlTagTextValue += runTimeNodes.elements ? runTimeNodes.elements[0].text : '';
398-
} else if (calledBy === TRANSFORMED_NODES.optional) {
435+
} else if (calledBy === TRANSFORMED_NODES.optional || calledBy === TRANSFORMED_NODES.conditional) {
399436
ooxmlTagTextValue = runTimeNodes.elements && runTimeNodes.elements[0].text;
400437
nodeInformation.value = ooxmlTagTextValue;
401438
} else {
@@ -407,7 +444,7 @@ class ToCiceroMarkVisitor {
407444
ooxmlTagTextValue += '\n';
408445
} else if (runTimeNodes.name === 'w:sym') {
409446
nodeInformation.nodeType = TRANSFORMED_NODES.softbreak;
410-
if (calledBy !== TRANSFORMED_NODES.optional) {
447+
if (!(calledBy === TRANSFORMED_NODES.optional || calledBy === TRANSFORMED_NODES.conditional)) {
411448
this.JSONXML = [...this.JSONXML, nodeInformation];
412449
}
413450
}
@@ -419,7 +456,7 @@ class ToCiceroMarkVisitor {
419456
nodeInformation.hasSome = true;
420457
}
421458

422-
if (calledBy === TRANSFORMED_NODES.optional) {
459+
if (calledBy === TRANSFORMED_NODES.optional || calledBy === TRANSFORMED_NODES.conditional) {
423460
return nodeInformation;
424461
}
425462
return ooxmlTagTextValue;
@@ -555,6 +592,20 @@ class ToCiceroMarkVisitor {
555592
this.JSONXML = [...this.JSONXML, { ...node }];
556593
}
557594
}
595+
} else if (nodeInformation.nodeType === TRANSFORMED_NODES.conditional) {
596+
if (variableSubNodes.elements) {
597+
const optionalNodes = this.traverseElements(
598+
variableSubNodes.elements,
599+
TRANSFORMED_NODES.conditional
600+
);
601+
for (const node of optionalNodes) {
602+
node.properties = [TRANSFORMED_NODES.conditional, ...node.properties];
603+
node.optionalProperties = {
604+
name: nodeInformation.name,
605+
};
606+
this.JSONXML = [...this.JSONXML, { ...node }];
607+
}
608+
}
558609
} else {
559610
for (const variableContentNodes of variableSubNodes.elements) {
560611
if (variableContentNodes.name === 'w:r') {
@@ -582,7 +633,7 @@ class ToCiceroMarkVisitor {
582633
inlineNodes = [...inlineNodes, this.fetchFormattingProperties(subNode, parent, nodeInformation)];
583634
}
584635
}
585-
if (parent === TRANSFORMED_NODES.optional) {
636+
if (parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional) {
586637
return inlineNodes;
587638
}
588639
return blockNodes;

packages/markdown-docx/src/ToOOXMLVisitor/index.js

+31-5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const {
3434
OPTIONAL_RULE,
3535
VANISH_PROPERTY_RULE,
3636
CONDITIONAL_OR_OPTIONAL_FONT_FAMILY_RULE,
37+
CONDITIONAL_RULE,
3738
} = require('./rules');
3839
const { wrapAroundDefaultDocxTags, wrapAroundLockedContentControls } = require('./helpers');
3940
const { TRANSFORMED_NODES, RELATIONSHIP_OFFSET } = require('../constants');
@@ -153,13 +154,13 @@ class ToOOXMLVisitor {
153154
if (this.getClass(subNode) === TRANSFORMED_NODES.text) {
154155
const tag = this.generateTextOrCodeOOXML(subNode.text, properties, false, parentProperties);
155156
inlineOOXML += tag;
156-
if (parent !== TRANSFORMED_NODES.optional) {
157+
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
157158
this.tags = [...this.tags, tag];
158159
}
159160
} else if (this.getClass(subNode) === TRANSFORMED_NODES.code) {
160161
const tag = this.generateTextOrCodeOOXML(subNode.text, properties, true, parentProperties);
161162
inlineOOXML += tag;
162-
if (parent !== TRANSFORMED_NODES.optional) {
163+
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
163164
this.tags = [...this.tags, tag];
164165
}
165166
} else if (this.getClass(subNode) === TRANSFORMED_NODES.codeBlock) {
@@ -190,12 +191,12 @@ class ToOOXMLVisitor {
190191
type,
191192
parentProperties.traversingNodeHiddenInConditional
192193
);
193-
if (parent !== TRANSFORMED_NODES.optional) {
194+
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
194195
this.tags = [...this.tags, VARIABLE_RULE(title, tag, value, type)];
195196
}
196197
} else if (this.getClass(subNode) === TRANSFORMED_NODES.softbreak) {
197198
inlineOOXML += SOFTBREAK_RULE();
198-
if (parent !== TRANSFORMED_NODES.optional) {
199+
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
199200
this.tags = [...this.tags, SOFTBREAK_RULE()];
200201
}
201202
} else if (this.getClass(subNode) === TRANSFORMED_NODES.thematicBreak) {
@@ -325,6 +326,31 @@ class ToOOXMLVisitor {
325326
// make the parentProperties false as traversal is done
326327
parentProperties.traversingNodeHiddenInConditional = false;
327328
parentProperties.traversingNodePresentInWhenFalseOrWhenNoneCondtion = false;
329+
} else if (this.getClass(subNode) === TRANSFORMED_NODES.conditional) {
330+
parentProperties.traversingNodeHiddenInConditional = !subNode.isTrue;
331+
// traverse true nodes
332+
const whenTrueOOXML = this.traverseNodes(
333+
subNode.whenTrue,
334+
properties,
335+
TRANSFORMED_NODES.conditional,
336+
parentProperties
337+
);
338+
parentProperties.traversingNodePresentInWhenFalseOrWhenNoneCondtion = true;
339+
// traverse false nodes
340+
const whenFalseOOXML = this.traverseNodes(
341+
subNode.whenFalse,
342+
properties,
343+
TRANSFORMED_NODES.conditional,
344+
parentProperties
345+
);
346+
const ooxml = `${whenTrueOOXML} ${whenFalseOOXML}`;
347+
const tag = subNode.name;
348+
this.createOrUpdateCounter(tag);
349+
const title = `${tag.toUpperCase()[0]}${tag.substring(1)}${this.counter[tag].count}`;
350+
const conditionalTag = CONDITIONAL_RULE(title, tag, ooxml);
351+
this.tags = [...this.tags, conditionalTag];
352+
parentProperties.traversingNodeHiddenInConditional = false;
353+
parentProperties.traversingNodePresentInWhenFalseOrWhenNoneCondtion = false;
328354
} else {
329355
if (this.getClass(subNode) === TRANSFORMED_NODES.link) {
330356
this.relationships = [
@@ -336,7 +362,7 @@ class ToOOXMLVisitor {
336362
];
337363
}
338364
let newProperties = [...properties, subNode.$class];
339-
this.traverseNodes(subNode.nodes, newProperties, parent, parentProperties);
365+
inlineOOXML += this.traverseNodes(subNode.nodes, newProperties, parent, parentProperties);
340366
}
341367
}
342368
}

packages/markdown-docx/src/ToOOXMLVisitor/rules.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,38 @@ const OPTIONAL_RULE = (title, tag, value, type) => {
283283
const VANISH_PROPERTY_RULE = () => '<w:vanish/>';
284284

285285
/**
286-
* Inserts a different font family so that the `whenNone` nodes for optional content can be distinguished from `whenSome` nodes.
287-
*
286+
* Inserts a different font family so that the conditional/optional content(else condition) can be distinguished. They can be of following types:-
287+
* 1) `whenNone` nodes for optional(to be distinguished from `whenSome`)
288+
* 2) `whenFalse` nodes for conditional(to be distinguished from `whenTrue`)
288289
* @returns {string} OOXML tag for Baskerville Old Face font family
289290
*/
290291
const CONDITIONAL_OR_OPTIONAL_FONT_FAMILY_RULE = () => {
291292
return '<w:rFonts w:ascii="Baskerville Old Face" w:hAnsi="Baskerville Old Face"/>';
292293
};
293294

295+
/**
296+
* Inserts optional node in OOXML form.
297+
*
298+
* @param {string} title Title of the optional node. Eg. receiver-1, shipper-1
299+
* @param {string} tag Name of the optional node. Eg. receiver, shipper
300+
* @param {string} value Value of the optional node
301+
* @returns {string} OOXML string for the variable
302+
*/
303+
const CONDITIONAL_RULE = (title, tag, value) => {
304+
return `
305+
<w:sdt>
306+
<w:sdtPr>
307+
<w:alias w:val="${titleGenerator(title)}"/>
308+
<w:tag w:val="${TRANSFORMED_NODES.conditional}${SEPARATOR}${tag}"/>
309+
<w:lock w:val="contentLocked"/>
310+
</w:sdtPr>
311+
<w:sdtContent>
312+
${value}
313+
</w:sdtContent>
314+
</w:sdt>
315+
`;
316+
};
317+
294318
module.exports = {
295319
TEXT_RULE,
296320
EMPHASIS_RULE,
@@ -311,4 +335,5 @@ module.exports = {
311335
OPTIONAL_RULE,
312336
VANISH_PROPERTY_RULE,
313337
CONDITIONAL_OR_OPTIONAL_FONT_FAMILY_RULE,
338+
CONDITIONAL_RULE,
314339
};

packages/markdown-docx/src/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const TRANSFORMED_NODES = {
2121
code: `${NS_PREFIX_CommonMarkModel}Code`,
2222
codeBlock: `${NS_PREFIX_CommonMarkModel}CodeBlock`,
2323
computedVariable: `${NS_PREFIX_CiceroMarkModel}ComputedVariable`,
24+
conditional: `${NS_PREFIX_CiceroMarkModel}Conditional`,
2425
optional: `${NS_PREFIX_CiceroMarkModel}Optional`,
2526
document: `${NS_PREFIX_CommonMarkModel}Document`,
2627
emphasize: `${NS_PREFIX_CommonMarkModel}Emph`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"$class":"org.accordproject.commonmark.Document","xmlns":"http://commonmark.org/xml/1.0","nodes":[{"$class":"org.accordproject.commonmark.Heading","level":"2","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"Late Delivery and Penalty."}]},{"$class":"org.accordproject.commonmark.Paragraph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"In case of delayed delivery"},{"$class":"org.accordproject.ciceromark.Conditional","isTrue":false,"whenTrue":[{"$class":"org.accordproject.commonmark.Text","text":" except for Force Majeure cases,"}],"whenFalse":[{"$class":"org.accordproject.commonmark.Text","text":" even when Force Majeure occurs,"}],"name":"forceMajeure","nodes":[{"$class":"org.accordproject.commonmark.Text","text":" even when Force Majeure occurs,"}]},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.ciceromark.Variable","value":"\"Dan\"","name":"seller","elementType":"org.accordproject.party.Party"},{"$class":"org.accordproject.commonmark.Text","text":" (the Seller) shall pay to "},{"$class":"org.accordproject.ciceromark.Variable","value":"\"Steve\"","name":"buyer","elementType":"org.accordproject.party.Party"},{"$class":"org.accordproject.commonmark.Text","text":" (the Buyer) for every "},{"$class":"org.accordproject.ciceromark.Variable","value":"2","name":"amount","elementType":"Long"},{"$class":"org.accordproject.commonmark.Text","text":" "},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"of delay penalty amounting to "},{"$class":"org.accordproject.ciceromark.Variable","value":"10.5","name":"penaltyPercentage","elementType":"Double"},{"$class":"org.accordproject.commonmark.Text","text":"% of the total value of the Equipment"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"whose delivery has been delayed. Any fractional part of a "},{"$class":"org.accordproject.commonmark.Text","text":" is to be"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"considered a full "},{"$class":"org.accordproject.commonmark.Text","text":". The total amount of penalty shall not however,"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"exceed "},{"$class":"org.accordproject.ciceromark.Variable","value":"55.0","name":"capPercentage","elementType":"Double"},{"$class":"org.accordproject.commonmark.Text","text":"% of the total value of the Equipment involved in late delivery."},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"If the delay is more than "},{"$class":"org.accordproject.ciceromark.Variable","value":"15","name":"amount","elementType":"Long"},{"$class":"org.accordproject.commonmark.Text","text":" "},{"$class":"org.accordproject.commonmark.Text","text":", the Buyer is entitled to terminate this Contract."}]}]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"$class":"org.accordproject.commonmark.Document","xmlns":"http://commonmark.org/xml/1.0","nodes":[{"$class":"org.accordproject.commonmark.Heading","level":"2","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"Late Delivery and Penalty."}]},{"$class":"org.accordproject.commonmark.Paragraph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"In case of delayed delivery"},{"$class":"org.accordproject.ciceromark.Conditional","isTrue":true,"whenTrue":[{"$class":"org.accordproject.commonmark.Text","text":" except for Force Majeure cases,"},{"$class":"org.accordproject.commonmark.Emph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"not"}]}],"whenFalse":[{"$class":"org.accordproject.commonmark.Text","text":" even when Force Majeure occurs,"}],"name":"forceMajeure","nodes":[{"$class":"org.accordproject.commonmark.Text","text":" except for Force Majeure cases,"},{"$class":"org.accordproject.commonmark.Emph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"not"}]}]},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.ciceromark.Variable","value":"\"Dan\"","name":"seller","elementType":"org.accordproject.party.Party"},{"$class":"org.accordproject.commonmark.Text","text":" (the Seller) shall pay to "},{"$class":"org.accordproject.ciceromark.Variable","value":"\"Steve\"","name":"buyer","elementType":"org.accordproject.party.Party"},{"$class":"org.accordproject.commonmark.Text","text":" (the Buyer) for every "},{"$class":"org.accordproject.ciceromark.Variable","value":"2","name":"amount","elementType":"Long"},{"$class":"org.accordproject.commonmark.Text","text":" "},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"of delay penalty amounting to "},{"$class":"org.accordproject.ciceromark.Variable","value":"10.5","name":"penaltyPercentage","elementType":"Double"},{"$class":"org.accordproject.commonmark.Text","text":"% of the total value of the Equipment"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"whose delivery has been delayed. Any fractional part of a "},{"$class":"org.accordproject.commonmark.Text","text":" is to be"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"considered a full "},{"$class":"org.accordproject.commonmark.Text","text":". The total amount of penalty shall not however,"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"exceed "},{"$class":"org.accordproject.ciceromark.Variable","value":"55.0","name":"capPercentage","elementType":"Double"},{"$class":"org.accordproject.commonmark.Text","text":"% of the total value of the Equipment involved in late delivery."},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"If the delay is more than "},{"$class":"org.accordproject.ciceromark.Variable","value":"15","name":"amount","elementType":"Long"},{"$class":"org.accordproject.commonmark.Text","text":" "},{"$class":"org.accordproject.commonmark.Text","text":", the Buyer is entitled to terminate this Contract."}]}]}

0 commit comments

Comments
 (0)