@ind-rcg/plugins-printengine 246.1010.0

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.
@@ -0,0 +1,1659 @@
1
+ /*
2
+ * FILE_HEADER
3
+ */
4
+ 'use strict';
5
+ const parser = require('xml-js');
6
+ const _ = require('lodash');
7
+ const Localization = require('./localizationClass');
8
+ const MacroHandler = require('./macroHandlerClass');
9
+ const Format = require('./formatClass');
10
+ const LogClass = require('./logClass');
11
+ const TableCellIdentifier = require('./tableCellIdentifier');
12
+
13
+ const EACH_NODE = "each";
14
+ const TABLE_NODE = "table";
15
+ const TBODY_NODE = "tbody";
16
+ const THEAD_NODE = "thead";
17
+ const HEADER_NODE = "header";
18
+ const FOOTER_NODE = "footer";
19
+ const PRINT_LAYOUT_NODE = "PrintLayout";
20
+ const REPORT_LAYOUT_NODE = "ReportLayout";
21
+ const DECLARATIONS_NODE = "Declarations";
22
+ const DOCUMENT_PROPERTIES_NODE = "DocumentProperties";
23
+ const PROPERTY_NODE = "Property";
24
+ const H1 = "h1";
25
+ const H2 = "h2";
26
+ const PARAGRAPH = "p";
27
+ const IMAGE = "img";
28
+ const TR_NODE = "tr";
29
+ const TH_NODE = "th";
30
+ const TD_NODE = "td";
31
+ const SUM_NODE = "sum";
32
+
33
+ const FILTERS_NODE = "filters";
34
+ const FILTER_NODE = "filter";
35
+ const ORDERCRITERIA_NODE = "orderCriteria";
36
+ const ORDERCRITERION_NODE = "orderCriterion";
37
+ const CORRELATION_NODE = "correlation";
38
+
39
+ const COMPAREMODE_DEFAULT = "STRING";
40
+ const COMPAREMODE_NUMBER = "NUMBER";
41
+
42
+ const DIV_NODE = "div";
43
+ const DIV_VISIBILITY_NODE = "visibility";
44
+ const DIV_VISIBILITY_BINDING_NODE = "visibilityBinding";
45
+ const DIV_VISIBILITY_CONDITION_NODE = "visibilityCondition";
46
+ const SIGNATURE_SIZE_LIMIT = 200 * 1024;
47
+
48
+ const ELEMENT_TYPE = {
49
+ "TEXT": "text",
50
+ "ELEMENT": "element",
51
+ "COMMENT": "comment"
52
+ };
53
+
54
+ class ContractToIntermediateJSON {
55
+ constructor(log) {
56
+ if(!(log instanceof LogClass)) {
57
+ throw new Error("Invalid constructor parameters");
58
+ }
59
+ this.__log = log;
60
+ // addParent: true,
61
+ this.__xmlParserLibraryOptions = {
62
+ spaces: 2,
63
+ compact: false
64
+ };
65
+
66
+ this.__printLayoutName = null;
67
+ this.__declarationsNode = null;
68
+ this.__localization = null;
69
+ this.__toggles = null;
70
+ this.__macroHandler = new MacroHandler(log);
71
+ this.__integerStringRegExp = /^\d+$/g;
72
+
73
+ this.__decimalSeparator = ".";
74
+ this.__thousandSeparator = ",";
75
+ this.__format = null;
76
+ this.__clearAllCaches();
77
+ }
78
+
79
+ __clearAllCaches(){
80
+ this.__tableIndex = {};
81
+ this.__sumTdThList = [];
82
+ this.__eachCache = {};
83
+ this.__divCache = [];
84
+ }
85
+
86
+ __checkDataForDeclaredItems(params, declarationNode){
87
+ let errorMessage = "";
88
+ let bos = params.getBOs();
89
+ let images = params.getImages();
90
+
91
+ let dataMissing = function dataMissing(itemType, itemName) {
92
+ let dataMissingMsg = `Missing declared ${itemType} "${itemName}" in print data.`;
93
+ if(errorMessage.length === 0) {
94
+ errorMessage = errorMessage + dataMissingMsg;
95
+ } else {
96
+ errorMessage = errorMessage + "\n" + dataMissingMsg;
97
+ }
98
+ };
99
+
100
+ if(!_.isNil(declarationNode) && !_.isNil(declarationNode.elements)) {
101
+ _.forEach(declarationNode.elements, (dataDeclaration) => {
102
+ if(!_.isNil(dataDeclaration.attributes)){
103
+ let itemName = dataDeclaration.attributes.name;
104
+ if(!_.isNil(dataDeclaration.attributes.type)) {
105
+ if (dataDeclaration.attributes.type === "Image") {
106
+ if(!_.isNil(itemName) && _.isNil(images[itemName])){
107
+ dataMissing("Image", itemName);
108
+ }
109
+ } else if (dataDeclaration.attributes.type === "Signature") {
110
+ if(!_.isNil(itemName) && _.isNil(images[itemName])){
111
+ dataMissing("Signature", itemName);
112
+ }
113
+ } else {
114
+ if(!_.isNil(itemName) && _.isNil(bos[itemName])){
115
+ dataMissing("BO", itemName);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ });
121
+
122
+ if(errorMessage.length > 0){
123
+ throw new Error(errorMessage);
124
+ }
125
+ }
126
+ }
127
+ __checkInput(contract, params) {
128
+ if(_.isNil(params) || !_.isObject(params) || _.isArray(params)) {
129
+ throw new Error("Invalid value for parameter 'params'.");
130
+ }
131
+ if(!_.isString(contract)) {
132
+ throw new Error("Invalid value for parameter 'contract'.");
133
+ }
134
+ }
135
+
136
+ toJson(contract, params) {
137
+ this.__checkInput(contract, params);
138
+ this.__clearAllCaches();
139
+ this.__toggles = params.getToggles();
140
+ this.__format = new Format();
141
+ this.__localization = new Localization(params.getLocalization());
142
+
143
+ this.__decimalSeparator = this.__localization.separatorDecimal;
144
+ this.__thousandSeparator = this.__localization.separatorThousand;
145
+
146
+ let contractJson = this.__parse(contract);
147
+
148
+ this.__declarationsNode = this.__getDeclarationsNode(contractJson);
149
+ this.__checkDataForDeclaredItems(params, this.__declarationsNode);
150
+
151
+ this.__walkParseTree(contractJson, {}, "", params);
152
+
153
+ let printLayoutNode = this.__findPrintLayoutNode(contractJson);
154
+ let reportLayout = _.find(printLayoutNode.elements, {name:REPORT_LAYOUT_NODE});
155
+
156
+ if(_.isNil(reportLayout)) {
157
+ throw new Error("Missing ReportLayout node in PrintLayout.");
158
+ }
159
+
160
+ this.__processDivs(params);
161
+ this.__postProcessReportLayout(reportLayout);
162
+ this.__processSumRecords();
163
+
164
+ this.__addWatermark(reportLayout, params);
165
+ this.__configureAutomaticPageBreak(reportLayout, params);
166
+
167
+ let documentPropertiesRaw = _.find(printLayoutNode.elements, {name:DOCUMENT_PROPERTIES_NODE});
168
+ let documentProperties = this.__reformatDocumentProperties(documentPropertiesRaw);
169
+
170
+ return {"reportLayout": reportLayout, "documentProperties": documentProperties};
171
+ }
172
+
173
+ __processDivs(params) {
174
+ if (!_.isNil(this.__divCache) && _.isArray(this.__divCache)) {
175
+
176
+ for (let index = 0; index < this.__divCache.length; index++) {
177
+ const divData = this.__divCache[index];
178
+
179
+ let result = this.__checkForDivToBeVisible(divData.divNode, params);
180
+ if (result) {
181
+ // rescue div children and put them in parent elements
182
+ let nodes = _.filter(divData.divNode.elements, node => node.type === ELEMENT_TYPE.ELEMENT && node.name !== DIV_VISIBILITY_NODE);
183
+ if (!_.isNil(nodes) && _.isArray(nodes)) {
184
+ // remember div id for future styling / layouting ...
185
+ _.forEach(nodes, node => {
186
+ this.__ensureAttributes(node);
187
+ node.attributes.div = divData.id;
188
+ });
189
+ let divIndex = _.findIndex(divData.parentNode.elements, node => _.isEqual(node, divData.divNode));
190
+ divData.parentNode.elements.splice(divIndex, 0, ..._.cloneDeep(nodes));
191
+ }
192
+ }
193
+ // remove div from parent
194
+ _.remove(divData.parentNode.elements, node => _.isEqual(node, divData.divNode));
195
+ }
196
+
197
+ }
198
+ }
199
+
200
+ __postProcessReportLayout(reportLayoutNode) {
201
+ // get header node
202
+ let headerNode = _.find(reportLayoutNode.elements, {name: HEADER_NODE});
203
+ if (!_.isNil(headerNode)) {
204
+ // => check header node and remove empty tables
205
+ this.__removeEmptyTables(headerNode);
206
+ }
207
+ // get footer node
208
+ let footerNode = _.find(reportLayoutNode.elements, {name: FOOTER_NODE});
209
+ if (!_.isNil(footerNode)) {
210
+ // => check footer node and remove empty tables
211
+ this.__removeEmptyTables(footerNode);
212
+ }
213
+ // => check reportlayout node and remove empty tables & divs
214
+ this.__removeEmptyTables(reportLayoutNode);
215
+ }
216
+
217
+ __removeEmptyTables(containerNode) {
218
+ if (_.isNil(containerNode) || (!_.isNil(containerNode) && !_.isUndefined(containerNode.elements) && containerNode.elements.length === 0)) {
219
+ return;
220
+ }
221
+ let tablesToRemove = [];
222
+ _.forEach(containerNode.elements, node => {
223
+ if (node.type === ELEMENT_TYPE.ELEMENT && node.name === TABLE_NODE && node.toBeRemoved) {
224
+ tablesToRemove.push(node);
225
+ }
226
+ });
227
+ if (tablesToRemove.length > 0) {
228
+ _.forEach(tablesToRemove, tableNode => {
229
+ _.remove(containerNode.elements, node => _.isEqual(node, tableNode));
230
+ });
231
+ }
232
+ }
233
+
234
+ __addWatermark(reportLayout, params){
235
+ if(_.isNil(reportLayout)){
236
+ return;
237
+ }
238
+ this.__ensureAttributes(reportLayout);
239
+ let watermarkInMetadata = params.getMetadata()[params.getMetadataKeys().WATERMARK];
240
+ if(_.isString(watermarkInMetadata) && _.trim(watermarkInMetadata) !== "") {
241
+ reportLayout.attributes.watermark = watermarkInMetadata;
242
+ } else if (!_.isNil(watermarkInMetadata) && !_.isString(watermarkInMetadata)) {
243
+ this.__log.error("Invalid value for watermark.");
244
+ }
245
+
246
+ }
247
+
248
+ __configureAutomaticPageBreak(reportLayout, params){
249
+ if(_.isNil(reportLayout)){
250
+ return;
251
+ }
252
+ this.__ensureAttributes(reportLayout);
253
+ let automaticPageBreak = false; // default
254
+ let automaticPageBreakInMetadata = params.getMetadata()[params.getMetadataKeys().AUTOMATIC_PAGEBREAK];
255
+ if(!_.isNil(automaticPageBreakInMetadata) && _.isBoolean(automaticPageBreakInMetadata)) {
256
+ automaticPageBreak = automaticPageBreakInMetadata;
257
+ }
258
+ reportLayout.attributes.automaticPageBreak = automaticPageBreak;
259
+ }
260
+
261
+ __reformatDocumentProperties(documentPropertiesRaw){
262
+ let result = {};
263
+ if(!_.isNil(documentPropertiesRaw)){
264
+ // this means the contract has document properties set
265
+ _.forEach(documentPropertiesRaw.elements, ele => {
266
+
267
+ if(ele.type === ELEMENT_TYPE.ELEMENT){
268
+ let sanitizedKey = ele.attributes.key.replace(/\s/g, "");
269
+ result[sanitizedKey]= ele.attributes.value;
270
+ }
271
+ });
272
+ }
273
+ return result;
274
+ }
275
+
276
+ __findPrintLayoutNode(contractJson) {
277
+ let printLayoutNode = _.find(contractJson.elements, {name:PRINT_LAYOUT_NODE});
278
+ if(_.isNil(printLayoutNode)) {
279
+ throw new Error("Missing PrintLayout node.");
280
+ }
281
+ return printLayoutNode;
282
+ }
283
+
284
+ __getDeclarationsNode(contractJson) {
285
+ let printLayoutNode = this.__findPrintLayoutNode(contractJson);
286
+ let declarationsNode = _.find(printLayoutNode.elements, {name: DECLARATIONS_NODE});
287
+ if(_.isNil(declarationsNode)) {
288
+ declarationsNode = {};
289
+ }
290
+ return declarationsNode;
291
+ }
292
+ __newXMLconvertibleJsonNode(type, children, otherAttributes) {
293
+ let newNode = {
294
+ "type": type
295
+ };
296
+ if(!_.isNil(children)) {
297
+ newNode.elements = _.castArray(children);
298
+ }
299
+ _.assign(newNode, otherAttributes);
300
+ return newNode;
301
+ }
302
+
303
+ __walkParseTree(contractJson, parentElement, parentName, params) {
304
+ // processing ...
305
+ this.__processCurrentElement(contractJson, parentElement, parentName, params);
306
+ // recursion ...
307
+ if(!_.isNil(contractJson.elements)) {
308
+ _.remove(contractJson.elements, (ele)=> {
309
+ if (ele.type === ELEMENT_TYPE.COMMENT) {
310
+ return true;
311
+ } else {
312
+ return false;
313
+ }
314
+ });
315
+ _.forEach(contractJson.elements, (ele) => {
316
+ this.__walkParseTree(ele, contractJson, contractJson.name, params);
317
+ });
318
+ }
319
+ // post-processing ...
320
+ this.__postProcessCurrentElement(contractJson);
321
+ }
322
+
323
+ __postProcessCurrentElement(currentJsonElement) {
324
+ if (!_.isNil(currentJsonElement)) {
325
+ if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT) {
326
+ if (currentJsonElement.name === TR_NODE) {
327
+
328
+ // perform colSpan handling ...
329
+ this.__applyColSpanHandling(currentJsonElement);
330
+
331
+ } else if (currentJsonElement.name === TABLE_NODE) {
332
+
333
+ // perform rowSpan handling ...
334
+ this.__applyRowSpanHandling(currentJsonElement);
335
+ // normalize table ...
336
+ this.__normalizeTable(currentJsonElement);
337
+ // check for empty table ...
338
+ this.__checkForEmptyTable(currentJsonElement);
339
+
340
+ }
341
+ }
342
+ }
343
+ }
344
+
345
+ __checkForDivToBeVisible(divNode, params) {
346
+ let result = true;
347
+ if (!_.isNil(divNode) && _.isArray(divNode.elements)) {
348
+
349
+ // set result to 'true' for easier evaluation ...
350
+ let divVisibilityNode = _.find(divNode.elements, {name: DIV_VISIBILITY_NODE});
351
+ if (!_.isNil(divVisibilityNode) && _.isArray(divVisibilityNode.elements)) {
352
+
353
+ // check for visibilityBinding elements ...
354
+ let divVisibilityBindingNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_BINDING_NODE});
355
+ if (!_.isNil(divVisibilityBindingNodes) && _.isArray(divVisibilityBindingNodes) && divVisibilityBindingNodes.length > 0) {
356
+ for (let i = 0; i < divVisibilityBindingNodes.length; i++) {
357
+ // process visibilityBinding ...
358
+ let divVisibilityBindingNode = divVisibilityBindingNodes[i];
359
+ result = this.__evaluateVisibilityBinding(divVisibilityBindingNode, params);
360
+ // exit if visibility criteria cannot be met ...
361
+ if (!result) {
362
+ break;
363
+ }
364
+ }
365
+ }
366
+
367
+ if (result) {
368
+ // check for visibilityCondition elements ...
369
+ let divVisibilityConditionNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_CONDITION_NODE});
370
+ if (!_.isNil(divVisibilityConditionNodes) && _.isArray(divVisibilityConditionNodes) && divVisibilityConditionNodes.length > 0) {
371
+ for (let i = 0; i < divVisibilityConditionNodes.length; i++) {
372
+ // process visibilityCondition ...
373
+ let divVisibilityConditionNode = divVisibilityConditionNodes[i];
374
+ result = this.__evaluateVisibilityCondition(divVisibilityConditionNode);
375
+ // exit if visibility criteria cannot be met ...
376
+ if (!result) {
377
+ break;
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ // => remove visibility node ...
384
+ _.remove(divNode.elements, node => (node.type === ELEMENT_TYPE.ELEMENT && node.name === DIV_VISIBILITY_NODE));
385
+ }
386
+
387
+ }
388
+ return result;
389
+ }
390
+
391
+ __evaluateVisibilityBinding(visiblityBindingNode, params) {
392
+ let result = true;
393
+
394
+ // <visiblityBinding binding="{{Declarations::header.itemCount}}" value="0" operator="GT" compareMode="NUMBER" />
395
+ if (!_.isNil(visiblityBindingNode)) {
396
+
397
+ let expression = this.__getVisibilityExpression(visiblityBindingNode, params);
398
+ if (!_.isNil(expression) && _.isFunction(expression)) {
399
+ result = expression();
400
+ }
401
+ }
402
+
403
+ return result;
404
+ }
405
+
406
+ __getVisibilityExpression(visiblityBindingOrCondition, params) {
407
+ let expression = null;
408
+
409
+ // extract attributes
410
+ let binding = null;
411
+ let eachName = null;
412
+ let compareMode = COMPAREMODE_DEFAULT;
413
+ let operator = visiblityBindingOrCondition.attributes.operator;
414
+ let value = visiblityBindingOrCondition.attributes.value;
415
+ let comparisonValue = null;
416
+
417
+ if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_BINDING_NODE) {
418
+ // => handle visibilityBinding
419
+ binding = visiblityBindingOrCondition.attributes.binding;
420
+ // get compareMode if available
421
+ if (_.has(visiblityBindingOrCondition.attributes, "compareMode")) {
422
+ compareMode = visiblityBindingOrCondition.attributes.compareMode;
423
+ }
424
+ // resolve binding
425
+ comparisonValue = this.__resolveScalarBinding(visiblityBindingOrCondition, binding, params);
426
+ if (_.isNil(comparisonValue)) {
427
+ // unknown binding => log error
428
+ this.__log.error("Unsupported binding '", binding, "' at <" + visiblityBindingOrCondition.name + "> element");
429
+ }
430
+
431
+ } else if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_CONDITION_NODE) {
432
+ // => handle visibilityCondition
433
+ eachName = visiblityBindingOrCondition.attributes.eachName;
434
+ // set compareMode to "NUMBER"
435
+ compareMode = COMPAREMODE_NUMBER;
436
+ // resolve eachName
437
+ if (_.has(this.__eachCache, eachName) && _.isArray(this.__eachCache[eachName])) {
438
+ comparisonValue = this.__eachCache[eachName].length;
439
+ }
440
+ if (_.isNil(comparisonValue)) {
441
+ // unknown eachName => log error
442
+ this.__log.error("Unsupported eachName '", eachName, "' at <" + visiblityBindingOrCondition.name + "> element");
443
+ }
444
+ }
445
+
446
+ // normalize compareMode & operator
447
+ compareMode = _.toUpper(compareMode);
448
+ operator = _.toUpper(operator);
449
+
450
+ if (!_.isNil(comparisonValue) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {
451
+
452
+ let converter = this.__resolveConverter(compareMode);
453
+ if (!_.isNil(converter) && _.isFunction(converter)) {
454
+
455
+ // convert comparison value once
456
+ let x = converter(comparisonValue);
457
+ // convert value once
458
+ let y = converter(value);
459
+
460
+ let comparator = this.__resolveComparator(operator);
461
+ if (!_.isNil(comparator) && _.isFunction(comparator)) {
462
+
463
+ // build filter expression performing comparison
464
+ expression = ( () => {
465
+ return comparator(x, y);
466
+ });
467
+
468
+ } else {
469
+ // unknown operator => log error
470
+ this.__log.error("Unsupported operator '", operator, "' at <" + visiblityBindingOrCondition.name + "> element");
471
+ }
472
+ } else {
473
+ // unknown compareMode => log error
474
+ this.__log.error("Unsupported compareMode '", compareMode, "' at <" + visiblityBindingOrCondition.name + "> element");
475
+ }
476
+ } else {
477
+ // missing/invalid mandatory attribute => log error
478
+ this.__log.error("Missing/invalid mandatory attribute at <" + visiblityBindingOrCondition.name + "> element");
479
+ }
480
+
481
+ return expression;
482
+ }
483
+
484
+ __evaluateVisibilityCondition(visiblityConditionNode) {
485
+ let result = true;
486
+
487
+ // <visiblityCondition eachName="each1" value="0" operator="GT" />
488
+ if (!_.isNil(visiblityConditionNode)) {
489
+ let expression = this.__getVisibilityExpression(visiblityConditionNode, null);
490
+ if (!_.isNil(expression) && _.isFunction(expression)) {
491
+ result = expression();
492
+ }
493
+ }
494
+
495
+ return result;
496
+ }
497
+
498
+ __checkForEmptyTable(tableNode) {
499
+ if (!_.isNil(tableNode)) {
500
+ // check thead
501
+ let thead = _.find(tableNode.elements, {name:THEAD_NODE});
502
+ if (_.isNil(thead) || _.isUndefined(thead.elements) || thead.elements.length === 0) {
503
+ // check tbody
504
+ let tbody = _.find(tableNode.elements, {name:TBODY_NODE});
505
+ if (_.isNil(tbody) || (!_.isNil(tbody) && !_.isUndefined(tbody.elements) && tbody.elements.length === 0)) {
506
+ // table is empty => to be removed
507
+ tableNode.toBeRemoved = true;
508
+ }
509
+ }
510
+ }
511
+ }
512
+
513
+ __applyColSpanHandling(tableRowNode) {
514
+ let newElements = [];
515
+ _.forEachRight(tableRowNode.elements, tdth => {
516
+ if (!_.isNil(tdth.attributes)) {
517
+ let colSpan = tdth.attributes.colSpan;
518
+ if (!_.isNil(colSpan)) {
519
+ colSpan = _.toInteger(colSpan);
520
+ if (!_.isNaN(colSpan) && colSpan > 1) {
521
+ let elementsToAdd = colSpan - 1;
522
+ let elementName = tdth.name;
523
+
524
+ let newAttributes = {
525
+ "name": elementName,
526
+ "doNotReplace": true
527
+ };
528
+ for (let i = 0; i < elementsToAdd; i++) {
529
+ let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, newAttributes);
530
+ newElements.push(newElement);
531
+ }
532
+ }
533
+ }
534
+ }
535
+ newElements.push(tdth);
536
+ });
537
+ // replace elements if applicable
538
+ let elementCount = 0;
539
+ if (!_.isNil(tableRowNode.elements)) {
540
+ elementCount = tableRowNode.elements.length;
541
+ }
542
+ if (newElements.length !== elementCount) {
543
+ tableRowNode.elements = _.reverse(newElements);
544
+ }
545
+ }
546
+
547
+ __applyRowSpanHandling(tableNode) {
548
+ if (!_.isNil(tableNode)) {
549
+ // fix thead
550
+ this.__applyRowSpanHandlingToContainer(tableNode, THEAD_NODE);
551
+
552
+ // fix tbody
553
+ this.__applyRowSpanHandlingToContainer(tableNode, TBODY_NODE);
554
+ }
555
+ }
556
+
557
+ __applyRowSpanHandlingToContainer(tableNode, trContainerNodeName) {
558
+
559
+ if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {
560
+ // check tbody / thead
561
+ let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});
562
+ if (!_.isNil(trContainerNode) && !_.isNil(trContainerNode.elements)) {
563
+ for (let i = 0; i < trContainerNode.elements.length; i++) {
564
+ // check tr
565
+ let tr = trContainerNode.elements[i];
566
+ if (!_.isNil(tr) && !_.isNil(tr.elements)) {
567
+ for (let j = 0; j < tr.elements.length; j++) {
568
+ // check td / th
569
+ let tdth = tr.elements[j];
570
+ if (!_.isNil(tdth)) {
571
+ if (!_.isNil(tdth.attributes)) {
572
+ let rowSpan = tdth.attributes.rowSpan;
573
+ if (!_.isNil(rowSpan)) {
574
+ rowSpan = _.toInteger(rowSpan);
575
+ if (!_.isNaN(rowSpan) && rowSpan > 1) {
576
+ let rowsToUpdate = rowSpan - 1;
577
+ let elementsToAdd = 1; // rowSpan element
578
+ let elementName = tdth.name;
579
+ let colSpan = tdth.attributes.colSpan;
580
+ if (!_.isNil(colSpan)) {
581
+ colSpan = _.toInteger(colSpan);
582
+ if (!_.isNaN(colSpan) && colSpan > 1) {
583
+ elementsToAdd = elementsToAdd + (colSpan - 1); // colSpan elements
584
+ }
585
+ }
586
+ // update "spanned" rows
587
+ for (let k = 0; k < rowsToUpdate; k++) {
588
+ let rowIndex = i + k + 1;
589
+ if (rowIndex < trContainerNode.elements.length) {
590
+ let tr2 = trContainerNode.elements[rowIndex];
591
+
592
+ if (_.isNil(tr2.elements)) {
593
+ _.assign(tr2, {"elements": []});
594
+ }
595
+
596
+ let tr2Index = j;
597
+ if (tr2Index > tr2.elements.length) {
598
+ tr2Index = 0;
599
+ }
600
+
601
+ // insert elements ...
602
+ for (let l = 0; l < elementsToAdd; l++) {
603
+ let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, {"name": elementName, "doNotReplace": true});
604
+ tr2.elements.splice(tr2Index, 0, newElement);
605
+ }
606
+ }
607
+ }
608
+ }
609
+ }
610
+ }
611
+ }
612
+ }
613
+ }
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ __normalizeTable(tableNode) {
620
+ if (!_.isNil(tableNode)) {
621
+ // get minimum thead width
622
+ let minTableHeadWidth = this.__getTableContainerWidth(tableNode, THEAD_NODE);
623
+
624
+ // get minimum tbody width
625
+ let minTableBodyWidth = this.__getTableContainerWidth(tableNode, TBODY_NODE);
626
+
627
+ // determine minimum width for table
628
+ let minTableWidth = _.min([minTableHeadWidth, minTableBodyWidth]);
629
+
630
+ // repair thead
631
+ this.__normalizeTableWidth(tableNode, THEAD_NODE, minTableWidth);
632
+
633
+ // repair tbody
634
+ this.__normalizeTableWidth(tableNode, TBODY_NODE, minTableWidth);
635
+ }
636
+ }
637
+
638
+ __getTableContainerWidth(tableNode, trContainerNodeName) {
639
+ let minWidth = Number.MAX_SAFE_INTEGER;
640
+ if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {
641
+ let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});
642
+ if (!_.isNil(trContainerNode)) {
643
+ _.forEach(trContainerNode.elements, tr => {
644
+ if (!_.isNil(tr) && tr.name === TR_NODE && !_.isNil(tr.elements)) {
645
+ let thCount = tr.elements.length;
646
+ if (thCount < minWidth) {
647
+ minWidth = thCount;
648
+ }
649
+ }
650
+ });
651
+ }
652
+ }
653
+ return minWidth;
654
+ }
655
+
656
+ __normalizeTableWidth(tableNode, trContainerNodeName, tableWidth) {
657
+ if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {
658
+ let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});
659
+ if (!_.isNil(trContainerNode)) {
660
+ let normalized = false;
661
+ _.forEach(trContainerNode.elements, tr => {
662
+ if (!_.isNil(tr) && tr.name === TR_NODE) {
663
+ let trWidth = 0;
664
+ if (!_.isNil(tr.elements)) {
665
+ trWidth = tr.elements.length;
666
+ }
667
+ if (trWidth > tableWidth) {
668
+ // remove elements ...
669
+ tr.elements.splice(tableWidth, trWidth - tableWidth);
670
+ normalized = true;
671
+ }
672
+ }
673
+ });
674
+ if (normalized) {
675
+ let tableName = tableNode.attributes.name;
676
+ let errorMessage = "<" + trContainerNodeName + "> element of <table> '" + tableName + "' has been normalized due to improper formatting";
677
+ this.__log.error(errorMessage);
678
+ }
679
+ }
680
+ }
681
+ }
682
+
683
+ __processSumRecords() {
684
+ let idxSum;
685
+ for (idxSum=0; idxSum < this.__sumTdThList.length; idxSum++) {
686
+ let tdthNode = this.__sumTdThList[idxSum];
687
+ let sumNode = _.find(tdthNode.elements, {name:SUM_NODE});
688
+ let tableName = sumNode.attributes.table;
689
+ let tablePos = parseFloat(sumNode.attributes.col, 10);
690
+ let sumFormat = sumNode.attributes.numberFormat;
691
+ let tableNode = this.__tableIndex[tableName];
692
+ let sum = 0;
693
+
694
+ if (!_.isNil(tableNode)) {
695
+ let tBodyNode = _.find(tableNode.elements, {name:TBODY_NODE});
696
+ if (!_.isNil(tBodyNode) && !_.isNil(tBodyNode.elements)) {
697
+ _.forEach(tBodyNode.elements, (trNode) => {
698
+ let tdNode = trNode.elements[tablePos];
699
+ if(!_.isNil(tdNode)) {
700
+ let bSum = false;
701
+ let text = "";
702
+ let nonRoundedValue = null;
703
+ if (!_.isNil(tdNode.attributes)) {
704
+ nonRoundedValue = tdNode.attributes.nonRoundedValue;
705
+ }
706
+ _.forEach(tdNode.elements, childElement => {
707
+ if ((childElement.type === "text") && !_.isNil(childElement.isSum) && childElement.isSum) {
708
+ bSum = true;
709
+ } else if (childElement.type === "text") {
710
+ text = childElement.text;
711
+ } else if (childElement.type === "element" && childElement.name === SUM_NODE) {
712
+ bSum = true;
713
+ }
714
+ });
715
+ if (!bSum) {
716
+ if (!_.isNil(nonRoundedValue)) {
717
+ // nonRoundedValue only exists if a format was applied to the cell
718
+ sum += nonRoundedValue;
719
+ } else {
720
+ let value = parseFloat(text, 10);
721
+ if (!isNaN(value)) {
722
+ sum += value;
723
+ }
724
+ }
725
+
726
+ }
727
+ }
728
+ });
729
+ } else {
730
+ sum = 0;
731
+ }
732
+ } else {
733
+ sum = 0;
734
+ }
735
+
736
+ let txtSum;
737
+
738
+ if (!_.isNil(sumFormat)) {
739
+ txtSum = this.__format.formatDecimalV2(sum, sumFormat, this.__decimalSeparator, this.__thousandSeparator);
740
+ } else {
741
+ txtSum = "" + sum;
742
+ }
743
+
744
+ let bText = false;
745
+
746
+ _.forEach(tdthNode.elements, childElement => {
747
+ if(childElement.type === "text") {
748
+ childElement.text = txtSum + childElement.text;
749
+ childElement.isSum = true;
750
+ bText = true;
751
+ }
752
+ });
753
+
754
+ if (!bText) {
755
+ if (txtSum.length > 0) {
756
+ tdthNode.elements.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {"text": txtSum, "doNotReplace": true, "isSum": true}));
757
+ }
758
+ }
759
+
760
+ this.__deleteInvalidNodes(tdthNode, [SUM_NODE], null);
761
+ }
762
+ }
763
+
764
+ __processH1Node(currentJsonElement/*, params*/) {
765
+ this.__makeTextOnlyNode(currentJsonElement);
766
+ }
767
+
768
+ __processH2Node(currentJsonElement/*, params*/) {
769
+ this.__makeTextOnlyNode(currentJsonElement);
770
+ }
771
+
772
+ __processParagraphNode(currentJsonElement/*, params*/) {
773
+ this.__makeTextOnlyNode(currentJsonElement);
774
+ }
775
+
776
+ __processPropertyNode(currentJsonElement, params){
777
+ let resolvedMacro = this.__resolveScalarBinding(currentJsonElement, currentJsonElement.attributes.value, params);
778
+ if(!_.isNil(resolvedMacro)) {
779
+ if(_.isObject(resolvedMacro)) {
780
+ resolvedMacro = JSON.stringify(resolvedMacro);
781
+ }
782
+ if(!_.isString(resolvedMacro)){
783
+ resolvedMacro = ""+resolvedMacro;
784
+ }
785
+ currentJsonElement.attributes.value = resolvedMacro;
786
+ }
787
+ }
788
+
789
+ __processPrintLayoutNode(currentJsonElement/*, parentName, params*/){
790
+ this.__printLayoutName = currentJsonElement.attributes.name;
791
+ }
792
+
793
+ __processCurrentElement(currentJsonElement, parentElement, parentName, params) {
794
+ if (currentJsonElement.doNotReplace) {
795
+ return;
796
+ }
797
+ if(currentJsonElement.type === ELEMENT_TYPE.TEXT) {
798
+ this.__replaceMacrosInTextNodes(parentElement, currentJsonElement, params);
799
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TBODY_NODE) {
800
+ this.__replaceEachTagsInTables(currentJsonElement, parentElement, params);
801
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === HEADER_NODE){
802
+ this.__processHeaderNode(currentJsonElement, params);
803
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === FOOTER_NODE){
804
+ this.__processFooterNode(currentJsonElement, params);
805
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === REPORT_LAYOUT_NODE){
806
+ this.__processReportLayoutNode(currentJsonElement, params);
807
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TABLE_NODE){
808
+ this.__processTableNode(currentJsonElement, params);
809
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === THEAD_NODE){
810
+ this.__processTheadNode(currentJsonElement, params);
811
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H1) {
812
+ this.__processH1Node(currentJsonElement, params);
813
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H2) {
814
+ this.__processH2Node(currentJsonElement, params);
815
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PARAGRAPH) {
816
+ this.__processParagraphNode(currentJsonElement, params);
817
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === IMAGE) {
818
+ this.__processImageNode(currentJsonElement, params);
819
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PROPERTY_NODE) {
820
+ this.__processPropertyNode(currentJsonElement, params);
821
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TR_NODE){
822
+ this.__processTrNode(currentJsonElement, parentName, params);
823
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TD_NODE){
824
+ this.__processTdThNode(currentJsonElement, parentName, params);
825
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TH_NODE){
826
+ this.__processTdThNode(currentJsonElement, parentName, params);
827
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PRINT_LAYOUT_NODE){
828
+ this.__processPrintLayoutNode(currentJsonElement, parentName, params);
829
+ } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === DIV_NODE){
830
+ this.__processDivNode(currentJsonElement, parentElement);
831
+ }
832
+ }
833
+
834
+ __processDivNode(divNode, parentNode) {
835
+ // cache divNode and parentNode for later ...
836
+ this.__divCache.push({
837
+ "id" : "div" + (this.__divCache.length + 1),
838
+ "divNode" : divNode, // div to evaluate
839
+ "parentNode": parentNode // parent to resolve
840
+ });
841
+ }
842
+
843
+ __parse(contract){
844
+ let contractJson = parser.xml2js(contract,this.__xmlParserLibraryOptions);
845
+ return contractJson;
846
+ }
847
+
848
+ __isLo(object){
849
+ return _.isArray(object);
850
+ }
851
+
852
+ __makeTextOnlyNode(currentJsonElement) {
853
+ _.remove(currentJsonElement.elements, (ele)=> { return ele.type !== ELEMENT_TYPE.TEXT;});
854
+ if(currentJsonElement.elements.length === 0) {
855
+ currentJsonElement.elements.push({type:ELEMENT_TYPE.TEXT, text: ""});
856
+ }
857
+ }
858
+
859
+ __deleteInvalidNodes(currentJsonElement, deleteNames, preserveNames) {
860
+ //Remove invalid nodes by positive or negative lists.
861
+ let bDelete = !_.isNil(deleteNames) && _.isArray(deleteNames) && (deleteNames.length > 0);
862
+ let bPreserve = !_.isNil(preserveNames) && _.isArray(preserveNames) && (preserveNames.length > 0);
863
+
864
+ //It makes no sense to fill both lists or no list but we cannot provide a true/true or a false/false
865
+ //scenario via Unit Tests. So we keep both paths split to fulfill the branch coverage.
866
+ if (bDelete) {
867
+ _.remove(currentJsonElement.elements, (ele)=> {
868
+ let names = 0;
869
+ for (names=0; names<deleteNames.length; names++) {
870
+ if (ele.name === deleteNames[names]) {
871
+ return true;
872
+ }
873
+ }
874
+ return false;
875
+ });
876
+ }
877
+ if (bPreserve) {
878
+ _.remove(currentJsonElement.elements, (ele)=> {
879
+ let names = 0;
880
+ for (names=0; names<preserveNames.length; names++) {
881
+ if (ele.name === preserveNames[names]) {
882
+ return false;
883
+ }
884
+ }
885
+ return true;
886
+ });
887
+ }
888
+ }
889
+
890
+ __processTheadNode(currentJsonElement/*, parentName, params*/){
891
+ this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);
892
+ }
893
+
894
+ __processTableNode(currentJsonElement/*, parentName, params*/){
895
+ this.__deleteInvalidNodes(currentJsonElement, null, [THEAD_NODE, TBODY_NODE]);
896
+ if (_.isNil(this.__tableIndex[currentJsonElement.attributes.name])) {
897
+ this.__tableIndex[currentJsonElement.attributes.name] = currentJsonElement;
898
+ }
899
+ }
900
+
901
+ __processTrNode(currentJsonElement, parentName/*, params*/){
902
+ if (parentName === THEAD_NODE) {
903
+ this.__deleteInvalidNodes(currentJsonElement, null, [TH_NODE]);
904
+ } else if (parentName === TBODY_NODE) {
905
+ this.__deleteInvalidNodes(currentJsonElement, null, [TD_NODE]);
906
+ }
907
+ }
908
+
909
+ __processTdThNode(currentJsonElement, /*, parentName, params*/){
910
+ let sumNode = _.find(currentJsonElement.elements, {name:SUM_NODE});
911
+
912
+ if (!_.isNil(sumNode)) {
913
+ this.__sumTdThList.push(currentJsonElement);
914
+ }
915
+ }
916
+
917
+ __processReportLayoutNode(currentJsonElement/*, parentName, params*/) {
918
+ this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);
919
+ }
920
+
921
+ __processHeaderNode(currentJsonElement/*, parentName, params*/) {
922
+ this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);
923
+ }
924
+
925
+ __processFooterNode(currentJsonElement/*, parentName, params*/) {
926
+ this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);
927
+ }
928
+
929
+ __createTableDataCache(tableRow, params) {
930
+ let tdCacheList = [];
931
+
932
+ _.forEach(tableRow.elements, subtag => {
933
+ let tdCacheElement = {};
934
+ tdCacheElement.elements = [];
935
+
936
+ if (subtag.name === "td") {
937
+ if (!_.isNil(subtag.elements)) {
938
+ let allTextNodeTexts = [];
939
+ _.forEach(subtag.elements, tdSubtag => {
940
+ if (tdSubtag.type === ELEMENT_TYPE.TEXT) {
941
+ this.__replaceMacrosInTextNodes(subtag, tdSubtag, params);
942
+ allTextNodeTexts.push(_.trim(tdSubtag.text));
943
+ } else if (tdSubtag.type === ELEMENT_TYPE.ELEMENT && (tdSubtag.name === IMAGE)) {
944
+ this.__processImageNode(tdSubtag, params);
945
+ tdSubtag.doNotReplace = true;
946
+ tdCacheElement.elements.push(tdSubtag);
947
+ }
948
+ });
949
+
950
+ tdCacheElement.text = allTextNodeTexts.join(' ');
951
+ tdCacheElement.macroList = [];
952
+
953
+ let macroRegex = this.__macroHandler.getMacroRegex();
954
+ let macroMatches = tdCacheElement.text.match(macroRegex);
955
+
956
+ _.forEach(macroMatches, match => {
957
+ let macroElement = {"match": match, "result": this.__macroHandler.parseMacro(match)};
958
+ tdCacheElement.macroList.push(macroElement);
959
+ });
960
+
961
+
962
+ } else {
963
+ tdCacheElement.text = "";
964
+ }
965
+
966
+ tdCacheElement.attributes = subtag.attributes;
967
+ tdCacheList.push(tdCacheElement);
968
+ }
969
+ });
970
+
971
+ return tdCacheList;
972
+ }
973
+
974
+ __createTableTrForOneListItem(tdCacheList, listItem, trAttributes, isChildItem, eachName, listItemIndex, correlatedListItemIndex) {
975
+ let tdNodes = [];
976
+ let newText = null;
977
+ let newAttributes = null;
978
+
979
+ // id handling...
980
+ let cellId = null;
981
+ let columnIndex = -1;
982
+
983
+ _.forEach(tdCacheList, cacheElement => {
984
+ newText = cacheElement.text;
985
+ newAttributes = _.clone(cacheElement.attributes);
986
+
987
+ if (cacheElement.text.length > 0) {
988
+ _.forEach(cacheElement.macroList, macroElement => {
989
+ let macroResult = macroElement.result;
990
+
991
+ if (!_.isNil(macroResult) && !_.isNil(macroResult.path)) {
992
+ let listItemValue = listItem[macroResult.path];
993
+
994
+ if(typeof listItemValue !== "undefined") {
995
+ if (!_.isNil(macroResult.numberFormat)) {
996
+ if(_.isNil(newAttributes)) {newAttributes = {};}
997
+ listItemValue = this.__resolveNumberBinding(listItemValue, macroResult,newAttributes);
998
+ if(isChildItem){
999
+ // setting the nonRoundedValue to 0.0 makes sure that correlation items are ignored in the sum tag
1000
+ newAttributes.nonRoundedValue = 0.0;
1001
+ }
1002
+ } if(!_.isNil(macroResult.dateTimeFormat)) {
1003
+ listItemValue = this.__resolveDateBinding(listItemValue, macroResult);
1004
+ } else {
1005
+ listItemValue = this.__replaceTogglesIfApplicable(listItemValue, macroResult, this.__localization);
1006
+ }
1007
+
1008
+ if(_.isString(listItemValue)){
1009
+ listItemValue = listItemValue.replace(/\$/g, "$$$$");
1010
+ }
1011
+
1012
+ newText = newText.replace(macroElement.match, listItemValue);
1013
+ }
1014
+ }
1015
+ });
1016
+ }
1017
+
1018
+ let newTextNode = [];
1019
+ if(newText.length > 0){
1020
+ newTextNode.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {"text": newText, "doNotReplace": true}));
1021
+ }
1022
+
1023
+ // only apply id to cells with text OR child elements ...
1024
+ if (newTextNode.length > 0 || cacheElement.elements.length > 0) {
1025
+ columnIndex += 1;
1026
+ cellId = this.__getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex);
1027
+ if (!_.isNil(cellId)) {
1028
+ if (_.isNil(newAttributes)) { newAttributes = {}; }
1029
+ newAttributes.id = cellId;
1030
+ }
1031
+ }
1032
+
1033
+ let newTdNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, newTextNode,
1034
+ {"name": "td", "attributes": newAttributes, "doNotReplace": true});
1035
+
1036
+ _.forEach(cacheElement.elements, element => {
1037
+ newTdNode.elements.push(element);
1038
+ });
1039
+
1040
+ tdNodes.push(newTdNode);
1041
+ });
1042
+
1043
+ let newTrNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, tdNodes,
1044
+ {"name": "tr", "attributes": trAttributes, "doNotReplace": true});
1045
+ return newTrNode;
1046
+ }
1047
+
1048
+ __createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName) {
1049
+
1050
+ if (!_.isNil(tableRow) && !_.isNil(tdCacheList) && !_.isNil(trNodes) && !_.isNil(listObject) && !_.isNil(eachName)) {
1051
+
1052
+ let hasCorrelationInfo = !_.isNil(correlationInfo);
1053
+ let listItemIndex = -1;
1054
+ let correlatedListItemIndex = -1;
1055
+
1056
+ _.forEach(listObject, listItem => {
1057
+
1058
+ if (hasCorrelationInfo) {
1059
+ listItemIndex += 1;
1060
+ correlatedListItemIndex = -1;
1061
+ }
1062
+
1063
+ let newTrNode = this.__createTableTrForOneListItem(tdCacheList, listItem, tableRow.attributes, false, eachName, listItemIndex, correlatedListItemIndex);
1064
+ trNodes.push(newTrNode);
1065
+
1066
+ if (!_.isNil(correlationInfo) && !_.isNil(listItem[correlationInfo.key])) {
1067
+ // TODO: this implementation is slow and can be done faster.
1068
+ let correlatedItemsForCurrentLI = _.filter(correlationInfo.listObjectForCorrelation, (elem) => {
1069
+ return elem[correlationInfo.correlationKey] === listItem[correlationInfo.key];
1070
+ });
1071
+
1072
+ // (re)set correlatedListItemIndex...
1073
+ correlatedListItemIndex = -1;
1074
+
1075
+ _.forEach(correlatedItemsForCurrentLI, corrLi => {
1076
+
1077
+ // set correlatedListItemIndex...
1078
+ correlatedListItemIndex += 1;
1079
+
1080
+ let newCorrelationTrNode = this.__createTableTrForOneListItem(correlationInfo.tdCacheForCorrelation, corrLi, {}, true, eachName, listItemIndex, correlatedListItemIndex);
1081
+ trNodes.push(newCorrelationTrNode);
1082
+ });
1083
+ }
1084
+ });
1085
+ }
1086
+
1087
+ return trNodes;
1088
+ }
1089
+
1090
+ __buildCorrelationInfo(correlationNode, params){
1091
+ let correlationInfo = null;
1092
+ if(!_.isNil(correlationNode)){
1093
+ correlationInfo = {};
1094
+ correlationInfo.correlationTableRow = _.find(correlationNode.elements, {name:TR_NODE}); // eachNode.elements[0];
1095
+ correlationInfo.tdCacheForCorrelation = this.__createTableDataCache(correlationInfo.correlationTableRow, params);
1096
+ correlationInfo.listObjectForCorrelation = this.__resolveScalarBinding(correlationNode,
1097
+ correlationNode.attributes.value, params);
1098
+ // => get and apply filters
1099
+ correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyFilters(correlationNode,
1100
+ correlationInfo.listObjectForCorrelation);
1101
+ // => get and apply orderCriteria
1102
+ correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyOrderCriteria(correlationNode,
1103
+ correlationInfo.listObjectForCorrelation);
1104
+ correlationInfo.key = correlationNode.attributes.key;
1105
+ correlationInfo.correlationKey = correlationNode.attributes.correlationKey;
1106
+ }
1107
+ return correlationInfo;
1108
+ }
1109
+
1110
+ __resolveEachTag(eachNode, tableNode, params) {
1111
+ let trNodes = [];
1112
+
1113
+ if(!_.isNil(eachNode) && (eachNode.name === EACH_NODE)){
1114
+ let eachName = null;
1115
+ let listObject = null;
1116
+
1117
+ if (!_.isNil(eachNode.attributes)) {
1118
+ eachName = eachNode.attributes.name;
1119
+ listObject = this.__resolveScalarBinding(eachNode, eachNode.attributes.value, params);
1120
+ if(!this.__isLo(listObject)){
1121
+ listObject = null;
1122
+ }
1123
+ }
1124
+
1125
+ if (_.isNil(listObject)) {
1126
+ if(!_.isNil(eachNode.attributes)) {
1127
+ trNodes.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {"text": eachNode.attributes.value,
1128
+ "doNotReplace": true}));
1129
+ }
1130
+ }
1131
+
1132
+ this.__deleteInvalidNodes(eachNode, null, [TR_NODE, FILTERS_NODE, ORDERCRITERIA_NODE, CORRELATION_NODE]);
1133
+
1134
+ if (!_.isNil(eachNode.elements) && (eachNode.elements.length > 0)) {
1135
+
1136
+ // => get and apply filters
1137
+ listObject = this.__evaluateAndApplyFilters(eachNode, listObject);
1138
+ // => get and apply orderCriteria
1139
+ listObject = this.__evaluateAndApplyOrderCriteria(eachNode, listObject);
1140
+
1141
+ // cache each data ...
1142
+ this.__eachCache[eachName] = listObject;
1143
+
1144
+ // the correlation node is needed for printing the correlated items
1145
+ const correlationNode = _.find(eachNode.elements, {name:CORRELATION_NODE});
1146
+ let correlationInfo = this.__buildCorrelationInfo(correlationNode, params);
1147
+ if (!_.isNil(correlationInfo)) {
1148
+ // each node with correlation => enable dynamicPageBreak for table
1149
+ tableNode.attributes.dynamicPageBreak = "true"; // as string to keep pdfconverter working ...
1150
+ }
1151
+
1152
+ // => process table row (with filtered / sorted list object)
1153
+ let tableRow = _.find(eachNode.elements, {name:TR_NODE});
1154
+ if (!_.isNil(tableRow)) {
1155
+
1156
+ this.__deleteInvalidNodes(tableRow, null, [TD_NODE]);
1157
+
1158
+ if (!_.isNil(tableRow.elements) && (tableRow.elements.length > 0)) {
1159
+ let tdCacheList = this.__createTableDataCache(tableRow, params);
1160
+ trNodes = this.__createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName);
1161
+ }
1162
+ } else {
1163
+ // no table row => log error
1164
+ this.__log.error("<each> element must contain a <tr> (table row) element");
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ return trNodes;
1170
+ }
1171
+
1172
+ __evaluateAndApplyFilters(iterableNode, listObject) {
1173
+ // <filters>
1174
+ // <filter fieldName="quantity" value="0" operator="GT" compareMode="NUMBER" />
1175
+ // <filter fieldName="movementDirection" value="In" operator="NE" />
1176
+ // </filters>
1177
+
1178
+ let filtersNode = _.find(iterableNode.elements, {name:FILTERS_NODE});
1179
+ if (!_.isNil(filtersNode)) {
1180
+
1181
+
1182
+ this.__deleteInvalidNodes(filtersNode, null, [FILTER_NODE]);
1183
+
1184
+ if (!_.isNil(filtersNode.elements) && (filtersNode.elements.length > 0)) {
1185
+ _.forEach(filtersNode.elements, element => {
1186
+
1187
+ let filterExpression = this.__getAdvancedFilterExpression(element);
1188
+ if (!_.isNil(filterExpression) && _.isFunction(filterExpression)) {
1189
+ listObject = _.filter(listObject, filterExpression);
1190
+ }
1191
+
1192
+ });
1193
+ } else {
1194
+ // no filter elements => log error
1195
+ this.__log.error("<each>.<filters> element must contain at least one <filter> element");
1196
+ }
1197
+ }
1198
+ return listObject;
1199
+ }
1200
+
1201
+ __getAdvancedFilterExpression(filterNode) {
1202
+ let filterExpression = null;
1203
+
1204
+ // extract attributes
1205
+ let fieldName = filterNode.attributes.fieldName;
1206
+ let operator = filterNode.attributes.operator;
1207
+ let value = filterNode.attributes.value;
1208
+
1209
+ let compareMode = filterNode.attributes.compareMode;
1210
+ if (_.isNil(compareMode)) {
1211
+ compareMode = COMPAREMODE_DEFAULT;
1212
+ }
1213
+ // normalize compareMode
1214
+ compareMode = _.toUpper(compareMode);
1215
+
1216
+ if (!_.isNil(fieldName) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {
1217
+
1218
+ let converter = this.__resolveConverter(compareMode);
1219
+ if (!_.isNil(converter) && _.isFunction(converter)) {
1220
+
1221
+ // convert value just once
1222
+ let y = converter(value);
1223
+
1224
+ // normalize operator
1225
+ operator = _.toUpper(operator);
1226
+
1227
+ let comparator = this.__resolveComparator(operator);
1228
+ if (!_.isNil(comparator) && _.isFunction(comparator)) {
1229
+
1230
+ // build filter expression
1231
+ filterExpression = (object => {
1232
+ // convert listobject value
1233
+ let x = converter(object[fieldName]);
1234
+ // perform comparison
1235
+ return comparator(x, y);
1236
+ });
1237
+
1238
+ } else {
1239
+ // unknown operator => log error
1240
+ this.__log.error("Unsupported operator '", operator, "' at <each>.<filters>.<filter> element");
1241
+ }
1242
+
1243
+ } else {
1244
+ // unknown compareMode => log error
1245
+ this.__log.error("Unsupported compareMode '", compareMode, "' at <each>.<filters>.<filter> element");
1246
+ }
1247
+
1248
+ } else {
1249
+ // missing mandatory attribute => log error
1250
+ this.__log.error("Missing mandatory attribute at <each>.<filters>.<filter> element");
1251
+ }
1252
+
1253
+ return filterExpression;
1254
+ }
1255
+
1256
+ __resolveComparator(comparator) {
1257
+ const COMPARATORS = {
1258
+ "EQ" : function (x, y) { return (x === y); },
1259
+ "NE" : function (x, y) { return (x !== y); },
1260
+ "GT" : function (x, y) { return (x > y); },
1261
+ "GE" : function (x, y) { return (x >= y); },
1262
+ "LT" : function (x, y) { return (x < y); },
1263
+ "LE" : function (x, y) { return (x <= y); }
1264
+ };
1265
+
1266
+ return COMPARATORS[comparator];
1267
+ }
1268
+
1269
+ __resolveConverter(compareMode) {
1270
+ const CONVERTERS = {
1271
+ //"NONE" : function (x) { return x; },
1272
+ "STRING" : function (x) {
1273
+ if (!_.isString(x)) {
1274
+ return _.toString(x);
1275
+ } else {
1276
+ return x;
1277
+ }
1278
+ },
1279
+ "NUMBER" : function (x) {
1280
+ if (!_.isNumber(x)) {
1281
+ return _.toNumber(x);
1282
+ } else {
1283
+ return x;
1284
+ }
1285
+ }
1286
+ };
1287
+
1288
+ return CONVERTERS[compareMode];
1289
+ }
1290
+
1291
+ __evaluateAndApplyOrderCriteria(iterableNode, listObject) {
1292
+ // <orderCriteria>
1293
+ // <orderCriterion fieldName="eAN" direction="ASC" compareMode="NUMBER" />
1294
+ // </orderCriteria>
1295
+ let orderCriteriaNode = _.find(iterableNode.elements, {name:ORDERCRITERIA_NODE});
1296
+ if (!_.isNil(orderCriteriaNode)) {
1297
+
1298
+ this.__deleteInvalidNodes(orderCriteriaNode, null, [ORDERCRITERION_NODE]);
1299
+
1300
+ if (!_.isNil(orderCriteriaNode.elements) && (orderCriteriaNode.elements.length > 0)) {
1301
+
1302
+ let selectors = [];
1303
+ let directions = [];
1304
+
1305
+ _.forEach(orderCriteriaNode.elements, element => {
1306
+
1307
+ // extract attributes
1308
+ let fieldName = element.attributes.fieldName;
1309
+ let direction = element.attributes.direction;
1310
+
1311
+ let compareMode = element.attributes.compareMode;
1312
+ if (_.isNil(compareMode)) {
1313
+ compareMode = COMPAREMODE_DEFAULT;
1314
+ }
1315
+ // normalize compareMode
1316
+ compareMode = _.toUpper(compareMode);
1317
+
1318
+ if (!_.isNil(fieldName) && !_.isNil(direction) && !_.isNil(compareMode)) {
1319
+
1320
+ // get converter
1321
+ let converter = this.__resolveConverter(compareMode);
1322
+ if (!_.isNil(converter) && _.isFunction(converter)) {
1323
+
1324
+ // build & store selector
1325
+ let selector = (object => {
1326
+ return converter(object[fieldName]);
1327
+ });
1328
+ selectors.push(selector);
1329
+
1330
+ // store direction
1331
+ directions.push(_.toLower(direction));
1332
+
1333
+ } else {
1334
+ // unknown compareMode => log error
1335
+ this.__log.error("Unsupported compareMode '", compareMode , "' at <each>.<orderCriteria>.<orderCriterion> element");
1336
+ }
1337
+
1338
+ } else {
1339
+ // missing mandatory attribute => log error
1340
+ this.__log.error("Missing mandatory attribute at <each>.<orderCriteria>.<orderCriterion> element");
1341
+ }
1342
+ });
1343
+
1344
+ if (selectors.length > 0 && directions.length > 0 && selectors.length === directions.length) {
1345
+ // sort using selectors & directions
1346
+ listObject = _.orderBy(listObject, selectors, directions);
1347
+ } else {
1348
+ // no order criterion fields / directions => log error
1349
+ this.__log.error("<each>.<orderCriteria> element does not contain any applicable <orderCriterion> elements - sorting skipped");
1350
+ }
1351
+
1352
+ } else {
1353
+ // no order criterion elements => log error
1354
+ this.__log.error("<each>.<orderCriteria> element must contain at least one <orderCriterion> element");
1355
+ }
1356
+ }
1357
+ return listObject;
1358
+ }
1359
+
1360
+ __replaceEachTagsInTables(tbodyNode, tableNode, params) {
1361
+ let eachNode = _.find(tbodyNode.elements, {name:EACH_NODE});
1362
+
1363
+ if (!_.isNil(eachNode)){
1364
+ let newTableRows = [];
1365
+
1366
+ _.forEach(tbodyNode.elements, (rowNode) => {
1367
+ if (rowNode.name === EACH_NODE) {
1368
+ newTableRows = _.concat(newTableRows, this.__resolveEachTag(rowNode, tableNode, params));
1369
+ } else {
1370
+ newTableRows.push(rowNode);
1371
+ }
1372
+ });
1373
+
1374
+ tbodyNode.elements = newTableRows;
1375
+ }
1376
+ }
1377
+
1378
+ __isBoDeclared(boName){
1379
+ let isDeclared = false;
1380
+
1381
+ _.forEach(this.__declarationsNode.elements, declaration =>{
1382
+ if(declaration.name === "DataDeclaration") {
1383
+ if(declaration.attributes.name === boName){
1384
+ isDeclared = true;
1385
+ }
1386
+ }
1387
+ });
1388
+
1389
+ return isDeclared;
1390
+ }
1391
+
1392
+ __replaceTogglesIfApplicable(resolvedMacro, macroResult, localization){
1393
+ let result = resolvedMacro;
1394
+ if (!_.isNil(macroResult.toggleId)) {
1395
+ try {
1396
+ if (!_.isNil(macroResult.toggleField)) {
1397
+ result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro, macroResult.toggleField);
1398
+ } else {
1399
+ result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro);
1400
+ }
1401
+ } catch(ex){
1402
+ if(ex.message.indexOf("Invalid toggle value") > -1){
1403
+
1404
+ result = localization.resolve("Framework.PrintV2_ToggleValueNotFound", "Toggle value not found");
1405
+ result = result + ": " + resolvedMacro;
1406
+ } else if (ex.message.indexOf("Invalid toggle field") > -1) {
1407
+ result = localization.resolve("Framework.PrintV2_ToggleFieldNotFound", "Toggle field not found");
1408
+ result = result + ": " + macroResult.toggleField;
1409
+ } else {
1410
+ result = localization.resolve("Framework.PrintV2_ToggleNotFound", "Toggle not found");
1411
+ result = result + ": " + macroResult.toggleId;
1412
+ }
1413
+
1414
+ }
1415
+ }
1416
+ return result;
1417
+ }
1418
+
1419
+ __ensureAttributes(currentNode) {
1420
+ if(_.isNil(currentNode.attributes)){
1421
+ currentNode.attributes = {};
1422
+ }
1423
+ }
1424
+
1425
+ __resolveNumberBinding(resolvedMacro, macroResult, attributeNode){
1426
+ if(resolvedMacro !== "null" && !_.isNil(resolvedMacro)){
1427
+ attributeNode.nonRoundedValue = resolvedMacro;
1428
+ return this.__format.formatDecimalV2(resolvedMacro, macroResult.numberFormat,
1429
+ this.__decimalSeparator, this.__thousandSeparator);
1430
+ } else {
1431
+ return " ";
1432
+ }
1433
+
1434
+ }
1435
+
1436
+ __resolveDateBinding(resolvedMacro, macroResult){
1437
+ if(resolvedMacro !== "null" && !_.isNil(resolvedMacro)) {
1438
+ let actualDateTimeFormat = macroResult.dateTimeFormat;
1439
+ switch (actualDateTimeFormat){
1440
+ case "shortDate": actualDateTimeFormat = this.__localization.shortDateFormat;
1441
+ break;
1442
+ case "date": actualDateTimeFormat = this.__localization.dateFormat;
1443
+ break;
1444
+ case "dateTime": actualDateTimeFormat = this.__localization.dateTimeFormat;
1445
+ break;
1446
+ case "time": actualDateTimeFormat = this.__localization.timeFormat;
1447
+ break;
1448
+ default: break;
1449
+ }
1450
+ return this.__format.formatDate(resolvedMacro, actualDateTimeFormat);
1451
+ } else {
1452
+ return " ";
1453
+ }
1454
+ }
1455
+
1456
+ __resolveScalarBinding(currentNode, bindingText, params) {
1457
+ let bos = params.getBOs();
1458
+ let localization = this.__localization;
1459
+ let resolvedMacro = null;
1460
+ let macroResult = this.__macroHandler.parseMacro(bindingText);
1461
+
1462
+ if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {
1463
+ let scope = macroResult.scope;
1464
+ let bindingPath = macroResult.path;
1465
+
1466
+ if (scope === "Globals") {
1467
+ let defaultLabel = macroResult.defaultLabel;
1468
+ resolvedMacro = localization.resolve(bindingPath, defaultLabel);
1469
+ }
1470
+ else if (scope === "Labels") {
1471
+ let defaultLabel = macroResult.defaultLabel;
1472
+
1473
+ if(bindingPath.indexOf(".") === -1) {
1474
+ // support for shortcut
1475
+ let lastUnderscoreIndex = this.__printLayoutName.lastIndexOf("_");
1476
+ let printLayoutNameWithoutSpecificVersion = this.__printLayoutName;
1477
+ if(lastUnderscoreIndex > -1) {
1478
+ printLayoutNameWithoutSpecificVersion =
1479
+ this.__printLayoutName.substr(0, lastUnderscoreIndex);
1480
+ }
1481
+ bindingPath = "PrintLayouts." + printLayoutNameWithoutSpecificVersion + "." + bindingPath;
1482
+ }
1483
+
1484
+ resolvedMacro = localization.resolve(bindingPath, defaultLabel);
1485
+ } else if (scope === "Declarations") {
1486
+ let boEndIndex = bindingPath.indexOf(".");
1487
+ let boName;
1488
+
1489
+ if(boEndIndex === -1){
1490
+ // this means it is a top level binding like Declarations::data
1491
+ boName = bindingPath;
1492
+ boEndIndex = bindingPath.length;
1493
+ }else {
1494
+ boName = bindingPath.substring(0, boEndIndex);
1495
+ }
1496
+
1497
+ let isBoDeclared = this.__isBoDeclared(boName);
1498
+
1499
+ if(isBoDeclared){
1500
+ let deeperBindingPath = bindingPath.substring(boEndIndex + 1, bindingPath.length);
1501
+ let actualBo = _.get(bos, boName);
1502
+
1503
+ if(deeperBindingPath.length > 0){
1504
+ resolvedMacro = _.get(actualBo, deeperBindingPath);
1505
+ } else {
1506
+ // this means it is a top level binding like Declarations::data
1507
+ resolvedMacro = actualBo;
1508
+ }
1509
+
1510
+ if (!_.isNil(macroResult.numberFormat)) {
1511
+ this.__ensureAttributes(currentNode);
1512
+ resolvedMacro = this.__resolveNumberBinding(resolvedMacro, macroResult,currentNode.attributes);
1513
+ } if(!_.isNil(macroResult.dateTimeFormat)) {
1514
+ resolvedMacro = this.__resolveDateBinding(resolvedMacro, macroResult);
1515
+ } else {
1516
+ resolvedMacro = this.__replaceTogglesIfApplicable(resolvedMacro, macroResult, localization);
1517
+ }
1518
+
1519
+
1520
+ } else {
1521
+ this.__log.error("Usage of undeclared BO:", boName);
1522
+ }
1523
+ }
1524
+ }
1525
+
1526
+ return resolvedMacro;
1527
+ }
1528
+
1529
+ __replaceMacrosInTextNodes(parentJsonElement, currentJsonElement, params){
1530
+ let xmlNodeText = currentJsonElement.text;
1531
+ let macroRegex = this.__macroHandler.getMacroRegex();
1532
+ let arrayMatches = xmlNodeText.match(macroRegex);
1533
+
1534
+ if(!_.isNil(arrayMatches) && arrayMatches.length > 0) {
1535
+ _.forEach(arrayMatches, currentMatch => {
1536
+ let resolvedMacro = this.__resolveScalarBinding(parentJsonElement, currentMatch, params);
1537
+ if (!_.isNil(resolvedMacro)){
1538
+ if(_.isString(resolvedMacro)){
1539
+ resolvedMacro = resolvedMacro.replace(/\$/g, "$$$$");
1540
+ }
1541
+ xmlNodeText = xmlNodeText.replace(currentMatch, resolvedMacro);
1542
+ currentJsonElement.text = xmlNodeText;
1543
+ }
1544
+ });
1545
+ }
1546
+ }
1547
+
1548
+ __isImageDeclared(imageName){
1549
+ let isDeclared = false;
1550
+
1551
+ _.forEach(this.__declarationsNode.elements, declaration => {
1552
+ if(declaration.name === "DataDeclaration") {
1553
+ if ((declaration.attributes.name === imageName) && (declaration.attributes.type === "Image" || declaration.attributes.type === "Signature")) {
1554
+ isDeclared = true;
1555
+ }
1556
+ }
1557
+ });
1558
+
1559
+ return isDeclared;
1560
+ }
1561
+
1562
+ __sanitizeImageAttributes(currentJsonElement/*, params*/) {
1563
+ let width = currentJsonElement.attributes.width;
1564
+ let height = currentJsonElement.attributes.height;
1565
+ let fit = currentJsonElement.attributes.fit;
1566
+ this.__integerStringRegExp.lastIndex = 0; // reset last index
1567
+ if(!this.__integerStringRegExp.test(height)){
1568
+ delete currentJsonElement.attributes.height;
1569
+ }
1570
+ this.__integerStringRegExp.lastIndex = 0; // reset last index
1571
+ if(!this.__integerStringRegExp.test(width)){
1572
+ delete currentJsonElement.attributes.width;
1573
+ }
1574
+
1575
+ let fitRegExp = /^\d+,\d+$/g;
1576
+ if(!fitRegExp.test(fit)){
1577
+ delete currentJsonElement.attributes.fit;
1578
+ }
1579
+ if(!_.isNil(width) || !_.isNil(height)){
1580
+ delete currentJsonElement.attributes.fit;
1581
+ }
1582
+ }
1583
+
1584
+ __processImageNode(currentJsonElement, params){
1585
+ this.__replaceMacrosInImageNodes(currentJsonElement, params);
1586
+ this.__sanitizeImageAttributes(currentJsonElement, params);
1587
+ }
1588
+
1589
+ __replaceMacrosInImageNodes(currentJsonElement, params){
1590
+ let images = params.getImages();
1591
+ let bindingText = currentJsonElement.attributes.src;
1592
+ let macroResult = this.__macroHandler.parseMacro(bindingText);
1593
+
1594
+ if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {
1595
+ let scope = macroResult.scope;
1596
+ let imageName = macroResult.path;
1597
+ if (scope === "Declarations") {
1598
+ if (this.__isImageDeclared(imageName)) {
1599
+ let imageObject = images[imageName];
1600
+ if(_.isNil(imageObject.src) && imageObject.type === "Signature") {
1601
+ currentJsonElement.name = "p";
1602
+ } else if (!_.isNil(imageObject) && !_.isNil(imageObject.src)
1603
+ && !_.isNil(imageObject.mimeType) && (!_.isNil(imageObject.imageId) || imageObject.type === "Signature")) {
1604
+ let dataUrlStart = "data:" + imageObject.mimeType +";base64,";
1605
+ let dataUrl = imageObject.src;
1606
+
1607
+ if (!_.startsWith(dataUrl, dataUrlStart)) {
1608
+ dataUrl = dataUrlStart + dataUrl;
1609
+ }
1610
+
1611
+ // => use imageName as id
1612
+ currentJsonElement.attributes.id = imageName;
1613
+ currentJsonElement.attributes.mimeType = imageObject.mimeType;
1614
+
1615
+ currentJsonElement.attributes.src = dataUrl;
1616
+ if (imageObject.type === "Signature") {
1617
+ // https://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript
1618
+ const m = encodeURIComponent(dataUrl).match(/%[89ABab]/g);
1619
+ const sizeInBytes = dataUrl.length + (m ? m.length : 0);
1620
+ if (sizeInBytes > SIGNATURE_SIZE_LIMIT) {
1621
+ this.__log.error("Usage of signature larger than 200kb:", imageName);
1622
+ this.__assignFallbackImage(currentJsonElement);
1623
+ }
1624
+ }
1625
+ } else {
1626
+ this.__log.error("Usage of undefined image:", imageName);
1627
+ this.__assignFallbackImage(currentJsonElement);
1628
+ }
1629
+ } else {
1630
+ this.__log.error("Usage of undeclared image:", imageName);
1631
+ this.__assignFallbackImage(currentJsonElement);
1632
+ }
1633
+ } else {
1634
+ this.__log.error("Invalid binding value:'"+ bindingText + "' found in image.");
1635
+ this.__assignFallbackImage(currentJsonElement);
1636
+ }
1637
+ } else {
1638
+ this.__log.error("Invalid value:'"+ bindingText + "' found in image.");
1639
+ this.__assignFallbackImage(currentJsonElement);
1640
+ }
1641
+ }
1642
+
1643
+ __assignFallbackImage(currentJsonElement) {
1644
+ const errorFallbackImage = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAzOCAzOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzggMzg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNGM0Y2Rjg7fQ0KCS5zdDF7ZmlsbDojQjRDN0Q0O30NCjwvc3R5bGU+DQo8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxOSIgY3k9IjE5IiByPSIxOSIvPg0KPGNpcmNsZSBjbGFzcz0ic3QxIiBjeD0iMjkuMSIgY3k9IjExLjQiIHI9IjIuMyIvPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0yMC43LDIyLjZsLTQtNC44bDAuNS0xLjdsLTIuMy00Yy0wLjEtMC4yLTAuMy0wLjItMC40LDBMNS4yLDI2LjljLTAuMSwwLjIsMCwwLjQsMC4yLDAuNGgxNUwxNywyMi41DQoJCQlMMjAuNywyMi42eiBNMzIuOCwyN2wtNy43LTljLTAuMS0wLjItMC40LTAuMy0wLjYtMC4xbC0yLjgsMmwxLjksNS41TDIwLDI0LjVsMS4yLDIuOGgxMS40QzMyLjksMjcuMywzMi45LDI3LjIsMzIuOCwyN3oiLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==";
1645
+ if (!_.isNil(currentJsonElement) && !_.isUndefined(currentJsonElement.attributes)) {
1646
+ currentJsonElement.attributes.id = "$$$fallbackImage$$$";
1647
+ currentJsonElement.attributes.mimeType = "image/svg+xml";
1648
+ currentJsonElement.attributes.src = errorFallbackImage;
1649
+ }
1650
+ }
1651
+
1652
+ __getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex){
1653
+ let tcid = TableCellIdentifier.create(eachName, listItemIndex, correlatedListItemIndex, columnIndex);
1654
+ return tcid.toString();
1655
+ }
1656
+
1657
+ }
1658
+
1659
+ module.exports = ContractToIntermediateJSON;