easy-template-x 7.2.0 → 7.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/easy-template-x.cjs +21 -2
- package/dist/es/easy-template-x.mjs +21 -2
- package/dist/types/office/officeMarkup.d.ts +1 -0
- package/dist/types/plugins/loop/strategy/loopParagraphStrategy.d.ts +1 -0
- package/package.json +1 -1
- package/src/compilation/tagParser.tests.ts +53 -0
- package/src/compilation/tagUtils.ts +1 -1
- package/src/office/officeMarkup.ts +4 -0
- package/src/plugins/loop/strategy/loopParagraphStrategy.ts +23 -2
|
@@ -1074,7 +1074,7 @@ const TagPlacement = Object.freeze({
|
|
|
1074
1074
|
|
|
1075
1075
|
function tagRegex(delimiters, global = false) {
|
|
1076
1076
|
const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
|
|
1077
|
-
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(
|
|
1077
|
+
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
|
|
1078
1078
|
const flags = global ? 'gm' : 'm';
|
|
1079
1079
|
return new RegExp(tagPattern, flags);
|
|
1080
1080
|
}
|
|
@@ -1963,6 +1963,9 @@ class Query {
|
|
|
1963
1963
|
isRunPropertiesNode(node) {
|
|
1964
1964
|
return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
|
|
1965
1965
|
}
|
|
1966
|
+
isTableNode(node) {
|
|
1967
|
+
return node.nodeName === OmlNode.W.Table;
|
|
1968
|
+
}
|
|
1966
1969
|
isTableCellNode(node) {
|
|
1967
1970
|
return node.nodeName === OmlNode.W.TableCell;
|
|
1968
1971
|
}
|
|
@@ -3409,6 +3412,11 @@ class LoopParagraphStrategy {
|
|
|
3409
3412
|
}
|
|
3410
3413
|
}
|
|
3411
3414
|
|
|
3415
|
+
// We cannot leave table cells completely empty, so we track them.
|
|
3416
|
+
// See: http://officeopenxml.com/WPtableCell.php
|
|
3417
|
+
const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
|
|
3418
|
+
const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
|
|
3419
|
+
|
|
3412
3420
|
// Remove old paragraphs - between first and last paragraph.
|
|
3413
3421
|
xml.modify.removeSiblings(firstParagraph, lastParagraph);
|
|
3414
3422
|
|
|
@@ -3417,6 +3425,17 @@ class LoopParagraphStrategy {
|
|
|
3417
3425
|
if (firstParagraph !== lastParagraph) {
|
|
3418
3426
|
xml.modify.remove(lastParagraph);
|
|
3419
3427
|
}
|
|
3428
|
+
|
|
3429
|
+
// Make sure the table cells are not empty (if they exist).
|
|
3430
|
+
if (newParagraphs.length === 0) {
|
|
3431
|
+
this.fillTableCell(firstTableCell);
|
|
3432
|
+
this.fillTableCell(lastTableCell);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
fillTableCell(tableCell) {
|
|
3436
|
+
if (tableCell && !tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) && !tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))) {
|
|
3437
|
+
xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
|
|
3438
|
+
}
|
|
3420
3439
|
}
|
|
3421
3440
|
}
|
|
3422
3441
|
|
|
@@ -5293,7 +5312,7 @@ class TemplateHandler {
|
|
|
5293
5312
|
/**
|
|
5294
5313
|
* Version number of the `easy-template-x` library.
|
|
5295
5314
|
*/
|
|
5296
|
-
version = "7.2.
|
|
5315
|
+
version = "7.2.2" ;
|
|
5297
5316
|
constructor(options) {
|
|
5298
5317
|
this.options = new TemplateHandlerOptions(options);
|
|
5299
5318
|
const delimiters = this.options.delimiters;
|
|
@@ -1072,7 +1072,7 @@ const TagPlacement = Object.freeze({
|
|
|
1072
1072
|
|
|
1073
1073
|
function tagRegex(delimiters, global = false) {
|
|
1074
1074
|
const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
|
|
1075
|
-
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(
|
|
1075
|
+
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
|
|
1076
1076
|
const flags = global ? 'gm' : 'm';
|
|
1077
1077
|
return new RegExp(tagPattern, flags);
|
|
1078
1078
|
}
|
|
@@ -1961,6 +1961,9 @@ class Query {
|
|
|
1961
1961
|
isRunPropertiesNode(node) {
|
|
1962
1962
|
return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
|
|
1963
1963
|
}
|
|
1964
|
+
isTableNode(node) {
|
|
1965
|
+
return node.nodeName === OmlNode.W.Table;
|
|
1966
|
+
}
|
|
1964
1967
|
isTableCellNode(node) {
|
|
1965
1968
|
return node.nodeName === OmlNode.W.TableCell;
|
|
1966
1969
|
}
|
|
@@ -3407,6 +3410,11 @@ class LoopParagraphStrategy {
|
|
|
3407
3410
|
}
|
|
3408
3411
|
}
|
|
3409
3412
|
|
|
3413
|
+
// We cannot leave table cells completely empty, so we track them.
|
|
3414
|
+
// See: http://officeopenxml.com/WPtableCell.php
|
|
3415
|
+
const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
|
|
3416
|
+
const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
|
|
3417
|
+
|
|
3410
3418
|
// Remove old paragraphs - between first and last paragraph.
|
|
3411
3419
|
xml.modify.removeSiblings(firstParagraph, lastParagraph);
|
|
3412
3420
|
|
|
@@ -3415,6 +3423,17 @@ class LoopParagraphStrategy {
|
|
|
3415
3423
|
if (firstParagraph !== lastParagraph) {
|
|
3416
3424
|
xml.modify.remove(lastParagraph);
|
|
3417
3425
|
}
|
|
3426
|
+
|
|
3427
|
+
// Make sure the table cells are not empty (if they exist).
|
|
3428
|
+
if (newParagraphs.length === 0) {
|
|
3429
|
+
this.fillTableCell(firstTableCell);
|
|
3430
|
+
this.fillTableCell(lastTableCell);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
fillTableCell(tableCell) {
|
|
3434
|
+
if (tableCell && !tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) && !tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))) {
|
|
3435
|
+
xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
|
|
3436
|
+
}
|
|
3418
3437
|
}
|
|
3419
3438
|
}
|
|
3420
3439
|
|
|
@@ -5291,7 +5310,7 @@ class TemplateHandler {
|
|
|
5291
5310
|
/**
|
|
5292
5311
|
* Version number of the `easy-template-x` library.
|
|
5293
5312
|
*/
|
|
5294
|
-
version = "7.2.
|
|
5313
|
+
version = "7.2.2" ;
|
|
5295
5314
|
constructor(options) {
|
|
5296
5315
|
this.options = new TemplateHandlerOptions(options);
|
|
5297
5316
|
const delimiters = this.options.delimiters;
|
|
@@ -8,6 +8,7 @@ declare class Query {
|
|
|
8
8
|
isTextNode(node: XmlNode): boolean;
|
|
9
9
|
isRunNode(node: XmlNode): boolean;
|
|
10
10
|
isRunPropertiesNode(node: XmlNode): boolean;
|
|
11
|
+
isTableNode(node: XmlNode): boolean;
|
|
11
12
|
isTableCellNode(node: XmlNode): boolean;
|
|
12
13
|
isParagraphNode(node: XmlNode): boolean;
|
|
13
14
|
isParagraphPropertiesNode(node: XmlNode): boolean;
|
|
@@ -5,4 +5,5 @@ export declare class LoopParagraphStrategy implements ILoopStrategy {
|
|
|
5
5
|
isApplicable(openTag: TextNodeTag, closeTag: TextNodeTag, isCondition: boolean): boolean;
|
|
6
6
|
splitBefore(openTag: TextNodeTag, closeTag: TextNodeTag): SplitBeforeResult;
|
|
7
7
|
mergeBack(newParagraphs: XmlNode[][], firstParagraph: XmlNode, lastParagraph: XmlNode): void;
|
|
8
|
+
private fillTableCell;
|
|
8
9
|
}
|
package/package.json
CHANGED
|
@@ -759,6 +759,59 @@ describe(TagParser, () => {
|
|
|
759
759
|
expect(tags[1].rawText).toEqual('{/loop}');
|
|
760
760
|
});
|
|
761
761
|
|
|
762
|
+
test('simple - with whitespace', () => {
|
|
763
|
+
|
|
764
|
+
const text = '{ # loop [opt: "yes"] }{ / loop }';
|
|
765
|
+
const paragraph = parseXml(`
|
|
766
|
+
<w:p><w:r><w:t>${text}</w:t></w:r></w:p>
|
|
767
|
+
`, false);
|
|
768
|
+
|
|
769
|
+
const textNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0);
|
|
770
|
+
const delimiters: TextNodeDelimiterMark[] = [
|
|
771
|
+
{
|
|
772
|
+
placement: TagPlacement.TextNode,
|
|
773
|
+
isOpen: true,
|
|
774
|
+
index: 0,
|
|
775
|
+
xmlTextNode: textNode
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
placement: TagPlacement.TextNode,
|
|
779
|
+
isOpen: false,
|
|
780
|
+
index: 22,
|
|
781
|
+
xmlTextNode: textNode
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
placement: TagPlacement.TextNode,
|
|
785
|
+
isOpen: true,
|
|
786
|
+
index: 23,
|
|
787
|
+
xmlTextNode: textNode
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
placement: TagPlacement.TextNode,
|
|
791
|
+
isOpen: false,
|
|
792
|
+
index: 32,
|
|
793
|
+
xmlTextNode: textNode
|
|
794
|
+
}
|
|
795
|
+
];
|
|
796
|
+
|
|
797
|
+
const parser = createTagParser();
|
|
798
|
+
const tags = parser.parse(delimiters);
|
|
799
|
+
|
|
800
|
+
expect(tags.length).toEqual(2);
|
|
801
|
+
|
|
802
|
+
// open tag
|
|
803
|
+
expect(tags[0].disposition).toEqual(TagDisposition.Open);
|
|
804
|
+
expect(tags[0].name).toEqual('loop');
|
|
805
|
+
expect(tags[0].options).toEqual({ opt: 'yes' });
|
|
806
|
+
expect(tags[0].rawText).toEqual('{ # loop [opt: "yes"] }');
|
|
807
|
+
|
|
808
|
+
// close tag
|
|
809
|
+
expect(tags[1].disposition).toEqual(TagDisposition.Close);
|
|
810
|
+
expect(tags[1].name).toEqual('loop');
|
|
811
|
+
expect(tags[1].options).toBeFalsy();
|
|
812
|
+
expect(tags[1].rawText).toEqual('{ / loop }');
|
|
813
|
+
});
|
|
814
|
+
|
|
762
815
|
test('angular parser style with brackets', () => {
|
|
763
816
|
|
|
764
817
|
const text = '{something[0] [[myOpt: 5]]}';
|
|
@@ -3,7 +3,7 @@ import { Regex } from "src/utils";
|
|
|
3
3
|
|
|
4
4
|
export function tagRegex(delimiters: Delimiters, global: boolean = false): RegExp {
|
|
5
5
|
const tagOptionsPattern = `${Regex.escape(delimiters.tagOptionsStart)}(?<tagOptions>.*?)${Regex.escape(delimiters.tagOptionsEnd)}`;
|
|
6
|
-
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(
|
|
6
|
+
const tagPattern = `${Regex.escape(delimiters.tagStart)}(?<tagName>.*?)(?:\\s*${tagOptionsPattern})?\\s*${Regex.escape(delimiters.tagEnd)}`;
|
|
7
7
|
const flags = global ? 'gm' : 'm';
|
|
8
8
|
return new RegExp(tagPattern, flags);
|
|
9
9
|
}
|
|
@@ -62,6 +62,10 @@ class Query {
|
|
|
62
62
|
return node.nodeName === OmlNode.W.RunProperties || node.nodeName === OmlNode.A.RunProperties;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
public isTableNode(node: XmlNode): boolean {
|
|
66
|
+
return node.nodeName === OmlNode.W.Table;
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
public isTableCellNode(node: XmlNode): boolean {
|
|
66
70
|
return node.nodeName === OmlNode.W.TableCell;
|
|
67
71
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TextNodeTag } from "src/compilation";
|
|
2
|
+
import { officeMarkup, OmlNode } from "src/office";
|
|
2
3
|
import { LoopOver, LoopTagOptions } from "src/plugins/loop/loopTagOptions";
|
|
3
|
-
import {
|
|
4
|
-
import { xml, XmlNode } from "src/xml";
|
|
4
|
+
import { xml, XmlGeneralNode, XmlNode } from "src/xml";
|
|
5
5
|
import { ILoopStrategy, SplitBeforeResult } from "./iLoopStrategy";
|
|
6
6
|
|
|
7
7
|
export class LoopParagraphStrategy implements ILoopStrategy {
|
|
@@ -38,6 +38,11 @@ export class LoopParagraphStrategy implements ILoopStrategy {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// We cannot leave table cells completely empty, so we track them.
|
|
42
|
+
// See: http://officeopenxml.com/WPtableCell.php
|
|
43
|
+
const firstTableCell = officeMarkup.query.containingTableCellNode(firstParagraph);
|
|
44
|
+
const lastTableCell = officeMarkup.query.containingTableCellNode(lastParagraph);
|
|
45
|
+
|
|
41
46
|
// Remove old paragraphs - between first and last paragraph.
|
|
42
47
|
xml.modify.removeSiblings(firstParagraph, lastParagraph);
|
|
43
48
|
|
|
@@ -46,5 +51,21 @@ export class LoopParagraphStrategy implements ILoopStrategy {
|
|
|
46
51
|
if (firstParagraph !== lastParagraph) {
|
|
47
52
|
xml.modify.remove(lastParagraph);
|
|
48
53
|
}
|
|
54
|
+
|
|
55
|
+
// Make sure the table cells are not empty (if they exist).
|
|
56
|
+
if (newParagraphs.length === 0) {
|
|
57
|
+
this.fillTableCell(firstTableCell);
|
|
58
|
+
this.fillTableCell(lastTableCell);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fillTableCell(tableCell: XmlGeneralNode): void {
|
|
63
|
+
if (
|
|
64
|
+
tableCell &&
|
|
65
|
+
!tableCell.childNodes?.find(node => officeMarkup.query.isParagraphNode(node)) &&
|
|
66
|
+
!tableCell.childNodes?.find(node => officeMarkup.query.isTableNode(node))
|
|
67
|
+
) {
|
|
68
|
+
xml.modify.appendChild(tableCell, xml.create.generalNode(OmlNode.W.Paragraph));
|
|
69
|
+
}
|
|
49
70
|
}
|
|
50
71
|
}
|