@ind-rcg/plugins-printengine 250.1002.0 → 250.1004.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.
@@ -402,7 +402,7 @@ eval("var helper = __webpack_require__(/*! ./options-helper */ \"./node_modules/
402
402
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
403
403
 
404
404
  "use strict";
405
- eval("/*\n * FILE_HEADER\n */\n\nconst parser = __webpack_require__(/*! xml-js */ \"./node_modules/xml-js/lib/index.js\");\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst Localization = __webpack_require__(/*! ./localizationClass */ \"./src/localizationClass.js\");\nconst MacroHandler = __webpack_require__(/*! ./macroHandlerClass */ \"./src/macroHandlerClass.js\");\nconst Format = __webpack_require__(/*! ./formatClass */ \"./src/formatClass.js\");\nconst LogClass = __webpack_require__(/*! ./logClass */ \"./src/logClass.js\");\nconst TableCellIdentifier = __webpack_require__(/*! ./tableCellIdentifier */ \"./src/tableCellIdentifier.js\");\n\nconst EACH_NODE = \"each\";\nconst TABLE_NODE = \"table\";\nconst TBODY_NODE = \"tbody\";\nconst THEAD_NODE = \"thead\";\nconst HEADER_NODE = \"header\";\nconst FOOTER_NODE = \"footer\";\nconst PRINT_LAYOUT_NODE = \"PrintLayout\";\nconst REPORT_LAYOUT_NODE = \"ReportLayout\";\nconst DECLARATIONS_NODE = \"Declarations\";\nconst DOCUMENT_PROPERTIES_NODE = \"DocumentProperties\";\nconst PROPERTY_NODE = \"Property\";\nconst H1 = \"h1\";\nconst H2 = \"h2\";\nconst PARAGRAPH = \"p\";\nconst IMAGE = \"img\";\nconst TR_NODE = \"tr\";\nconst TH_NODE = \"th\";\nconst TD_NODE = \"td\";\nconst SUM_NODE = \"sum\";\n\nconst FILTERS_NODE = \"filters\";\nconst FILTER_NODE = \"filter\";\nconst ORDERCRITERIA_NODE = \"orderCriteria\";\nconst ORDERCRITERION_NODE = \"orderCriterion\";\nconst CORRELATION_NODE = \"correlation\";\n\nconst COMPAREMODE_DEFAULT = \"STRING\";\nconst COMPAREMODE_NUMBER = \"NUMBER\";\n\nconst DIV_NODE = \"div\";\nconst DIV_VISIBILITY_NODE = \"visibility\";\nconst DIV_VISIBILITY_BINDING_NODE = \"visibilityBinding\";\nconst DIV_VISIBILITY_CONDITION_NODE = \"visibilityCondition\";\nconst SIGNATURE_SIZE_LIMIT = 200 * 1024;\n\nconst ELEMENT_TYPE = {\n \"TEXT\": \"text\",\n \"ELEMENT\": \"element\",\n \"COMMENT\": \"comment\"\n};\n\nclass ContractToIntermediateJSON {\n constructor(log) {\n if(!(log instanceof LogClass)) {\n throw new Error(\"Invalid constructor parameters\");\n }\n this.__log = log;\n // addParent: true,\n this.__xmlParserLibraryOptions = {\n spaces: 2,\n compact: false\n };\n\n this.__printLayoutName = null;\n this.__declarationsNode = null;\n this.__localization = null;\n this.__toggles = null;\n this.__macroHandler = new MacroHandler(log);\n this.__integerStringRegExp = /^\\d+$/g;\n\n this.__decimalSeparator = \".\";\n this.__thousandSeparator = \",\";\n this.__format = null;\n this.__clearAllCaches();\n }\n\n __clearAllCaches(){\n this.__tableIndex = {};\n this.__sumTdThList = [];\n this.__eachCache = {};\n this.__divCache = [];\n }\n\n __checkDataForDeclaredItems(params, declarationNode){\n let errorMessage = \"\";\n let bos = params.getBOs();\n let images = params.getImages();\n\n let dataMissing = function dataMissing(itemType, itemName) {\n let dataMissingMsg = `Missing declared ${itemType} \"${itemName}\" in print data.`;\n if(errorMessage.length === 0) {\n errorMessage = errorMessage + dataMissingMsg;\n } else {\n errorMessage = errorMessage + \"\\n\" + dataMissingMsg;\n }\n };\n\n if(!_.isNil(declarationNode) && !_.isNil(declarationNode.elements)) {\n _.forEach(declarationNode.elements, (dataDeclaration) => {\n if(!_.isNil(dataDeclaration.attributes)){\n let itemName = dataDeclaration.attributes.name;\n if(!_.isNil(dataDeclaration.attributes.type)) {\n if (dataDeclaration.attributes.type === \"Image\") {\n if(!_.isNil(itemName) && _.isNil(images[itemName])){\n dataMissing(\"Image\", itemName);\n }\n } else if (dataDeclaration.attributes.type === \"Signature\") {\n if(!_.isNil(itemName) && _.isNil(images[itemName])){\n dataMissing(\"Signature\", itemName);\n }\n } else {\n if(!_.isNil(itemName) && _.isNil(bos[itemName])){\n dataMissing(\"BO\", itemName);\n }\n }\n }\n }\n });\n\n if(errorMessage.length > 0){\n throw new Error(errorMessage);\n }\n }\n }\n __checkInput(contract, params) {\n if(_.isNil(params) || !_.isObject(params) || _.isArray(params)) {\n throw new Error(\"Invalid value for parameter 'params'.\");\n }\n if(!_.isString(contract)) {\n throw new Error(\"Invalid value for parameter 'contract'.\");\n }\n }\n\n toJson(contract, params) {\n this.__checkInput(contract, params);\n this.__clearAllCaches();\n this.__toggles = params.getToggles();\n this.__format = new Format();\n this.__localization = new Localization(params.getLocalization());\n\n this.__decimalSeparator = this.__localization.separatorDecimal;\n this.__thousandSeparator = this.__localization.separatorThousand;\n\n let contractJson = this.__parse(contract);\n\n this.__declarationsNode = this.__getDeclarationsNode(contractJson);\n this.__checkDataForDeclaredItems(params, this.__declarationsNode);\n\n this.__walkParseTree(contractJson, {}, \"\", params);\n\n let printLayoutNode = this.__findPrintLayoutNode(contractJson);\n let reportLayout = _.find(printLayoutNode.elements, {name:REPORT_LAYOUT_NODE});\n\n if(_.isNil(reportLayout)) {\n throw new Error(\"Missing ReportLayout node in PrintLayout.\");\n } \n \n this.__processDivs(params);\n this.__postProcessReportLayout(reportLayout);\n this.__processSumRecords(); \n \n this.__addWatermark(reportLayout, params);\n this.__configureAutomaticPageBreak(reportLayout, params);\n\n let documentPropertiesRaw = _.find(printLayoutNode.elements, {name:DOCUMENT_PROPERTIES_NODE});\n let documentProperties = this.__reformatDocumentProperties(documentPropertiesRaw); \n\n return {\"reportLayout\": reportLayout, \"documentProperties\": documentProperties};\n }\n\n __processDivs(params) {\n if (!_.isNil(this.__divCache) && _.isArray(this.__divCache)) {\n\n for (let index = 0; index < this.__divCache.length; index++) {\n const divData = this.__divCache[index];\n\n let result = this.__checkForDivToBeVisible(divData.divNode, params);\n if (result) {\n // rescue div children and put them in parent elements\n let nodes = _.filter(divData.divNode.elements, node => node.type === ELEMENT_TYPE.ELEMENT && node.name !== DIV_VISIBILITY_NODE);\n if (!_.isNil(nodes) && _.isArray(nodes)) {\n // remember div id for future styling / layouting ...\n _.forEach(nodes, node => {\n this.__ensureAttributes(node);\n node.attributes.div = divData.id;\n });\n let divIndex = _.findIndex(divData.parentNode.elements, node => _.isEqual(node, divData.divNode));\n divData.parentNode.elements.splice(divIndex, 0, ..._.cloneDeep(nodes));\n }\n }\n // remove div from parent\n _.remove(divData.parentNode.elements, node => _.isEqual(node, divData.divNode)); \n }\n\n }\n }\n\n __postProcessReportLayout(reportLayoutNode) {\n // get header node\n let headerNode = _.find(reportLayoutNode.elements, {name: HEADER_NODE});\n if (!_.isNil(headerNode)) {\n // => check header node and remove empty tables\n this.__removeEmptyTables(headerNode); \n }\n // get footer node\n let footerNode = _.find(reportLayoutNode.elements, {name: FOOTER_NODE});\n if (!_.isNil(footerNode)) {\n // => check footer node and remove empty tables\n this.__removeEmptyTables(footerNode);\n } \n // => check reportlayout node and remove empty tables & divs\n this.__removeEmptyTables(reportLayoutNode);\n }\n\n __removeEmptyTables(containerNode) {\n if (_.isNil(containerNode) || (!_.isNil(containerNode) && !_.isUndefined(containerNode.elements) && containerNode.elements.length === 0)) {\n return;\n }\n let tablesToRemove = [];\n _.forEach(containerNode.elements, node => {\n if (node.type === ELEMENT_TYPE.ELEMENT && node.name === TABLE_NODE && node.toBeRemoved) {\n tablesToRemove.push(node);\n }\n });\n if (tablesToRemove.length > 0) {\n _.forEach(tablesToRemove, tableNode => {\n _.remove(containerNode.elements, node => _.isEqual(node, tableNode));\n });\n }\n }\n\n __addWatermark(reportLayout, params){\n if(_.isNil(reportLayout)){\n return;\n }\n this.__ensureAttributes(reportLayout);\n let watermarkInMetadata = params.getMetadata()[params.getMetadataKeys().WATERMARK];\n if(_.isString(watermarkInMetadata) && _.trim(watermarkInMetadata) !== \"\") {\n reportLayout.attributes.watermark = watermarkInMetadata;\n } else if (!_.isNil(watermarkInMetadata) && !_.isString(watermarkInMetadata)) {\n this.__log.error(\"Invalid value for watermark.\");\n }\n\n }\n \n __configureAutomaticPageBreak(reportLayout, params){\n if(_.isNil(reportLayout)){\n return;\n }\n this.__ensureAttributes(reportLayout); \n let automaticPageBreak = false; // default \n let automaticPageBreakInMetadata = params.getMetadata()[params.getMetadataKeys().AUTOMATIC_PAGEBREAK];\n if(!_.isNil(automaticPageBreakInMetadata) && _.isBoolean(automaticPageBreakInMetadata)) {\n automaticPageBreak = automaticPageBreakInMetadata; \n } \n reportLayout.attributes.automaticPageBreak = automaticPageBreak;\n }\n\n __reformatDocumentProperties(documentPropertiesRaw){\n let result = {};\n if(!_.isNil(documentPropertiesRaw)){\n // this means the contract has document properties set\n _.forEach(documentPropertiesRaw.elements, ele => {\n\n if(ele.type === ELEMENT_TYPE.ELEMENT){\n let sanitizedKey = ele.attributes.key.replace(/\\s/g, \"\");\n result[sanitizedKey]= ele.attributes.value;\n }\n });\n }\n return result;\n }\n\n __findPrintLayoutNode(contractJson) {\n let printLayoutNode = _.find(contractJson.elements, {name:PRINT_LAYOUT_NODE});\n if(_.isNil(printLayoutNode)) {\n throw new Error(\"Missing PrintLayout node.\");\n }\n return printLayoutNode;\n }\n\n __getDeclarationsNode(contractJson) {\n let printLayoutNode = this.__findPrintLayoutNode(contractJson);\n let declarationsNode = _.find(printLayoutNode.elements, {name: DECLARATIONS_NODE});\n if(_.isNil(declarationsNode)) {\n declarationsNode = {};\n }\n return declarationsNode;\n }\n __newXMLconvertibleJsonNode(type, children, otherAttributes) {\n let newNode = {\n \"type\": type\n };\n if(!_.isNil(children)) {\n newNode.elements = _.castArray(children);\n }\n _.assign(newNode, otherAttributes);\n return newNode;\n }\n\n __walkParseTree(contractJson, parentElement, parentName, params) {\n // processing ...\n this.__processCurrentElement(contractJson, parentElement, parentName, params);\n // recursion ...\n if(!_.isNil(contractJson.elements)) {\n _.remove(contractJson.elements, (ele)=> {\n if (ele.type === ELEMENT_TYPE.COMMENT) {\n return true;\n } else {\n return false;\n }\n });\n _.forEach(contractJson.elements, (ele) => {\n this.__walkParseTree(ele, contractJson, contractJson.name, params);\n });\n }\n // post-processing ...\n this.__postProcessCurrentElement(contractJson);\n }\n\n __postProcessCurrentElement(currentJsonElement) {\n if (!_.isNil(currentJsonElement)) {\n if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT) {\n if (currentJsonElement.name === TR_NODE) {\n\n // perform colSpan handling ...\n this.__applyColSpanHandling(currentJsonElement);\n\n } else if (currentJsonElement.name === TABLE_NODE) {\n\n // perform rowSpan handling ...\n this.__applyRowSpanHandling(currentJsonElement);\n // normalize table ...\n this.__normalizeTable(currentJsonElement);\n // check for empty table ...\n this.__checkForEmptyTable(currentJsonElement);\n\n } \n }\n }\n }\n\n __checkForDivToBeVisible(divNode, params) {\n let result = true;\n if (!_.isNil(divNode) && _.isArray(divNode.elements)) { \n\n // set result to 'true' for easier evaluation ... \n let divVisibilityNode = _.find(divNode.elements, {name: DIV_VISIBILITY_NODE});\n if (!_.isNil(divVisibilityNode) && _.isArray(divVisibilityNode.elements)) {\n\n // check for visibilityBinding elements ...\n let divVisibilityBindingNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_BINDING_NODE});\n if (!_.isNil(divVisibilityBindingNodes) && _.isArray(divVisibilityBindingNodes) && divVisibilityBindingNodes.length > 0) {\n for (let i = 0; i < divVisibilityBindingNodes.length; i++) {\n // process visibilityBinding ...\n let divVisibilityBindingNode = divVisibilityBindingNodes[i];\n result = this.__evaluateVisibilityBinding(divVisibilityBindingNode, params);\n // exit if visibility criteria cannot be met ...\n if (!result) {\n break;\n }\n }\n }\n \n if (result) {\n // check for visibilityCondition elements ...\n let divVisibilityConditionNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_CONDITION_NODE});\n if (!_.isNil(divVisibilityConditionNodes) && _.isArray(divVisibilityConditionNodes) && divVisibilityConditionNodes.length > 0) {\n for (let i = 0; i < divVisibilityConditionNodes.length; i++) {\n // process visibilityCondition ...\n let divVisibilityConditionNode = divVisibilityConditionNodes[i];\n result = this.__evaluateVisibilityCondition(divVisibilityConditionNode);\n // exit if visibility criteria cannot be met ...\n if (!result) {\n break;\n }\n }\n }\n }\n\n // => remove visibility node ...\n _.remove(divNode.elements, node => (node.type === ELEMENT_TYPE.ELEMENT && node.name === DIV_VISIBILITY_NODE));\n } \n \n }\n return result;\n }\n\n __evaluateVisibilityBinding(visiblityBindingNode, params) {\n let result = true;\n\n // <visiblityBinding binding=\"{{Declarations::header.itemCount}}\" value=\"0\" operator=\"GT\" compareMode=\"NUMBER\" />\n if (!_.isNil(visiblityBindingNode)) {\n\n let expression = this.__getVisibilityExpression(visiblityBindingNode, params);\n if (!_.isNil(expression) && _.isFunction(expression)) {\n result = expression();\n } \n }\n\n return result;\n }\n\n __getVisibilityExpression(visiblityBindingOrCondition, params) {\n let expression = null;\n\n // extract attributes\n let binding = null;\n let eachName = null;\n let compareMode = COMPAREMODE_DEFAULT;\n let operator = visiblityBindingOrCondition.attributes.operator;\n let value = visiblityBindingOrCondition.attributes.value;\n let comparisonValue = null; \n\n if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_BINDING_NODE) {\n // => handle visibilityBinding\n binding = visiblityBindingOrCondition.attributes.binding;\n // get compareMode if available\n if (_.has(visiblityBindingOrCondition.attributes, \"compareMode\")) {\n compareMode = visiblityBindingOrCondition.attributes.compareMode;\n }\n // resolve binding\n comparisonValue = this.__resolveScalarBinding(visiblityBindingOrCondition, binding, params);\n if (_.isNil(comparisonValue)) {\n // unknown binding => log error\n this.__log.error(\"Unsupported binding '\", binding, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n\n } else if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_CONDITION_NODE) {\n // => handle visibilityCondition\n eachName = visiblityBindingOrCondition.attributes.eachName;\n // set compareMode to \"NUMBER\"\n compareMode = COMPAREMODE_NUMBER;\n // resolve eachName\n if (_.has(this.__eachCache, eachName) && _.isArray(this.__eachCache[eachName])) {\n comparisonValue = this.__eachCache[eachName].length; \n } \n if (_.isNil(comparisonValue)) {\n // unknown eachName => log error\n this.__log.error(\"Unsupported eachName '\", eachName, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n }\n\n // normalize compareMode & operator\n compareMode = _.toUpper(compareMode);\n operator = _.toUpper(operator);\n\n if (!_.isNil(comparisonValue) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {\n\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // convert comparison value once\n let x = converter(comparisonValue);\n // convert value once\n let y = converter(value);\n\n let comparator = this.__resolveComparator(operator);\n if (!_.isNil(comparator) && _.isFunction(comparator)) {\n\n // build filter expression performing comparison\n expression = ( () => {\n return comparator(x, y);\n });\n\n } else {\n // unknown operator => log error\n this.__log.error(\"Unsupported operator '\", operator, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n } else {\n // missing/invalid mandatory attribute => log error\n this.__log.error(\"Missing/invalid mandatory attribute at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n\n return expression;\n }\n\n __evaluateVisibilityCondition(visiblityConditionNode) {\n let result = true;\n\n // <visiblityCondition eachName=\"each1\" value=\"0\" operator=\"GT\" />\n if (!_.isNil(visiblityConditionNode)) {\n let expression = this.__getVisibilityExpression(visiblityConditionNode, null);\n if (!_.isNil(expression) && _.isFunction(expression)) {\n result = expression();\n } \n }\n\n return result;\n }\n\n __checkForEmptyTable(tableNode) {\n if (!_.isNil(tableNode)) {\n // check thead\n let thead = _.find(tableNode.elements, {name:THEAD_NODE});\n if (_.isNil(thead) || _.isUndefined(thead.elements) || thead.elements.length === 0) {\n // check tbody\n let tbody = _.find(tableNode.elements, {name:TBODY_NODE});\n if (_.isNil(tbody) || (!_.isNil(tbody) && !_.isUndefined(tbody.elements) && tbody.elements.length === 0)) {\n // table is empty => to be removed\n tableNode.toBeRemoved = true;\n }\n } \n }\n }\n\n __applyColSpanHandling(tableRowNode) {\n let newElements = [];\n _.forEachRight(tableRowNode.elements, tdth => {\n if (!_.isNil(tdth.attributes)) {\n let colSpan = tdth.attributes.colSpan;\n if (!_.isNil(colSpan)) {\n colSpan = _.toInteger(colSpan);\n if (!_.isNaN(colSpan) && colSpan > 1) {\n let elementsToAdd = colSpan - 1;\n let elementName = tdth.name;\n\n let newAttributes = {\n \"name\": elementName,\n \"doNotReplace\": true\n };\n for (let i = 0; i < elementsToAdd; i++) {\n let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, newAttributes);\n newElements.push(newElement);\n }\n }\n }\n }\n newElements.push(tdth);\n });\n // replace elements if applicable\n let elementCount = 0;\n if (!_.isNil(tableRowNode.elements)) {\n elementCount = tableRowNode.elements.length;\n }\n if (newElements.length !== elementCount) {\n tableRowNode.elements = _.reverse(newElements);\n }\n }\n\n __applyRowSpanHandling(tableNode) {\n if (!_.isNil(tableNode)) {\n // fix thead\n this.__applyRowSpanHandlingToContainer(tableNode, THEAD_NODE);\n\n // fix tbody\n this.__applyRowSpanHandlingToContainer(tableNode, TBODY_NODE);\n }\n }\n\n __applyRowSpanHandlingToContainer(tableNode, trContainerNodeName) {\n\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n // check tbody / thead\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode) && !_.isNil(trContainerNode.elements)) {\n for (let i = 0; i < trContainerNode.elements.length; i++) {\n // check tr\n let tr = trContainerNode.elements[i];\n if (!_.isNil(tr) && !_.isNil(tr.elements)) {\n for (let j = 0; j < tr.elements.length; j++) {\n // check td / th\n let tdth = tr.elements[j];\n if (!_.isNil(tdth)) {\n if (!_.isNil(tdth.attributes)) {\n let rowSpan = tdth.attributes.rowSpan;\n if (!_.isNil(rowSpan)) {\n rowSpan = _.toInteger(rowSpan);\n if (!_.isNaN(rowSpan) && rowSpan > 1) {\n let rowsToUpdate = rowSpan - 1;\n let elementsToAdd = 1; // rowSpan element\n let elementName = tdth.name;\n let colSpan = tdth.attributes.colSpan;\n if (!_.isNil(colSpan)) {\n colSpan = _.toInteger(colSpan);\n if (!_.isNaN(colSpan) && colSpan > 1) {\n elementsToAdd = elementsToAdd + (colSpan - 1); // colSpan elements\n }\n }\n // update \"spanned\" rows\n for (let k = 0; k < rowsToUpdate; k++) {\n let rowIndex = i + k + 1;\n if (rowIndex < trContainerNode.elements.length) {\n let tr2 = trContainerNode.elements[rowIndex];\n\n if (_.isNil(tr2.elements)) {\n _.assign(tr2, {\"elements\": []});\n }\n\n let tr2Index = j;\n if (tr2Index > tr2.elements.length) {\n tr2Index = 0;\n }\n\n // insert elements ...\n for (let l = 0; l < elementsToAdd; l++) {\n let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, {\"name\": elementName, \"doNotReplace\": true});\n tr2.elements.splice(tr2Index, 0, newElement);\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n __normalizeTable(tableNode) {\n if (!_.isNil(tableNode)) {\n // get minimum thead width\n let minTableHeadWidth = this.__getTableContainerWidth(tableNode, THEAD_NODE);\n\n // get minimum tbody width\n let minTableBodyWidth = this.__getTableContainerWidth(tableNode, TBODY_NODE);\n\n // determine minimum width for table\n let minTableWidth = _.min([minTableHeadWidth, minTableBodyWidth]);\n\n // repair thead\n this.__normalizeTableWidth(tableNode, THEAD_NODE, minTableWidth);\n\n // repair tbody\n this.__normalizeTableWidth(tableNode, TBODY_NODE, minTableWidth);\n }\n }\n\n __getTableContainerWidth(tableNode, trContainerNodeName) {\n let minWidth = Number.MAX_SAFE_INTEGER;\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode)) {\n _.forEach(trContainerNode.elements, tr => {\n if (!_.isNil(tr) && tr.name === TR_NODE && !_.isNil(tr.elements)) {\n let thCount = tr.elements.length;\n if (thCount < minWidth) {\n minWidth = thCount;\n }\n }\n });\n }\n }\n return minWidth;\n }\n\n __normalizeTableWidth(tableNode, trContainerNodeName, tableWidth) {\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode)) {\n let normalized = false;\n _.forEach(trContainerNode.elements, tr => {\n if (!_.isNil(tr) && tr.name === TR_NODE) {\n let trWidth = 0;\n if (!_.isNil(tr.elements)) {\n trWidth = tr.elements.length;\n }\n if (trWidth > tableWidth) {\n // remove elements ...\n tr.elements.splice(tableWidth, trWidth - tableWidth);\n normalized = true;\n }\n }\n });\n if (normalized) {\n let tableName = tableNode.attributes.name;\n let errorMessage = \"<\" + trContainerNodeName + \"> element of <table> '\" + tableName + \"' has been normalized due to improper formatting\";\n this.__log.error(errorMessage);\n }\n }\n }\n }\n\n __processSumRecords() {\n let idxSum;\n for (idxSum=0; idxSum < this.__sumTdThList.length; idxSum++) {\n let tdthNode = this.__sumTdThList[idxSum];\n let sumNode = _.find(tdthNode.elements, {name:SUM_NODE});\n let tableName = sumNode.attributes.table;\n let tablePos = parseFloat(sumNode.attributes.col, 10);\n let sumFormat = sumNode.attributes.numberFormat;\n let tableNode = this.__tableIndex[tableName];\n let sum = 0;\n\n if (!_.isNil(tableNode)) {\n let tBodyNode = _.find(tableNode.elements, {name:TBODY_NODE});\n if (!_.isNil(tBodyNode) && !_.isNil(tBodyNode.elements)) {\n _.forEach(tBodyNode.elements, (trNode) => {\n let tdNode = trNode.elements[tablePos];\n if(!_.isNil(tdNode)) {\n let bSum = false;\n let text = \"\";\n let nonRoundedValue = null;\n if (!_.isNil(tdNode.attributes)) {\n nonRoundedValue = tdNode.attributes.nonRoundedValue;\n }\n _.forEach(tdNode.elements, childElement => {\n if ((childElement.type === \"text\") && !_.isNil(childElement.isSum) && childElement.isSum) {\n bSum = true;\n } else if (childElement.type === \"text\") {\n text = childElement.text;\n } else if (childElement.type === \"element\" && childElement.name === SUM_NODE) {\n bSum = true;\n }\n });\n if (!bSum) {\n if (!_.isNil(nonRoundedValue)) {\n // nonRoundedValue only exists if a format was applied to the cell\n sum += nonRoundedValue;\n } else {\n let value = parseFloat(text, 10);\n if (!isNaN(value)) {\n sum += value;\n }\n }\n\n }\n }\n });\n } else {\n sum = 0;\n }\n } else {\n sum = 0;\n }\n\n let txtSum;\n\n if (!_.isNil(sumFormat)) {\n txtSum = this.__format.formatDecimalV2(sum, sumFormat, this.__decimalSeparator, this.__thousandSeparator);\n } else {\n txtSum = \"\" + sum;\n }\n\n let bText = false;\n\n _.forEach(tdthNode.elements, childElement => {\n if(childElement.type === \"text\") {\n childElement.text = txtSum + childElement.text;\n childElement.isSum = true;\n bText = true;\n }\n });\n\n if (!bText) {\n if (txtSum.length > 0) {\n tdthNode.elements.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": txtSum, \"doNotReplace\": true, \"isSum\": true}));\n }\n }\n\n this.__deleteInvalidNodes(tdthNode, [SUM_NODE], null);\n }\n }\n\n __processH1Node(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processH2Node(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processParagraphNode(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processPropertyNode(currentJsonElement, params){\n let resolvedMacro = this.__resolveScalarBinding(currentJsonElement, currentJsonElement.attributes.value, params);\n if(!_.isNil(resolvedMacro)) {\n if(_.isObject(resolvedMacro)) {\n resolvedMacro = JSON.stringify(resolvedMacro);\n }\n if(!_.isString(resolvedMacro)){\n resolvedMacro = \"\"+resolvedMacro;\n }\n currentJsonElement.attributes.value = resolvedMacro;\n }\n }\n\n __processPrintLayoutNode(currentJsonElement/*, parentName, params*/){\n this.__printLayoutName = currentJsonElement.attributes.name;\n }\n\n __processCurrentElement(currentJsonElement, parentElement, parentName, params) {\n if (currentJsonElement.doNotReplace) {\n return;\n }\n if(currentJsonElement.type === ELEMENT_TYPE.TEXT) {\n this.__replaceMacrosInTextNodes(parentElement, currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TBODY_NODE) {\n this.__replaceEachTagsInTables(currentJsonElement, parentElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === HEADER_NODE){\n this.__processHeaderNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === FOOTER_NODE){\n this.__processFooterNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === REPORT_LAYOUT_NODE){\n this.__processReportLayoutNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TABLE_NODE){\n this.__processTableNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === THEAD_NODE){\n this.__processTheadNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H1) {\n this.__processH1Node(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H2) {\n this.__processH2Node(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PARAGRAPH) {\n this.__processParagraphNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === IMAGE) {\n this.__processImageNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PROPERTY_NODE) {\n this.__processPropertyNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TR_NODE){\n this.__processTrNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TD_NODE){\n this.__processTdThNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TH_NODE){\n this.__processTdThNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PRINT_LAYOUT_NODE){\n this.__processPrintLayoutNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === DIV_NODE){\n this.__processDivNode(currentJsonElement, parentElement);\n }\n }\n\n __processDivNode(divNode, parentNode) {\n // cache divNode and parentNode for later ...\n this.__divCache.push({\n \"id\" : \"div\" + (this.__divCache.length + 1),\n \"divNode\" : divNode, // div to evaluate\n \"parentNode\": parentNode // parent to resolve\n }); \n }\n\n __parse(contract){\n let contractJson = parser.xml2js(contract,this.__xmlParserLibraryOptions);\n return contractJson;\n }\n\n __isLo(object){\n return _.isArray(object);\n }\n\n __makeTextOnlyNode(currentJsonElement) {\n _.remove(currentJsonElement.elements, (ele)=> { return ele.type !== ELEMENT_TYPE.TEXT;});\n if(currentJsonElement.elements.length === 0) {\n currentJsonElement.elements.push({type:ELEMENT_TYPE.TEXT, text: \"\"});\n }\n }\n\n __deleteInvalidNodes(currentJsonElement, deleteNames, preserveNames) {\n //Remove invalid nodes by positive or negative lists.\n let bDelete = !_.isNil(deleteNames) && _.isArray(deleteNames) && (deleteNames.length > 0);\n let bPreserve = !_.isNil(preserveNames) && _.isArray(preserveNames) && (preserveNames.length > 0);\n\n //It makes no sense to fill both lists or no list but we cannot provide a true/true or a false/false\n //scenario via Unit Tests. So we keep both paths split to fulfill the branch coverage.\n if (bDelete) {\n _.remove(currentJsonElement.elements, (ele)=> {\n let names = 0;\n for (names=0; names<deleteNames.length; names++) {\n if (ele.name === deleteNames[names]) {\n return true;\n }\n }\n return false;\n });\n }\n if (bPreserve) {\n _.remove(currentJsonElement.elements, (ele)=> {\n let names = 0;\n for (names=0; names<preserveNames.length; names++) {\n if (ele.name === preserveNames[names]) {\n return false;\n }\n }\n return true;\n });\n }\n }\n\n __processTheadNode(currentJsonElement/*, parentName, params*/){\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processTableNode(currentJsonElement/*, parentName, params*/){\n this.__deleteInvalidNodes(currentJsonElement, null, [THEAD_NODE, TBODY_NODE]);\n if (_.isNil(this.__tableIndex[currentJsonElement.attributes.name])) {\n this.__tableIndex[currentJsonElement.attributes.name] = currentJsonElement;\n }\n }\n\n __processTrNode(currentJsonElement, parentName/*, params*/){\n if (parentName === THEAD_NODE) {\n this.__deleteInvalidNodes(currentJsonElement, null, [TH_NODE]);\n } else if (parentName === TBODY_NODE) {\n this.__deleteInvalidNodes(currentJsonElement, null, [TD_NODE]);\n }\n }\n\n __processTdThNode(currentJsonElement, /*, parentName, params*/){\n let sumNode = _.find(currentJsonElement.elements, {name:SUM_NODE});\n\n if (!_.isNil(sumNode)) {\n this.__sumTdThList.push(currentJsonElement);\n }\n }\n\n __processReportLayoutNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processHeaderNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processFooterNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __createTableDataCache(tableRow, params) {\n let tdCacheList = [];\n\n _.forEach(tableRow.elements, subtag => {\n let tdCacheElement = {};\n tdCacheElement.elements = [];\n\n if (subtag.name === \"td\") {\n if (!_.isNil(subtag.elements)) {\n let allTextNodeTexts = [];\n _.forEach(subtag.elements, tdSubtag => {\n if (tdSubtag.type === ELEMENT_TYPE.TEXT) {\n this.__replaceMacrosInTextNodes(subtag, tdSubtag, params);\n allTextNodeTexts.push(_.trim(tdSubtag.text));\n } else if (tdSubtag.type === ELEMENT_TYPE.ELEMENT && (tdSubtag.name === IMAGE)) {\n this.__processImageNode(tdSubtag, params);\n tdSubtag.doNotReplace = true;\n tdCacheElement.elements.push(tdSubtag);\n }\n });\n\n tdCacheElement.text = allTextNodeTexts.join(' ');\n tdCacheElement.macroList = [];\n\n let macroRegex = this.__macroHandler.getMacroRegex();\n let macroMatches = tdCacheElement.text.match(macroRegex);\n\n _.forEach(macroMatches, match => {\n let macroElement = {\"match\": match, \"result\": this.__macroHandler.parseMacro(match)};\n tdCacheElement.macroList.push(macroElement);\n });\n\n\n } else {\n tdCacheElement.text = \"\";\n }\n\n tdCacheElement.attributes = subtag.attributes;\n tdCacheList.push(tdCacheElement);\n }\n });\n\n return tdCacheList;\n }\n\n __createTableTrForOneListItem(tdCacheList, listItem, trAttributes, isChildItem, eachName, listItemIndex, correlatedListItemIndex) {\n let tdNodes = [];\n let newText = null;\n let newAttributes = null;\n\n // id handling...\n let cellId = null;\n let columnIndex = -1;\n\n _.forEach(tdCacheList, cacheElement => {\n newText = cacheElement.text;\n newAttributes = _.clone(cacheElement.attributes);\n\n if (cacheElement.text.length > 0) {\n _.forEach(cacheElement.macroList, macroElement => {\n let macroResult = macroElement.result;\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.path)) {\n let listItemValue = listItem[macroResult.path];\n\n if(typeof listItemValue !== \"undefined\") {\n if (!_.isNil(macroResult.numberFormat)) {\n if(_.isNil(newAttributes)) {newAttributes = {};}\n listItemValue = this.__resolveNumberBinding(listItemValue, macroResult,newAttributes);\n if(isChildItem){\n // setting the nonRoundedValue to 0.0 makes sure that correlation items are ignored in the sum tag\n newAttributes.nonRoundedValue = 0.0;\n }\n } if(!_.isNil(macroResult.dateTimeFormat)) {\n listItemValue = this.__resolveDateBinding(listItemValue, macroResult);\n } else {\n listItemValue = this.__replaceTogglesIfApplicable(listItemValue, macroResult, this.__localization);\n }\n\n if(_.isString(listItemValue)){\n listItemValue = listItemValue.replace(/\\$/g, \"$$$$\");\n }\n\n newText = newText.replace(macroElement.match, listItemValue);\n }\n }\n });\n }\n\n let newTextNode = [];\n if(newText.length > 0){\n newTextNode.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": newText, \"doNotReplace\": true}));\n }\n\n // only apply id to cells with text OR child elements ...\n if (newTextNode.length > 0 || cacheElement.elements.length > 0) {\n columnIndex += 1;\n cellId = this.__getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex);\n if (!_.isNil(cellId)) {\n if (_.isNil(newAttributes)) { newAttributes = {}; }\n newAttributes.id = cellId;\n }\n }\n\n let newTdNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, newTextNode,\n {\"name\": \"td\", \"attributes\": newAttributes, \"doNotReplace\": true});\n\n _.forEach(cacheElement.elements, element => {\n newTdNode.elements.push(element);\n });\n\n tdNodes.push(newTdNode);\n });\n\n let newTrNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, tdNodes,\n {\"name\": \"tr\", \"attributes\": trAttributes, \"doNotReplace\": true});\n return newTrNode;\n }\n\n __createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName) {\n\n if (!_.isNil(tableRow) && !_.isNil(tdCacheList) && !_.isNil(trNodes) && !_.isNil(listObject) && !_.isNil(eachName)) {\n\n let hasCorrelationInfo = !_.isNil(correlationInfo);\n let listItemIndex = -1;\n let correlatedListItemIndex = -1;\n\n _.forEach(listObject, listItem => {\n\n if (hasCorrelationInfo) {\n listItemIndex += 1;\n correlatedListItemIndex = -1;\n }\n\n let newTrNode = this.__createTableTrForOneListItem(tdCacheList, listItem, tableRow.attributes, false, eachName, listItemIndex, correlatedListItemIndex);\n trNodes.push(newTrNode);\n\n if (!_.isNil(correlationInfo) && !_.isNil(listItem[correlationInfo.key])) {\n // TODO: this implementation is slow and can be done faster.\n let correlatedItemsForCurrentLI = _.filter(correlationInfo.listObjectForCorrelation, (elem) => {\n return elem[correlationInfo.correlationKey] === listItem[correlationInfo.key];\n });\n\n // (re)set correlatedListItemIndex...\n correlatedListItemIndex = -1;\n\n _.forEach(correlatedItemsForCurrentLI, corrLi => {\n\n // set correlatedListItemIndex...\n correlatedListItemIndex += 1;\n\n let newCorrelationTrNode = this.__createTableTrForOneListItem(correlationInfo.tdCacheForCorrelation, corrLi, {}, true, eachName, listItemIndex, correlatedListItemIndex);\n trNodes.push(newCorrelationTrNode);\n });\n }\n });\n }\n\n return trNodes;\n }\n\n __buildCorrelationInfo(correlationNode, params){\n let correlationInfo = null;\n if(!_.isNil(correlationNode)){\n correlationInfo = {};\n correlationInfo.correlationTableRow = _.find(correlationNode.elements, {name:TR_NODE}); // eachNode.elements[0];\n correlationInfo.tdCacheForCorrelation = this.__createTableDataCache(correlationInfo.correlationTableRow, params);\n correlationInfo.listObjectForCorrelation = this.__resolveScalarBinding(correlationNode,\n correlationNode.attributes.value, params);\n // => get and apply filters\n correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyFilters(correlationNode,\n correlationInfo.listObjectForCorrelation);\n // => get and apply orderCriteria\n correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyOrderCriteria(correlationNode,\n correlationInfo.listObjectForCorrelation);\n correlationInfo.key = correlationNode.attributes.key;\n correlationInfo.correlationKey = correlationNode.attributes.correlationKey;\n }\n return correlationInfo;\n }\n\n __resolveEachTag(eachNode, tableNode, params) {\n let trNodes = [];\n\n if(!_.isNil(eachNode) && (eachNode.name === EACH_NODE)){\n let eachName = null;\n let listObject = null;\n\n if (!_.isNil(eachNode.attributes)) {\n eachName = eachNode.attributes.name;\n listObject = this.__resolveScalarBinding(eachNode, eachNode.attributes.value, params);\n if(!this.__isLo(listObject)){\n listObject = null;\n }\n }\n\n if (_.isNil(listObject)) {\n if(!_.isNil(eachNode.attributes)) {\n trNodes.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": eachNode.attributes.value,\n \"doNotReplace\": true}));\n }\n }\n\n this.__deleteInvalidNodes(eachNode, null, [TR_NODE, FILTERS_NODE, ORDERCRITERIA_NODE, CORRELATION_NODE]);\n\n if (!_.isNil(eachNode.elements) && (eachNode.elements.length > 0)) {\n\n // => get and apply filters\n listObject = this.__evaluateAndApplyFilters(eachNode, listObject);\n // => get and apply orderCriteria\n listObject = this.__evaluateAndApplyOrderCriteria(eachNode, listObject);\n\n // cache each data ...\n this.__eachCache[eachName] = listObject;\n\n // the correlation node is needed for printing the correlated items\n const correlationNode = _.find(eachNode.elements, {name:CORRELATION_NODE});\n let correlationInfo = this.__buildCorrelationInfo(correlationNode, params);\n if (!_.isNil(correlationInfo)) {\n // each node with correlation => enable dynamicPageBreak for table\n tableNode.attributes.dynamicPageBreak = \"true\"; // as string to keep pdfconverter working ...\n }\n\n // => process table row (with filtered / sorted list object)\n let tableRow = _.find(eachNode.elements, {name:TR_NODE});\n if (!_.isNil(tableRow)) {\n\n this.__deleteInvalidNodes(tableRow, null, [TD_NODE]);\n\n if (!_.isNil(tableRow.elements) && (tableRow.elements.length > 0)) {\n let tdCacheList = this.__createTableDataCache(tableRow, params);\n trNodes = this.__createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName);\n }\n } else {\n // no table row => log error\n this.__log.error(\"<each> element must contain a <tr> (table row) element\");\n }\n }\n }\n\n return trNodes;\n }\n\n __evaluateAndApplyFilters(iterableNode, listObject) {\n // <filters>\n // <filter fieldName=\"quantity\" value=\"0\" operator=\"GT\" compareMode=\"NUMBER\" />\n // <filter fieldName=\"movementDirection\" value=\"In\" operator=\"NE\" />\n // </filters>\n\n let filtersNode = _.find(iterableNode.elements, {name:FILTERS_NODE});\n if (!_.isNil(filtersNode)) {\n\n\n this.__deleteInvalidNodes(filtersNode, null, [FILTER_NODE]);\n\n if (!_.isNil(filtersNode.elements) && (filtersNode.elements.length > 0)) {\n _.forEach(filtersNode.elements, element => {\n\n let filterExpression = this.__getAdvancedFilterExpression(element);\n if (!_.isNil(filterExpression) && _.isFunction(filterExpression)) {\n listObject = _.filter(listObject, filterExpression);\n }\n\n });\n } else {\n // no filter elements => log error\n this.__log.error(\"<each>.<filters> element must contain at least one <filter> element\");\n }\n }\n return listObject;\n }\n\n __getAdvancedFilterExpression(filterNode) {\n let filterExpression = null;\n\n // extract attributes\n let fieldName = filterNode.attributes.fieldName;\n let operator = filterNode.attributes.operator;\n let value = filterNode.attributes.value;\n\n let compareMode = filterNode.attributes.compareMode;\n if (_.isNil(compareMode)) {\n compareMode = COMPAREMODE_DEFAULT;\n }\n // normalize compareMode\n compareMode = _.toUpper(compareMode);\n\n if (!_.isNil(fieldName) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {\n\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // convert value just once\n let y = converter(value);\n\n // normalize operator\n operator = _.toUpper(operator);\n\n let comparator = this.__resolveComparator(operator);\n if (!_.isNil(comparator) && _.isFunction(comparator)) {\n\n // build filter expression\n filterExpression = (object => {\n // convert listobject value\n let x = converter(object[fieldName]);\n // perform comparison\n return comparator(x, y);\n });\n\n } else {\n // unknown operator => log error\n this.__log.error(\"Unsupported operator '\", operator, \"' at <each>.<filters>.<filter> element\");\n }\n\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode, \"' at <each>.<filters>.<filter> element\");\n }\n\n } else {\n // missing mandatory attribute => log error\n this.__log.error(\"Missing mandatory attribute at <each>.<filters>.<filter> element\");\n }\n\n return filterExpression;\n }\n\n __resolveComparator(comparator) {\n const COMPARATORS = {\n \"EQ\" : function (x, y) { return (x === y); },\n \"NE\" : function (x, y) { return (x !== y); },\n \"GT\" : function (x, y) { return (x > y); },\n \"GE\" : function (x, y) { return (x >= y); },\n \"LT\" : function (x, y) { return (x < y); },\n \"LE\" : function (x, y) { return (x <= y); }\n };\n\n return COMPARATORS[comparator];\n }\n\n __resolveConverter(compareMode) {\n const CONVERTERS = {\n //\"NONE\" : function (x) { return x; },\n \"STRING\" : function (x) {\n if (!_.isString(x)) {\n return _.toString(x);\n } else {\n return x;\n }\n },\n \"NUMBER\" : function (x) {\n if (!_.isNumber(x)) {\n return _.toNumber(x);\n } else {\n return x;\n }\n }\n };\n\n return CONVERTERS[compareMode];\n }\n\n __evaluateAndApplyOrderCriteria(iterableNode, listObject) {\n // <orderCriteria>\n // <orderCriterion fieldName=\"eAN\" direction=\"ASC\" compareMode=\"NUMBER\" />\n // </orderCriteria>\n let orderCriteriaNode = _.find(iterableNode.elements, {name:ORDERCRITERIA_NODE});\n if (!_.isNil(orderCriteriaNode)) {\n\n this.__deleteInvalidNodes(orderCriteriaNode, null, [ORDERCRITERION_NODE]);\n\n if (!_.isNil(orderCriteriaNode.elements) && (orderCriteriaNode.elements.length > 0)) {\n\n let selectors = [];\n let directions = [];\n\n _.forEach(orderCriteriaNode.elements, element => {\n\n // extract attributes\n let fieldName = element.attributes.fieldName;\n let direction = element.attributes.direction;\n\n let compareMode = element.attributes.compareMode;\n if (_.isNil(compareMode)) {\n compareMode = COMPAREMODE_DEFAULT;\n }\n // normalize compareMode\n compareMode = _.toUpper(compareMode);\n\n if (!_.isNil(fieldName) && !_.isNil(direction) && !_.isNil(compareMode)) {\n\n // get converter\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // build & store selector\n let selector = (object => {\n return converter(object[fieldName]);\n });\n selectors.push(selector);\n\n // store direction\n directions.push(_.toLower(direction));\n\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode , \"' at <each>.<orderCriteria>.<orderCriterion> element\");\n }\n\n } else {\n // missing mandatory attribute => log error\n this.__log.error(\"Missing mandatory attribute at <each>.<orderCriteria>.<orderCriterion> element\");\n }\n });\n\n if (selectors.length > 0 && directions.length > 0 && selectors.length === directions.length) {\n // sort using selectors & directions\n listObject = _.orderBy(listObject, selectors, directions);\n } else {\n // no order criterion fields / directions => log error\n this.__log.error(\"<each>.<orderCriteria> element does not contain any applicable <orderCriterion> elements - sorting skipped\");\n }\n\n } else {\n // no order criterion elements => log error\n this.__log.error(\"<each>.<orderCriteria> element must contain at least one <orderCriterion> element\");\n }\n }\n return listObject;\n }\n\n __replaceEachTagsInTables(tbodyNode, tableNode, params) {\n let eachNode = _.find(tbodyNode.elements, {name:EACH_NODE});\n\n if (!_.isNil(eachNode)){\n let newTableRows = [];\n\n _.forEach(tbodyNode.elements, (rowNode) => {\n if (rowNode.name === EACH_NODE) {\n newTableRows = _.concat(newTableRows, this.__resolveEachTag(rowNode, tableNode, params));\n } else {\n newTableRows.push(rowNode);\n }\n });\n\n tbodyNode.elements = newTableRows;\n }\n }\n\n __isBoDeclared(boName){\n let isDeclared = false;\n\n _.forEach(this.__declarationsNode.elements, declaration =>{\n if(declaration.name === \"DataDeclaration\") {\n if(declaration.attributes.name === boName){\n isDeclared = true;\n }\n }\n });\n\n return isDeclared;\n }\n\n __replaceTogglesIfApplicable(resolvedMacro, macroResult, localization){\n let result = resolvedMacro;\n if (!_.isNil(macroResult.toggleId)) {\n try {\n if (!_.isNil(macroResult.toggleField)) {\n result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro, macroResult.toggleField);\n } else {\n result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro);\n }\n } catch(ex){\n if(ex.message.indexOf(\"Invalid toggle value\") > -1){\n\n result = localization.resolve(\"Framework.PrintV2_ToggleValueNotFound\", \"Toggle value not found\");\n result = result + \": \" + resolvedMacro;\n } else if (ex.message.indexOf(\"Invalid toggle field\") > -1) {\n result = localization.resolve(\"Framework.PrintV2_ToggleFieldNotFound\", \"Toggle field not found\");\n result = result + \": \" + macroResult.toggleField;\n } else {\n result = localization.resolve(\"Framework.PrintV2_ToggleNotFound\", \"Toggle not found\");\n result = result + \": \" + macroResult.toggleId;\n }\n\n }\n }\n return result;\n }\n\n __ensureAttributes(currentNode) {\n if(_.isNil(currentNode.attributes)){\n currentNode.attributes = {};\n }\n }\n\n __resolveNumberBinding(resolvedMacro, macroResult, attributeNode){\n if(resolvedMacro !== \"null\" && !_.isNil(resolvedMacro)){\n attributeNode.nonRoundedValue = resolvedMacro;\n return this.__format.formatDecimalV2(resolvedMacro, macroResult.numberFormat,\n this.__decimalSeparator, this.__thousandSeparator);\n } else {\n return \" \";\n }\n\n }\n\n __resolveDateBinding(resolvedMacro, macroResult){\n if(resolvedMacro !== \"null\" && !_.isNil(resolvedMacro)) {\n let actualDateTimeFormat = macroResult.dateTimeFormat;\n switch (actualDateTimeFormat){\n case \"shortDate\": actualDateTimeFormat = this.__localization.shortDateFormat;\n break;\n case \"date\": actualDateTimeFormat = this.__localization.dateFormat;\n break;\n case \"dateTime\": actualDateTimeFormat = this.__localization.dateTimeFormat;\n break;\n case \"time\": actualDateTimeFormat = this.__localization.timeFormat;\n break;\n default: break;\n }\n return this.__format.formatDate(resolvedMacro, actualDateTimeFormat);\n } else {\n return \" \";\n }\n }\n\n __resolveScalarBinding(currentNode, bindingText, params) {\n let bos = params.getBOs();\n let localization = this.__localization;\n let resolvedMacro = null;\n let macroResult = this.__macroHandler.parseMacro(bindingText);\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {\n let scope = macroResult.scope;\n let bindingPath = macroResult.path;\n\n if (scope === \"Globals\") {\n let defaultLabel = macroResult.defaultLabel;\n resolvedMacro = localization.resolve(bindingPath, defaultLabel);\n }\n else if (scope === \"Labels\") {\n let defaultLabel = macroResult.defaultLabel;\n\n if(bindingPath.indexOf(\".\") === -1) {\n // support for shortcut\n let lastUnderscoreIndex = this.__printLayoutName.lastIndexOf(\"_\");\n let printLayoutNameWithoutSpecificVersion = this.__printLayoutName;\n if(lastUnderscoreIndex > -1) {\n printLayoutNameWithoutSpecificVersion =\n this.__printLayoutName.substr(0, lastUnderscoreIndex);\n }\n bindingPath = \"PrintLayouts.\" + printLayoutNameWithoutSpecificVersion + \".\" + bindingPath;\n }\n\n resolvedMacro = localization.resolve(bindingPath, defaultLabel);\n } else if (scope === \"Declarations\") {\n let boEndIndex = bindingPath.indexOf(\".\");\n let boName;\n\n if(boEndIndex === -1){\n // this means it is a top level binding like Declarations::data\n boName = bindingPath;\n boEndIndex = bindingPath.length;\n }else {\n boName = bindingPath.substring(0, boEndIndex);\n }\n\n let isBoDeclared = this.__isBoDeclared(boName);\n\n if(isBoDeclared){\n let deeperBindingPath = bindingPath.substring(boEndIndex + 1, bindingPath.length);\n let actualBo = _.get(bos, boName);\n\n if(deeperBindingPath.length > 0){\n resolvedMacro = _.get(actualBo, deeperBindingPath);\n } else {\n // this means it is a top level binding like Declarations::data\n resolvedMacro = actualBo;\n }\n\n if (!_.isNil(macroResult.numberFormat)) {\n this.__ensureAttributes(currentNode);\n resolvedMacro = this.__resolveNumberBinding(resolvedMacro, macroResult,currentNode.attributes);\n } if(!_.isNil(macroResult.dateTimeFormat)) {\n resolvedMacro = this.__resolveDateBinding(resolvedMacro, macroResult);\n } else {\n resolvedMacro = this.__replaceTogglesIfApplicable(resolvedMacro, macroResult, localization);\n }\n\n\n } else {\n this.__log.error(\"Usage of undeclared BO:\", boName);\n }\n }\n }\n\n return resolvedMacro;\n }\n\n __replaceMacrosInTextNodes(parentJsonElement, currentJsonElement, params){\n let xmlNodeText = currentJsonElement.text;\n let macroRegex = this.__macroHandler.getMacroRegex();\n let arrayMatches = xmlNodeText.match(macroRegex);\n\n if(!_.isNil(arrayMatches) && arrayMatches.length > 0) {\n _.forEach(arrayMatches, currentMatch => {\n let resolvedMacro = this.__resolveScalarBinding(parentJsonElement, currentMatch, params);\n if (!_.isNil(resolvedMacro)){\n if(_.isString(resolvedMacro)){\n resolvedMacro = resolvedMacro.replace(/\\$/g, \"$$$$\");\n }\n xmlNodeText = xmlNodeText.replace(currentMatch, resolvedMacro);\n currentJsonElement.text = xmlNodeText;\n }\n });\n }\n }\n\n __isImageDeclared(imageName){\n let isDeclared = false;\n\n _.forEach(this.__declarationsNode.elements, declaration => {\n if(declaration.name === \"DataDeclaration\") {\n if ((declaration.attributes.name === imageName) && (declaration.attributes.type === \"Image\" || declaration.attributes.type === \"Signature\")) {\n isDeclared = true;\n }\n }\n });\n\n return isDeclared;\n }\n\n __sanitizeImageAttributes(currentJsonElement/*, params*/) {\n let width = currentJsonElement.attributes.width;\n let height = currentJsonElement.attributes.height;\n let fit = currentJsonElement.attributes.fit;\n this.__integerStringRegExp.lastIndex = 0; // reset last index\n if(!this.__integerStringRegExp.test(height)){\n delete currentJsonElement.attributes.height;\n }\n this.__integerStringRegExp.lastIndex = 0; // reset last index\n if(!this.__integerStringRegExp.test(width)){\n delete currentJsonElement.attributes.width;\n }\n\n let fitRegExp = /^\\d+,\\d+$/g;\n if(!fitRegExp.test(fit)){\n delete currentJsonElement.attributes.fit;\n }\n if(!_.isNil(width) || !_.isNil(height)){\n delete currentJsonElement.attributes.fit;\n }\n }\n\n __processImageNode(currentJsonElement, params){\n this.__replaceMacrosInImageNodes(currentJsonElement, params);\n this.__sanitizeImageAttributes(currentJsonElement, params);\n }\n\n __replaceMacrosInImageNodes(currentJsonElement, params){\n let images = params.getImages();\n let bindingText = currentJsonElement.attributes.src;\n let macroResult = this.__macroHandler.parseMacro(bindingText);\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {\n let scope = macroResult.scope;\n let imageName = macroResult.path;\n if (scope === \"Declarations\") {\n if (this.__isImageDeclared(imageName)) {\n let imageObject = images[imageName];\n if(_.isNil(imageObject.src) && imageObject.type === \"Signature\") {\n currentJsonElement.name = \"p\";\n } else if (!_.isNil(imageObject) && !_.isNil(imageObject.src)\n && !_.isNil(imageObject.mimeType) && (!_.isNil(imageObject.imageId) || imageObject.type === \"Signature\")) {\n let dataUrlStart = \"data:\" + imageObject.mimeType +\";base64,\";\n let dataUrl = imageObject.src;\n\n if (!_.startsWith(dataUrl, dataUrlStart)) {\n dataUrl = dataUrlStart + dataUrl;\n }\n\n // => use imageName as id\n currentJsonElement.attributes.id = imageName;\n currentJsonElement.attributes.mimeType = imageObject.mimeType;\n\n currentJsonElement.attributes.src = dataUrl;\n if (imageObject.type === \"Signature\") {\n // https://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript\n const m = encodeURIComponent(dataUrl).match(/%[89ABab]/g);\n const sizeInBytes = dataUrl.length + (m ? m.length : 0);\n if (sizeInBytes > SIGNATURE_SIZE_LIMIT) {\n this.__log.error(\"Usage of signature larger than 200kb:\", imageName);\n this.__assignFallbackImage(currentJsonElement); \n }\n }\n } else {\n this.__log.error(\"Usage of undefined image:\", imageName);\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Usage of undeclared image:\", imageName);\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Invalid binding value:'\"+ bindingText + \"' found in image.\");\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Invalid value:'\"+ bindingText + \"' found in image.\");\n this.__assignFallbackImage(currentJsonElement);\n }\n }\n\n __assignFallbackImage(currentJsonElement) {\n const errorFallbackImage = \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAzOCAzOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzggMzg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNGM0Y2Rjg7fQ0KCS5zdDF7ZmlsbDojQjRDN0Q0O30NCjwvc3R5bGU+DQo8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxOSIgY3k9IjE5IiByPSIxOSIvPg0KPGNpcmNsZSBjbGFzcz0ic3QxIiBjeD0iMjkuMSIgY3k9IjExLjQiIHI9IjIuMyIvPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0yMC43LDIyLjZsLTQtNC44bDAuNS0xLjdsLTIuMy00Yy0wLjEtMC4yLTAuMy0wLjItMC40LDBMNS4yLDI2LjljLTAuMSwwLjIsMCwwLjQsMC4yLDAuNGgxNUwxNywyMi41DQoJCQlMMjAuNywyMi42eiBNMzIuOCwyN2wtNy43LTljLTAuMS0wLjItMC40LTAuMy0wLjYtMC4xbC0yLjgsMmwxLjksNS41TDIwLDI0LjVsMS4yLDIuOGgxMS40QzMyLjksMjcuMywzMi45LDI3LjIsMzIuOCwyN3oiLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==\";\n if (!_.isNil(currentJsonElement) && !_.isUndefined(currentJsonElement.attributes)) {\n currentJsonElement.attributes.id = \"$$$fallbackImage$$$\";\n currentJsonElement.attributes.mimeType = \"image/svg+xml\";\n currentJsonElement.attributes.src = errorFallbackImage;\n } \n }\n\n __getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex){\n let tcid = TableCellIdentifier.create(eachName, listItemIndex, correlatedListItemIndex, columnIndex);\n return tcid.toString();\n }\n\n}\n\nmodule.exports = ContractToIntermediateJSON;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/contractToIntermediateJSONClass.js?");
405
+ eval("/*\n * FILE_HEADER\n */\n\nconst parser = __webpack_require__(/*! xml-js */ \"./node_modules/xml-js/lib/index.js\");\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst Localization = __webpack_require__(/*! ./localizationClass */ \"./src/localizationClass.js\");\nconst MacroHandler = __webpack_require__(/*! ./macroHandlerClass */ \"./src/macroHandlerClass.js\");\nconst Format = __webpack_require__(/*! ./formatClass */ \"./src/formatClass.js\");\nconst LogClass = __webpack_require__(/*! ./logClass */ \"./src/logClass.js\");\nconst TableCellIdentifier = __webpack_require__(/*! ./tableCellIdentifier */ \"./src/tableCellIdentifier.js\");\n\nconst EACH_NODE = \"each\";\nconst TABLE_NODE = \"table\";\nconst TBODY_NODE = \"tbody\";\nconst THEAD_NODE = \"thead\";\nconst HEADER_NODE = \"header\";\nconst FOOTER_NODE = \"footer\";\nconst PRINT_LAYOUT_NODE = \"PrintLayout\";\nconst REPORT_LAYOUT_NODE = \"ReportLayout\";\nconst DECLARATIONS_NODE = \"Declarations\";\nconst DOCUMENT_PROPERTIES_NODE = \"DocumentProperties\";\nconst PROPERTY_NODE = \"Property\";\nconst H1 = \"h1\";\nconst H2 = \"h2\";\nconst PARAGRAPH = \"p\";\nconst IMAGE = \"img\";\nconst TR_NODE = \"tr\";\nconst TH_NODE = \"th\";\nconst TD_NODE = \"td\";\nconst SUM_NODE = \"sum\";\n\nconst FILTERS_NODE = \"filters\";\nconst FILTER_NODE = \"filter\";\nconst ORDERCRITERIA_NODE = \"orderCriteria\";\nconst ORDERCRITERION_NODE = \"orderCriterion\";\nconst CORRELATION_NODE = \"correlation\";\n\nconst COMPAREMODE_DEFAULT = \"STRING\";\nconst COMPAREMODE_NUMBER = \"NUMBER\";\n\nconst DIV_NODE = \"div\";\nconst DIV_VISIBILITY_NODE = \"visibility\";\nconst DIV_VISIBILITY_BINDING_NODE = \"visibilityBinding\";\nconst DIV_VISIBILITY_CONDITION_NODE = \"visibilityCondition\";\nconst SIGNATURE_SIZE_LIMIT = 200 * 1024;\n\nconst ELEMENT_TYPE = {\n \"TEXT\": \"text\",\n \"ELEMENT\": \"element\",\n \"COMMENT\": \"comment\"\n};\n\nclass ContractToIntermediateJSON {\n constructor(log) {\n if(!(log instanceof LogClass)) {\n throw new Error(\"Invalid constructor parameters\");\n }\n this.__log = log;\n // addParent: true,\n this.__xmlParserLibraryOptions = {\n spaces: 2,\n compact: false\n };\n\n this.__printLayoutName = null;\n this.__declarationsNode = null;\n this.__localization = null;\n this.__toggles = null;\n this.__macroHandler = new MacroHandler(log);\n this.__integerStringRegExp = /^\\d+$/g;\n\n this.__decimalSeparator = \".\";\n this.__thousandSeparator = \",\";\n this.__format = null;\n this.__clearAllCaches();\n }\n\n __clearAllCaches(){\n this.__tableIndex = {};\n this.__sumTdThList = [];\n this.__eachCache = {};\n this.__divCache = [];\n }\n\n __checkDataForDeclaredItems(params, declarationNode){\n let errorMessage = \"\";\n let bos = params.getBOs();\n let images = params.getImages();\n\n let dataMissing = function dataMissing(itemType, itemName) {\n let dataMissingMsg = `Missing declared ${itemType} \"${itemName}\" in print data.`;\n if(errorMessage.length === 0) {\n errorMessage = errorMessage + dataMissingMsg;\n } else {\n errorMessage = errorMessage + \"\\n\" + dataMissingMsg;\n }\n };\n\n if(!_.isNil(declarationNode) && !_.isNil(declarationNode.elements)) {\n _.forEach(declarationNode.elements, (dataDeclaration) => {\n if(!_.isNil(dataDeclaration.attributes)){\n let itemName = dataDeclaration.attributes.name;\n if(!_.isNil(dataDeclaration.attributes.type)) {\n if (dataDeclaration.attributes.type === \"Image\") {\n if(!_.isNil(itemName) && _.isNil(images[itemName])){\n dataMissing(\"Image\", itemName);\n }\n } else if (dataDeclaration.attributes.type === \"Signature\") {\n if(!_.isNil(itemName) && _.isNil(images[itemName])){\n dataMissing(\"Signature\", itemName);\n }\n } else {\n if(!_.isNil(itemName) && _.isNil(bos[itemName])){\n dataMissing(\"BO\", itemName);\n }\n }\n }\n }\n });\n\n if(errorMessage.length > 0){\n throw new Error(errorMessage);\n }\n }\n }\n __checkInput(contract, params) {\n if(_.isNil(params) || !_.isObject(params) || _.isArray(params)) {\n throw new Error(\"Invalid value for parameter 'params'.\");\n }\n if(!_.isString(contract)) {\n throw new Error(\"Invalid value for parameter 'contract'.\");\n }\n }\n\n toJson(contract, params) {\n this.__checkInput(contract, params);\n this.__clearAllCaches();\n this.__toggles = params.getToggles();\n this.__format = new Format();\n this.__localization = new Localization(params.getLocalization());\n\n this.__decimalSeparator = this.__localization.separatorDecimal;\n this.__thousandSeparator = this.__localization.separatorThousand;\n\n let contractJson = this.__parse(contract);\n\n this.__declarationsNode = this.__getDeclarationsNode(contractJson);\n this.__checkDataForDeclaredItems(params, this.__declarationsNode);\n\n this.__walkParseTree(contractJson, {}, \"\", params);\n\n let printLayoutNode = this.__findPrintLayoutNode(contractJson);\n let reportLayout = _.find(printLayoutNode.elements, {name:REPORT_LAYOUT_NODE});\n\n if(_.isNil(reportLayout)) {\n throw new Error(\"Missing ReportLayout node in PrintLayout.\");\n } \n \n this.__processDivs(params);\n this.__postProcessReportLayout(reportLayout);\n this.__processSumRecords(); \n \n this.__addWatermark(reportLayout, params);\n this.__configureAutomaticPageBreak(reportLayout, params);\n\n let documentPropertiesRaw = _.find(printLayoutNode.elements, {name:DOCUMENT_PROPERTIES_NODE});\n let documentProperties = this.__reformatDocumentProperties(documentPropertiesRaw); \n\n return {\"reportLayout\": reportLayout, \"documentProperties\": documentProperties, \"printFont\": params.getPrintFont() };\n }\n\n __processDivs(params) {\n if (!_.isNil(this.__divCache) && _.isArray(this.__divCache)) {\n\n for (let index = 0; index < this.__divCache.length; index++) {\n const divData = this.__divCache[index];\n\n let result = this.__checkForDivToBeVisible(divData.divNode, params);\n if (result) {\n // rescue div children and put them in parent elements\n let nodes = _.filter(divData.divNode.elements, node => node.type === ELEMENT_TYPE.ELEMENT && node.name !== DIV_VISIBILITY_NODE);\n if (!_.isNil(nodes) && _.isArray(nodes)) {\n // remember div id for future styling / layouting ...\n _.forEach(nodes, node => {\n this.__ensureAttributes(node);\n node.attributes.div = divData.id;\n });\n let divIndex = _.findIndex(divData.parentNode.elements, node => _.isEqual(node, divData.divNode));\n divData.parentNode.elements.splice(divIndex, 0, ..._.cloneDeep(nodes));\n }\n }\n // remove div from parent\n _.remove(divData.parentNode.elements, node => _.isEqual(node, divData.divNode)); \n }\n\n }\n }\n\n __postProcessReportLayout(reportLayoutNode) {\n // get header node\n let headerNode = _.find(reportLayoutNode.elements, {name: HEADER_NODE});\n if (!_.isNil(headerNode)) {\n // => check header node and remove empty tables\n this.__removeEmptyTables(headerNode); \n }\n // get footer node\n let footerNode = _.find(reportLayoutNode.elements, {name: FOOTER_NODE});\n if (!_.isNil(footerNode)) {\n // => check footer node and remove empty tables\n this.__removeEmptyTables(footerNode);\n } \n // => check reportlayout node and remove empty tables & divs\n this.__removeEmptyTables(reportLayoutNode);\n }\n\n __removeEmptyTables(containerNode) {\n if (_.isNil(containerNode) || (!_.isNil(containerNode) && !_.isUndefined(containerNode.elements) && containerNode.elements.length === 0)) {\n return;\n }\n let tablesToRemove = [];\n _.forEach(containerNode.elements, node => {\n if (node.type === ELEMENT_TYPE.ELEMENT && node.name === TABLE_NODE && node.toBeRemoved) {\n tablesToRemove.push(node);\n }\n });\n if (tablesToRemove.length > 0) {\n _.forEach(tablesToRemove, tableNode => {\n _.remove(containerNode.elements, node => _.isEqual(node, tableNode));\n });\n }\n }\n\n __addWatermark(reportLayout, params){\n if(_.isNil(reportLayout)){\n return;\n }\n this.__ensureAttributes(reportLayout);\n let watermarkInMetadata = params.getMetadata()[params.getMetadataKeys().WATERMARK];\n if(_.isString(watermarkInMetadata) && _.trim(watermarkInMetadata) !== \"\") {\n reportLayout.attributes.watermark = watermarkInMetadata;\n } else if (!_.isNil(watermarkInMetadata) && !_.isString(watermarkInMetadata)) {\n this.__log.error(\"Invalid value for watermark.\");\n }\n\n }\n \n __configureAutomaticPageBreak(reportLayout, params){\n if(_.isNil(reportLayout)){\n return;\n }\n this.__ensureAttributes(reportLayout); \n let automaticPageBreak = false; // default \n let automaticPageBreakInMetadata = params.getMetadata()[params.getMetadataKeys().AUTOMATIC_PAGEBREAK];\n if(!_.isNil(automaticPageBreakInMetadata) && _.isBoolean(automaticPageBreakInMetadata)) {\n automaticPageBreak = automaticPageBreakInMetadata; \n } \n reportLayout.attributes.automaticPageBreak = automaticPageBreak;\n }\n\n __reformatDocumentProperties(documentPropertiesRaw){\n let result = {};\n if(!_.isNil(documentPropertiesRaw)){\n // this means the contract has document properties set\n _.forEach(documentPropertiesRaw.elements, ele => {\n\n if(ele.type === ELEMENT_TYPE.ELEMENT){\n let sanitizedKey = ele.attributes.key.replace(/\\s/g, \"\");\n result[sanitizedKey]= ele.attributes.value;\n }\n });\n }\n return result;\n }\n\n __findPrintLayoutNode(contractJson) {\n let printLayoutNode = _.find(contractJson.elements, {name:PRINT_LAYOUT_NODE});\n if(_.isNil(printLayoutNode)) {\n throw new Error(\"Missing PrintLayout node.\");\n }\n return printLayoutNode;\n }\n\n __getDeclarationsNode(contractJson) {\n let printLayoutNode = this.__findPrintLayoutNode(contractJson);\n let declarationsNode = _.find(printLayoutNode.elements, {name: DECLARATIONS_NODE});\n if(_.isNil(declarationsNode)) {\n declarationsNode = {};\n }\n return declarationsNode;\n }\n __newXMLconvertibleJsonNode(type, children, otherAttributes) {\n let newNode = {\n \"type\": type\n };\n if(!_.isNil(children)) {\n newNode.elements = _.castArray(children);\n }\n _.assign(newNode, otherAttributes);\n return newNode;\n }\n\n __walkParseTree(contractJson, parentElement, parentName, params) {\n // processing ...\n this.__processCurrentElement(contractJson, parentElement, parentName, params);\n // recursion ...\n if(!_.isNil(contractJson.elements)) {\n _.remove(contractJson.elements, (ele)=> {\n if (ele.type === ELEMENT_TYPE.COMMENT) {\n return true;\n } else {\n return false;\n }\n });\n _.forEach(contractJson.elements, (ele) => {\n this.__walkParseTree(ele, contractJson, contractJson.name, params);\n });\n }\n // post-processing ...\n this.__postProcessCurrentElement(contractJson);\n }\n\n __postProcessCurrentElement(currentJsonElement) {\n if (!_.isNil(currentJsonElement)) {\n if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT) {\n if (currentJsonElement.name === TR_NODE) {\n\n // perform colSpan handling ...\n this.__applyColSpanHandling(currentJsonElement);\n\n } else if (currentJsonElement.name === TABLE_NODE) {\n\n // perform rowSpan handling ...\n this.__applyRowSpanHandling(currentJsonElement);\n // normalize table ...\n this.__normalizeTable(currentJsonElement);\n // check for empty table ...\n this.__checkForEmptyTable(currentJsonElement);\n\n } \n }\n }\n }\n\n __checkForDivToBeVisible(divNode, params) {\n let result = true;\n if (!_.isNil(divNode) && _.isArray(divNode.elements)) { \n\n // set result to 'true' for easier evaluation ... \n let divVisibilityNode = _.find(divNode.elements, {name: DIV_VISIBILITY_NODE});\n if (!_.isNil(divVisibilityNode) && _.isArray(divVisibilityNode.elements)) {\n\n // check for visibilityBinding elements ...\n let divVisibilityBindingNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_BINDING_NODE});\n if (!_.isNil(divVisibilityBindingNodes) && _.isArray(divVisibilityBindingNodes) && divVisibilityBindingNodes.length > 0) {\n for (let i = 0; i < divVisibilityBindingNodes.length; i++) {\n // process visibilityBinding ...\n let divVisibilityBindingNode = divVisibilityBindingNodes[i];\n result = this.__evaluateVisibilityBinding(divVisibilityBindingNode, params);\n // exit if visibility criteria cannot be met ...\n if (!result) {\n break;\n }\n }\n }\n \n if (result) {\n // check for visibilityCondition elements ...\n let divVisibilityConditionNodes = _.filter(divVisibilityNode.elements, {name: DIV_VISIBILITY_CONDITION_NODE});\n if (!_.isNil(divVisibilityConditionNodes) && _.isArray(divVisibilityConditionNodes) && divVisibilityConditionNodes.length > 0) {\n for (let i = 0; i < divVisibilityConditionNodes.length; i++) {\n // process visibilityCondition ...\n let divVisibilityConditionNode = divVisibilityConditionNodes[i];\n result = this.__evaluateVisibilityCondition(divVisibilityConditionNode);\n // exit if visibility criteria cannot be met ...\n if (!result) {\n break;\n }\n }\n }\n }\n\n // => remove visibility node ...\n _.remove(divNode.elements, node => (node.type === ELEMENT_TYPE.ELEMENT && node.name === DIV_VISIBILITY_NODE));\n } \n \n }\n return result;\n }\n\n __evaluateVisibilityBinding(visiblityBindingNode, params) {\n let result = true;\n\n // <visiblityBinding binding=\"{{Declarations::header.itemCount}}\" value=\"0\" operator=\"GT\" compareMode=\"NUMBER\" />\n if (!_.isNil(visiblityBindingNode)) {\n\n let expression = this.__getVisibilityExpression(visiblityBindingNode, params);\n if (!_.isNil(expression) && _.isFunction(expression)) {\n result = expression();\n } \n }\n\n return result;\n }\n\n __getVisibilityExpression(visiblityBindingOrCondition, params) {\n let expression = null;\n\n // extract attributes\n let binding = null;\n let eachName = null;\n let compareMode = COMPAREMODE_DEFAULT;\n let operator = visiblityBindingOrCondition.attributes.operator;\n let value = visiblityBindingOrCondition.attributes.value;\n let comparisonValue = null; \n\n if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_BINDING_NODE) {\n // => handle visibilityBinding\n binding = visiblityBindingOrCondition.attributes.binding;\n // get compareMode if available\n if (_.has(visiblityBindingOrCondition.attributes, \"compareMode\")) {\n compareMode = visiblityBindingOrCondition.attributes.compareMode;\n }\n // resolve binding\n comparisonValue = this.__resolveScalarBinding(visiblityBindingOrCondition, binding, params);\n if (_.isNil(comparisonValue)) {\n // unknown binding => log error\n this.__log.error(\"Unsupported binding '\", binding, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n\n } else if (visiblityBindingOrCondition.type === ELEMENT_TYPE.ELEMENT && visiblityBindingOrCondition.name === DIV_VISIBILITY_CONDITION_NODE) {\n // => handle visibilityCondition\n eachName = visiblityBindingOrCondition.attributes.eachName;\n // set compareMode to \"NUMBER\"\n compareMode = COMPAREMODE_NUMBER;\n // resolve eachName\n if (_.has(this.__eachCache, eachName) && _.isArray(this.__eachCache[eachName])) {\n comparisonValue = this.__eachCache[eachName].length; \n } \n if (_.isNil(comparisonValue)) {\n // unknown eachName => log error\n this.__log.error(\"Unsupported eachName '\", eachName, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n }\n\n // normalize compareMode & operator\n compareMode = _.toUpper(compareMode);\n operator = _.toUpper(operator);\n\n if (!_.isNil(comparisonValue) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {\n\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // convert comparison value once\n let x = converter(comparisonValue);\n // convert value once\n let y = converter(value);\n\n let comparator = this.__resolveComparator(operator);\n if (!_.isNil(comparator) && _.isFunction(comparator)) {\n\n // build filter expression performing comparison\n expression = ( () => {\n return comparator(x, y);\n });\n\n } else {\n // unknown operator => log error\n this.__log.error(\"Unsupported operator '\", operator, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode, \"' at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n } else {\n // missing/invalid mandatory attribute => log error\n this.__log.error(\"Missing/invalid mandatory attribute at <\" + visiblityBindingOrCondition.name + \"> element\");\n }\n\n return expression;\n }\n\n __evaluateVisibilityCondition(visiblityConditionNode) {\n let result = true;\n\n // <visiblityCondition eachName=\"each1\" value=\"0\" operator=\"GT\" />\n if (!_.isNil(visiblityConditionNode)) {\n let expression = this.__getVisibilityExpression(visiblityConditionNode, null);\n if (!_.isNil(expression) && _.isFunction(expression)) {\n result = expression();\n } \n }\n\n return result;\n }\n\n __checkForEmptyTable(tableNode) {\n if (!_.isNil(tableNode)) {\n // check thead\n let thead = _.find(tableNode.elements, {name:THEAD_NODE});\n if (_.isNil(thead) || _.isUndefined(thead.elements) || thead.elements.length === 0) {\n // check tbody\n let tbody = _.find(tableNode.elements, {name:TBODY_NODE});\n if (_.isNil(tbody) || (!_.isNil(tbody) && !_.isUndefined(tbody.elements) && tbody.elements.length === 0)) {\n // table is empty => to be removed\n tableNode.toBeRemoved = true;\n }\n } \n }\n }\n\n __applyColSpanHandling(tableRowNode) {\n let newElements = [];\n _.forEachRight(tableRowNode.elements, tdth => {\n if (!_.isNil(tdth.attributes)) {\n let colSpan = tdth.attributes.colSpan;\n if (!_.isNil(colSpan)) {\n colSpan = _.toInteger(colSpan);\n if (!_.isNaN(colSpan) && colSpan > 1) {\n let elementsToAdd = colSpan - 1;\n let elementName = tdth.name;\n\n let newAttributes = {\n \"name\": elementName,\n \"doNotReplace\": true\n };\n for (let i = 0; i < elementsToAdd; i++) {\n let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, newAttributes);\n newElements.push(newElement);\n }\n }\n }\n }\n newElements.push(tdth);\n });\n // replace elements if applicable\n let elementCount = 0;\n if (!_.isNil(tableRowNode.elements)) {\n elementCount = tableRowNode.elements.length;\n }\n if (newElements.length !== elementCount) {\n tableRowNode.elements = _.reverse(newElements);\n }\n }\n\n __applyRowSpanHandling(tableNode) {\n if (!_.isNil(tableNode)) {\n // fix thead\n this.__applyRowSpanHandlingToContainer(tableNode, THEAD_NODE);\n\n // fix tbody\n this.__applyRowSpanHandlingToContainer(tableNode, TBODY_NODE);\n }\n }\n\n __applyRowSpanHandlingToContainer(tableNode, trContainerNodeName) {\n\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n // check tbody / thead\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode) && !_.isNil(trContainerNode.elements)) {\n for (let i = 0; i < trContainerNode.elements.length; i++) {\n // check tr\n let tr = trContainerNode.elements[i];\n if (!_.isNil(tr) && !_.isNil(tr.elements)) {\n for (let j = 0; j < tr.elements.length; j++) {\n // check td / th\n let tdth = tr.elements[j];\n if (!_.isNil(tdth)) {\n if (!_.isNil(tdth.attributes)) {\n let rowSpan = tdth.attributes.rowSpan;\n if (!_.isNil(rowSpan)) {\n rowSpan = _.toInteger(rowSpan);\n if (!_.isNaN(rowSpan) && rowSpan > 1) {\n let rowsToUpdate = rowSpan - 1;\n let elementsToAdd = 1; // rowSpan element\n let elementName = tdth.name;\n let colSpan = tdth.attributes.colSpan;\n if (!_.isNil(colSpan)) {\n colSpan = _.toInteger(colSpan);\n if (!_.isNaN(colSpan) && colSpan > 1) {\n elementsToAdd = elementsToAdd + (colSpan - 1); // colSpan elements\n }\n }\n // update \"spanned\" rows\n for (let k = 0; k < rowsToUpdate; k++) {\n let rowIndex = i + k + 1;\n if (rowIndex < trContainerNode.elements.length) {\n let tr2 = trContainerNode.elements[rowIndex];\n\n if (_.isNil(tr2.elements)) {\n _.assign(tr2, {\"elements\": []});\n }\n\n let tr2Index = j;\n if (tr2Index > tr2.elements.length) {\n tr2Index = 0;\n }\n\n // insert elements ...\n for (let l = 0; l < elementsToAdd; l++) {\n let newElement = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, null, {\"name\": elementName, \"doNotReplace\": true});\n tr2.elements.splice(tr2Index, 0, newElement);\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n __normalizeTable(tableNode) {\n if (!_.isNil(tableNode)) {\n // get minimum thead width\n let minTableHeadWidth = this.__getTableContainerWidth(tableNode, THEAD_NODE);\n\n // get minimum tbody width\n let minTableBodyWidth = this.__getTableContainerWidth(tableNode, TBODY_NODE);\n\n // determine minimum width for table\n let minTableWidth = _.min([minTableHeadWidth, minTableBodyWidth]);\n\n // repair thead\n this.__normalizeTableWidth(tableNode, THEAD_NODE, minTableWidth);\n\n // repair tbody\n this.__normalizeTableWidth(tableNode, TBODY_NODE, minTableWidth);\n }\n }\n\n __getTableContainerWidth(tableNode, trContainerNodeName) {\n let minWidth = Number.MAX_SAFE_INTEGER;\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode)) {\n _.forEach(trContainerNode.elements, tr => {\n if (!_.isNil(tr) && tr.name === TR_NODE && !_.isNil(tr.elements)) {\n let thCount = tr.elements.length;\n if (thCount < minWidth) {\n minWidth = thCount;\n }\n }\n });\n }\n }\n return minWidth;\n }\n\n __normalizeTableWidth(tableNode, trContainerNodeName, tableWidth) {\n if (!_.isNil(tableNode) && !_.isNil(trContainerNodeName)) {\n let trContainerNode = _.find(tableNode.elements, {name:trContainerNodeName});\n if (!_.isNil(trContainerNode)) {\n let normalized = false;\n _.forEach(trContainerNode.elements, tr => {\n if (!_.isNil(tr) && tr.name === TR_NODE) {\n let trWidth = 0;\n if (!_.isNil(tr.elements)) {\n trWidth = tr.elements.length;\n }\n if (trWidth > tableWidth) {\n // remove elements ...\n tr.elements.splice(tableWidth, trWidth - tableWidth);\n normalized = true;\n }\n }\n });\n if (normalized) {\n let tableName = tableNode.attributes.name;\n let errorMessage = \"<\" + trContainerNodeName + \"> element of <table> '\" + tableName + \"' has been normalized due to improper formatting\";\n this.__log.error(errorMessage);\n }\n }\n }\n }\n\n __processSumRecords() {\n let idxSum;\n for (idxSum=0; idxSum < this.__sumTdThList.length; idxSum++) {\n let tdthNode = this.__sumTdThList[idxSum];\n let sumNode = _.find(tdthNode.elements, {name:SUM_NODE});\n let tableName = sumNode.attributes.table;\n let tablePos = parseFloat(sumNode.attributes.col, 10);\n let sumFormat = sumNode.attributes.numberFormat;\n let tableNode = this.__tableIndex[tableName];\n let sum = 0;\n\n if (!_.isNil(tableNode)) {\n let tBodyNode = _.find(tableNode.elements, {name:TBODY_NODE});\n if (!_.isNil(tBodyNode) && !_.isNil(tBodyNode.elements)) {\n _.forEach(tBodyNode.elements, (trNode) => {\n let tdNode = trNode.elements[tablePos];\n if(!_.isNil(tdNode)) {\n let bSum = false;\n let text = \"\";\n let nonRoundedValue = null;\n if (!_.isNil(tdNode.attributes)) {\n nonRoundedValue = tdNode.attributes.nonRoundedValue;\n }\n _.forEach(tdNode.elements, childElement => {\n if ((childElement.type === \"text\") && !_.isNil(childElement.isSum) && childElement.isSum) {\n bSum = true;\n } else if (childElement.type === \"text\") {\n text = childElement.text;\n } else if (childElement.type === \"element\" && childElement.name === SUM_NODE) {\n bSum = true;\n }\n });\n if (!bSum) {\n if (!_.isNil(nonRoundedValue)) {\n // nonRoundedValue only exists if a format was applied to the cell\n sum += nonRoundedValue;\n } else {\n let value = parseFloat(text, 10);\n if (!isNaN(value)) {\n sum += value;\n }\n }\n\n }\n }\n });\n } else {\n sum = 0;\n }\n } else {\n sum = 0;\n }\n\n let txtSum;\n\n if (!_.isNil(sumFormat)) {\n txtSum = this.__format.formatDecimalV2(sum, sumFormat, this.__decimalSeparator, this.__thousandSeparator);\n } else {\n txtSum = \"\" + sum;\n }\n\n let bText = false;\n\n _.forEach(tdthNode.elements, childElement => {\n if(childElement.type === \"text\") {\n childElement.text = txtSum + childElement.text;\n childElement.isSum = true;\n bText = true;\n }\n });\n\n if (!bText) {\n if (txtSum.length > 0) {\n tdthNode.elements.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": txtSum, \"doNotReplace\": true, \"isSum\": true}));\n }\n }\n\n this.__deleteInvalidNodes(tdthNode, [SUM_NODE], null);\n }\n }\n\n __processH1Node(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processH2Node(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processParagraphNode(currentJsonElement/*, params*/) {\n this.__makeTextOnlyNode(currentJsonElement);\n }\n\n __processPropertyNode(currentJsonElement, params){\n let resolvedMacro = this.__resolveScalarBinding(currentJsonElement, currentJsonElement.attributes.value, params);\n if(!_.isNil(resolvedMacro)) {\n if(_.isObject(resolvedMacro)) {\n resolvedMacro = JSON.stringify(resolvedMacro);\n }\n if(!_.isString(resolvedMacro)){\n resolvedMacro = \"\"+resolvedMacro;\n }\n currentJsonElement.attributes.value = resolvedMacro;\n }\n }\n\n __processPrintLayoutNode(currentJsonElement/*, parentName, params*/){\n this.__printLayoutName = currentJsonElement.attributes.name;\n }\n\n __processCurrentElement(currentJsonElement, parentElement, parentName, params) {\n if (currentJsonElement.doNotReplace) {\n return;\n }\n if(currentJsonElement.type === ELEMENT_TYPE.TEXT) {\n this.__replaceMacrosInTextNodes(parentElement, currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TBODY_NODE) {\n this.__replaceEachTagsInTables(currentJsonElement, parentElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === HEADER_NODE){\n this.__processHeaderNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === FOOTER_NODE){\n this.__processFooterNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === REPORT_LAYOUT_NODE){\n this.__processReportLayoutNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TABLE_NODE){\n this.__processTableNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === THEAD_NODE){\n this.__processTheadNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H1) {\n this.__processH1Node(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === H2) {\n this.__processH2Node(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PARAGRAPH) {\n this.__processParagraphNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === IMAGE) {\n this.__processImageNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PROPERTY_NODE) {\n this.__processPropertyNode(currentJsonElement, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TR_NODE){\n this.__processTrNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TD_NODE){\n this.__processTdThNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === TH_NODE){\n this.__processTdThNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === PRINT_LAYOUT_NODE){\n this.__processPrintLayoutNode(currentJsonElement, parentName, params);\n } else if (currentJsonElement.type === ELEMENT_TYPE.ELEMENT && currentJsonElement.name === DIV_NODE){\n this.__processDivNode(currentJsonElement, parentElement);\n }\n }\n\n __processDivNode(divNode, parentNode) {\n // cache divNode and parentNode for later ...\n this.__divCache.push({\n \"id\" : \"div\" + (this.__divCache.length + 1),\n \"divNode\" : divNode, // div to evaluate\n \"parentNode\": parentNode // parent to resolve\n }); \n }\n\n __parse(contract){\n let contractJson = parser.xml2js(contract,this.__xmlParserLibraryOptions);\n return contractJson;\n }\n\n __isLo(object){\n return _.isArray(object);\n }\n\n __makeTextOnlyNode(currentJsonElement) {\n _.remove(currentJsonElement.elements, (ele)=> { return ele.type !== ELEMENT_TYPE.TEXT;});\n if(currentJsonElement.elements.length === 0) {\n currentJsonElement.elements.push({type:ELEMENT_TYPE.TEXT, text: \"\"});\n }\n }\n\n __deleteInvalidNodes(currentJsonElement, deleteNames, preserveNames) {\n //Remove invalid nodes by positive or negative lists.\n let bDelete = !_.isNil(deleteNames) && _.isArray(deleteNames) && (deleteNames.length > 0);\n let bPreserve = !_.isNil(preserveNames) && _.isArray(preserveNames) && (preserveNames.length > 0);\n\n //It makes no sense to fill both lists or no list but we cannot provide a true/true or a false/false\n //scenario via Unit Tests. So we keep both paths split to fulfill the branch coverage.\n if (bDelete) {\n _.remove(currentJsonElement.elements, (ele)=> {\n let names = 0;\n for (names=0; names<deleteNames.length; names++) {\n if (ele.name === deleteNames[names]) {\n return true;\n }\n }\n return false;\n });\n }\n if (bPreserve) {\n _.remove(currentJsonElement.elements, (ele)=> {\n let names = 0;\n for (names=0; names<preserveNames.length; names++) {\n if (ele.name === preserveNames[names]) {\n return false;\n }\n }\n return true;\n });\n }\n }\n\n __processTheadNode(currentJsonElement/*, parentName, params*/){\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processTableNode(currentJsonElement/*, parentName, params*/){\n this.__deleteInvalidNodes(currentJsonElement, null, [THEAD_NODE, TBODY_NODE]);\n if (_.isNil(this.__tableIndex[currentJsonElement.attributes.name])) {\n this.__tableIndex[currentJsonElement.attributes.name] = currentJsonElement;\n }\n }\n\n __processTrNode(currentJsonElement, parentName/*, params*/){\n if (parentName === THEAD_NODE) {\n this.__deleteInvalidNodes(currentJsonElement, null, [TH_NODE]);\n } else if (parentName === TBODY_NODE) {\n this.__deleteInvalidNodes(currentJsonElement, null, [TD_NODE]);\n }\n }\n\n __processTdThNode(currentJsonElement, /*, parentName, params*/){\n let sumNode = _.find(currentJsonElement.elements, {name:SUM_NODE});\n\n if (!_.isNil(sumNode)) {\n this.__sumTdThList.push(currentJsonElement);\n }\n }\n\n __processReportLayoutNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processHeaderNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __processFooterNode(currentJsonElement/*, parentName, params*/) {\n this.__deleteInvalidNodes(currentJsonElement, [EACH_NODE], null);\n }\n\n __createTableDataCache(tableRow, params) {\n let tdCacheList = [];\n\n _.forEach(tableRow.elements, subtag => {\n let tdCacheElement = {};\n tdCacheElement.elements = [];\n\n if (subtag.name === \"td\") {\n if (!_.isNil(subtag.elements)) {\n let allTextNodeTexts = [];\n _.forEach(subtag.elements, tdSubtag => {\n if (tdSubtag.type === ELEMENT_TYPE.TEXT) {\n this.__replaceMacrosInTextNodes(subtag, tdSubtag, params);\n allTextNodeTexts.push(_.trim(tdSubtag.text));\n } else if (tdSubtag.type === ELEMENT_TYPE.ELEMENT && (tdSubtag.name === IMAGE)) {\n this.__processImageNode(tdSubtag, params);\n tdSubtag.doNotReplace = true;\n tdCacheElement.elements.push(tdSubtag);\n }\n });\n\n tdCacheElement.text = allTextNodeTexts.join(' ');\n tdCacheElement.macroList = [];\n\n let macroRegex = this.__macroHandler.getMacroRegex();\n let macroMatches = tdCacheElement.text.match(macroRegex);\n\n _.forEach(macroMatches, match => {\n let macroElement = {\"match\": match, \"result\": this.__macroHandler.parseMacro(match)};\n tdCacheElement.macroList.push(macroElement);\n });\n\n\n } else {\n tdCacheElement.text = \"\";\n }\n\n tdCacheElement.attributes = subtag.attributes;\n tdCacheList.push(tdCacheElement);\n }\n });\n\n return tdCacheList;\n }\n\n __createTableTrForOneListItem(tdCacheList, listItem, trAttributes, isChildItem, eachName, listItemIndex, correlatedListItemIndex) {\n let tdNodes = [];\n let newText = null;\n let newAttributes = null;\n\n // id handling...\n let cellId = null;\n let columnIndex = -1;\n\n _.forEach(tdCacheList, cacheElement => {\n newText = cacheElement.text;\n newAttributes = _.clone(cacheElement.attributes);\n\n if (cacheElement.text.length > 0) {\n _.forEach(cacheElement.macroList, macroElement => {\n let macroResult = macroElement.result;\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.path)) {\n let listItemValue = listItem[macroResult.path];\n\n if(typeof listItemValue !== \"undefined\") {\n if (!_.isNil(macroResult.numberFormat)) {\n if(_.isNil(newAttributes)) {newAttributes = {};}\n listItemValue = this.__resolveNumberBinding(listItemValue, macroResult,newAttributes);\n if(isChildItem){\n // setting the nonRoundedValue to 0.0 makes sure that correlation items are ignored in the sum tag\n newAttributes.nonRoundedValue = 0.0;\n }\n } if(!_.isNil(macroResult.dateTimeFormat)) {\n listItemValue = this.__resolveDateBinding(listItemValue, macroResult);\n } else {\n listItemValue = this.__replaceTogglesIfApplicable(listItemValue, macroResult, this.__localization);\n }\n\n if(_.isString(listItemValue)){\n listItemValue = listItemValue.replace(/\\$/g, \"$$$$\");\n }\n\n newText = newText.replace(macroElement.match, listItemValue);\n }\n }\n });\n }\n\n let newTextNode = [];\n if(newText.length > 0){\n newTextNode.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": newText, \"doNotReplace\": true}));\n }\n\n // only apply id to cells with text OR child elements ...\n if (newTextNode.length > 0 || cacheElement.elements.length > 0) {\n columnIndex += 1;\n cellId = this.__getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex);\n if (!_.isNil(cellId)) {\n if (_.isNil(newAttributes)) { newAttributes = {}; }\n newAttributes.id = cellId;\n }\n }\n\n let newTdNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, newTextNode,\n {\"name\": \"td\", \"attributes\": newAttributes, \"doNotReplace\": true});\n\n _.forEach(cacheElement.elements, element => {\n newTdNode.elements.push(element);\n });\n\n tdNodes.push(newTdNode);\n });\n\n let newTrNode = this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.ELEMENT, tdNodes,\n {\"name\": \"tr\", \"attributes\": trAttributes, \"doNotReplace\": true});\n return newTrNode;\n }\n\n __createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName) {\n\n if (!_.isNil(tableRow) && !_.isNil(tdCacheList) && !_.isNil(trNodes) && !_.isNil(listObject) && !_.isNil(eachName)) {\n\n let hasCorrelationInfo = !_.isNil(correlationInfo);\n let listItemIndex = -1;\n let correlatedListItemIndex = -1;\n\n _.forEach(listObject, listItem => {\n\n if (hasCorrelationInfo) {\n listItemIndex += 1;\n correlatedListItemIndex = -1;\n }\n\n let newTrNode = this.__createTableTrForOneListItem(tdCacheList, listItem, tableRow.attributes, false, eachName, listItemIndex, correlatedListItemIndex);\n trNodes.push(newTrNode);\n\n if (!_.isNil(correlationInfo) && !_.isNil(listItem[correlationInfo.key])) {\n // TODO: this implementation is slow and can be done faster.\n let correlatedItemsForCurrentLI = _.filter(correlationInfo.listObjectForCorrelation, (elem) => {\n return elem[correlationInfo.correlationKey] === listItem[correlationInfo.key];\n });\n\n // (re)set correlatedListItemIndex...\n correlatedListItemIndex = -1;\n\n _.forEach(correlatedItemsForCurrentLI, corrLi => {\n\n // set correlatedListItemIndex...\n correlatedListItemIndex += 1;\n\n let newCorrelationTrNode = this.__createTableTrForOneListItem(correlationInfo.tdCacheForCorrelation, corrLi, {}, true, eachName, listItemIndex, correlatedListItemIndex);\n trNodes.push(newCorrelationTrNode);\n });\n }\n });\n }\n\n return trNodes;\n }\n\n __buildCorrelationInfo(correlationNode, params){\n let correlationInfo = null;\n if(!_.isNil(correlationNode)){\n correlationInfo = {};\n correlationInfo.correlationTableRow = _.find(correlationNode.elements, {name:TR_NODE}); // eachNode.elements[0];\n correlationInfo.tdCacheForCorrelation = this.__createTableDataCache(correlationInfo.correlationTableRow, params);\n correlationInfo.listObjectForCorrelation = this.__resolveScalarBinding(correlationNode,\n correlationNode.attributes.value, params);\n // => get and apply filters\n correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyFilters(correlationNode,\n correlationInfo.listObjectForCorrelation);\n // => get and apply orderCriteria\n correlationInfo.listObjectForCorrelation = this.__evaluateAndApplyOrderCriteria(correlationNode,\n correlationInfo.listObjectForCorrelation);\n correlationInfo.key = correlationNode.attributes.key;\n correlationInfo.correlationKey = correlationNode.attributes.correlationKey;\n }\n return correlationInfo;\n }\n\n __resolveEachTag(eachNode, tableNode, params) {\n let trNodes = [];\n\n if(!_.isNil(eachNode) && (eachNode.name === EACH_NODE)){\n let eachName = null;\n let listObject = null;\n\n if (!_.isNil(eachNode.attributes)) {\n eachName = eachNode.attributes.name;\n listObject = this.__resolveScalarBinding(eachNode, eachNode.attributes.value, params);\n if(!this.__isLo(listObject)){\n listObject = null;\n }\n }\n\n if (_.isNil(listObject)) {\n if(!_.isNil(eachNode.attributes)) {\n trNodes.push(this.__newXMLconvertibleJsonNode(ELEMENT_TYPE.TEXT, null, {\"text\": eachNode.attributes.value,\n \"doNotReplace\": true}));\n }\n }\n\n this.__deleteInvalidNodes(eachNode, null, [TR_NODE, FILTERS_NODE, ORDERCRITERIA_NODE, CORRELATION_NODE]);\n\n if (!_.isNil(eachNode.elements) && (eachNode.elements.length > 0)) {\n\n // => get and apply filters\n listObject = this.__evaluateAndApplyFilters(eachNode, listObject);\n // => get and apply orderCriteria\n listObject = this.__evaluateAndApplyOrderCriteria(eachNode, listObject);\n\n // cache each data ...\n this.__eachCache[eachName] = listObject;\n\n // the correlation node is needed for printing the correlated items\n const correlationNode = _.find(eachNode.elements, {name:CORRELATION_NODE});\n let correlationInfo = this.__buildCorrelationInfo(correlationNode, params);\n if (!_.isNil(correlationInfo)) {\n // each node with correlation => enable dynamicPageBreak for table\n tableNode.attributes.dynamicPageBreak = \"true\"; // as string to keep pdfconverter working ...\n }\n\n // => process table row (with filtered / sorted list object)\n let tableRow = _.find(eachNode.elements, {name:TR_NODE});\n if (!_.isNil(tableRow)) {\n\n this.__deleteInvalidNodes(tableRow, null, [TD_NODE]);\n\n if (!_.isNil(tableRow.elements) && (tableRow.elements.length > 0)) {\n let tdCacheList = this.__createTableDataCache(tableRow, params);\n trNodes = this.__createTableRowData(tableRow, tdCacheList, trNodes, listObject, correlationInfo, eachName);\n }\n } else {\n // no table row => log error\n this.__log.error(\"<each> element must contain a <tr> (table row) element\");\n }\n }\n }\n\n return trNodes;\n }\n\n __evaluateAndApplyFilters(iterableNode, listObject) {\n // <filters>\n // <filter fieldName=\"quantity\" value=\"0\" operator=\"GT\" compareMode=\"NUMBER\" />\n // <filter fieldName=\"movementDirection\" value=\"In\" operator=\"NE\" />\n // </filters>\n\n let filtersNode = _.find(iterableNode.elements, {name:FILTERS_NODE});\n if (!_.isNil(filtersNode)) {\n\n\n this.__deleteInvalidNodes(filtersNode, null, [FILTER_NODE]);\n\n if (!_.isNil(filtersNode.elements) && (filtersNode.elements.length > 0)) {\n _.forEach(filtersNode.elements, element => {\n\n let filterExpression = this.__getAdvancedFilterExpression(element);\n if (!_.isNil(filterExpression) && _.isFunction(filterExpression)) {\n listObject = _.filter(listObject, filterExpression);\n }\n\n });\n } else {\n // no filter elements => log error\n this.__log.error(\"<each>.<filters> element must contain at least one <filter> element\");\n }\n }\n return listObject;\n }\n\n __getAdvancedFilterExpression(filterNode) {\n let filterExpression = null;\n\n // extract attributes\n let fieldName = filterNode.attributes.fieldName;\n let operator = filterNode.attributes.operator;\n let value = filterNode.attributes.value;\n\n let compareMode = filterNode.attributes.compareMode;\n if (_.isNil(compareMode)) {\n compareMode = COMPAREMODE_DEFAULT;\n }\n // normalize compareMode\n compareMode = _.toUpper(compareMode);\n\n if (!_.isNil(fieldName) && !_.isNil(operator) && !_.isNil(value) && !_.isNil(compareMode)) {\n\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // convert value just once\n let y = converter(value);\n\n // normalize operator\n operator = _.toUpper(operator);\n\n let comparator = this.__resolveComparator(operator);\n if (!_.isNil(comparator) && _.isFunction(comparator)) {\n\n // build filter expression\n filterExpression = (object => {\n // convert listobject value\n let x = converter(object[fieldName]);\n // perform comparison\n return comparator(x, y);\n });\n\n } else {\n // unknown operator => log error\n this.__log.error(\"Unsupported operator '\", operator, \"' at <each>.<filters>.<filter> element\");\n }\n\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode, \"' at <each>.<filters>.<filter> element\");\n }\n\n } else {\n // missing mandatory attribute => log error\n this.__log.error(\"Missing mandatory attribute at <each>.<filters>.<filter> element\");\n }\n\n return filterExpression;\n }\n\n __resolveComparator(comparator) {\n const COMPARATORS = {\n \"EQ\" : function (x, y) { return (x === y); },\n \"NE\" : function (x, y) { return (x !== y); },\n \"GT\" : function (x, y) { return (x > y); },\n \"GE\" : function (x, y) { return (x >= y); },\n \"LT\" : function (x, y) { return (x < y); },\n \"LE\" : function (x, y) { return (x <= y); }\n };\n\n return COMPARATORS[comparator];\n }\n\n __resolveConverter(compareMode) {\n const CONVERTERS = {\n //\"NONE\" : function (x) { return x; },\n \"STRING\" : function (x) {\n if (!_.isString(x)) {\n return _.toString(x);\n } else {\n return x;\n }\n },\n \"NUMBER\" : function (x) {\n if (!_.isNumber(x)) {\n return _.toNumber(x);\n } else {\n return x;\n }\n }\n };\n\n return CONVERTERS[compareMode];\n }\n\n __evaluateAndApplyOrderCriteria(iterableNode, listObject) {\n // <orderCriteria>\n // <orderCriterion fieldName=\"eAN\" direction=\"ASC\" compareMode=\"NUMBER\" />\n // </orderCriteria>\n let orderCriteriaNode = _.find(iterableNode.elements, {name:ORDERCRITERIA_NODE});\n if (!_.isNil(orderCriteriaNode)) {\n\n this.__deleteInvalidNodes(orderCriteriaNode, null, [ORDERCRITERION_NODE]);\n\n if (!_.isNil(orderCriteriaNode.elements) && (orderCriteriaNode.elements.length > 0)) {\n\n let selectors = [];\n let directions = [];\n\n _.forEach(orderCriteriaNode.elements, element => {\n\n // extract attributes\n let fieldName = element.attributes.fieldName;\n let direction = element.attributes.direction;\n\n let compareMode = element.attributes.compareMode;\n if (_.isNil(compareMode)) {\n compareMode = COMPAREMODE_DEFAULT;\n }\n // normalize compareMode\n compareMode = _.toUpper(compareMode);\n\n if (!_.isNil(fieldName) && !_.isNil(direction) && !_.isNil(compareMode)) {\n\n // get converter\n let converter = this.__resolveConverter(compareMode);\n if (!_.isNil(converter) && _.isFunction(converter)) {\n\n // build & store selector\n let selector = (object => {\n return converter(object[fieldName]);\n });\n selectors.push(selector);\n\n // store direction\n directions.push(_.toLower(direction));\n\n } else {\n // unknown compareMode => log error\n this.__log.error(\"Unsupported compareMode '\", compareMode , \"' at <each>.<orderCriteria>.<orderCriterion> element\");\n }\n\n } else {\n // missing mandatory attribute => log error\n this.__log.error(\"Missing mandatory attribute at <each>.<orderCriteria>.<orderCriterion> element\");\n }\n });\n\n if (selectors.length > 0 && directions.length > 0 && selectors.length === directions.length) {\n // sort using selectors & directions\n listObject = _.orderBy(listObject, selectors, directions);\n } else {\n // no order criterion fields / directions => log error\n this.__log.error(\"<each>.<orderCriteria> element does not contain any applicable <orderCriterion> elements - sorting skipped\");\n }\n\n } else {\n // no order criterion elements => log error\n this.__log.error(\"<each>.<orderCriteria> element must contain at least one <orderCriterion> element\");\n }\n }\n return listObject;\n }\n\n __replaceEachTagsInTables(tbodyNode, tableNode, params) {\n let eachNode = _.find(tbodyNode.elements, {name:EACH_NODE});\n\n if (!_.isNil(eachNode)){\n let newTableRows = [];\n\n _.forEach(tbodyNode.elements, (rowNode) => {\n if (rowNode.name === EACH_NODE) {\n newTableRows = _.concat(newTableRows, this.__resolveEachTag(rowNode, tableNode, params));\n } else {\n newTableRows.push(rowNode);\n }\n });\n\n tbodyNode.elements = newTableRows;\n }\n }\n\n __isBoDeclared(boName){\n let isDeclared = false;\n\n _.forEach(this.__declarationsNode.elements, declaration =>{\n if(declaration.name === \"DataDeclaration\") {\n if(declaration.attributes.name === boName){\n isDeclared = true;\n }\n }\n });\n\n return isDeclared;\n }\n\n __replaceTogglesIfApplicable(resolvedMacro, macroResult, localization){\n let result = resolvedMacro;\n if (!_.isNil(macroResult.toggleId)) {\n try {\n if (!_.isNil(macroResult.toggleField)) {\n result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro, macroResult.toggleField);\n } else {\n result = this.__toggles.getToggleText(macroResult.toggleId, resolvedMacro);\n }\n } catch(ex){\n if(ex.message.indexOf(\"Invalid toggle value\") > -1){\n\n result = localization.resolve(\"Framework.PrintV2_ToggleValueNotFound\", \"Toggle value not found\");\n result = result + \": \" + resolvedMacro;\n } else if (ex.message.indexOf(\"Invalid toggle field\") > -1) {\n result = localization.resolve(\"Framework.PrintV2_ToggleFieldNotFound\", \"Toggle field not found\");\n result = result + \": \" + macroResult.toggleField;\n } else {\n result = localization.resolve(\"Framework.PrintV2_ToggleNotFound\", \"Toggle not found\");\n result = result + \": \" + macroResult.toggleId;\n }\n\n }\n }\n return result;\n }\n\n __ensureAttributes(currentNode) {\n if(_.isNil(currentNode.attributes)){\n currentNode.attributes = {};\n }\n }\n\n __resolveNumberBinding(resolvedMacro, macroResult, attributeNode){\n if(resolvedMacro !== \"null\" && !_.isNil(resolvedMacro)){\n attributeNode.nonRoundedValue = resolvedMacro;\n return this.__format.formatDecimalV2(resolvedMacro, macroResult.numberFormat,\n this.__decimalSeparator, this.__thousandSeparator);\n } else {\n return \" \";\n }\n\n }\n\n __resolveDateBinding(resolvedMacro, macroResult){\n if(resolvedMacro !== \"null\" && !_.isNil(resolvedMacro)) {\n let actualDateTimeFormat = macroResult.dateTimeFormat;\n switch (actualDateTimeFormat){\n case \"shortDate\": actualDateTimeFormat = this.__localization.shortDateFormat;\n break;\n case \"date\": actualDateTimeFormat = this.__localization.dateFormat;\n break;\n case \"dateTime\": actualDateTimeFormat = this.__localization.dateTimeFormat;\n break;\n case \"time\": actualDateTimeFormat = this.__localization.timeFormat;\n break;\n default: break;\n }\n return this.__format.formatDate(resolvedMacro, actualDateTimeFormat);\n } else {\n return \" \";\n }\n }\n\n __resolveScalarBinding(currentNode, bindingText, params) {\n let bos = params.getBOs();\n let localization = this.__localization;\n let resolvedMacro = null;\n let macroResult = this.__macroHandler.parseMacro(bindingText);\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {\n let scope = macroResult.scope;\n let bindingPath = macroResult.path;\n\n if (scope === \"Globals\") {\n let defaultLabel = macroResult.defaultLabel;\n resolvedMacro = localization.resolve(bindingPath, defaultLabel);\n }\n else if (scope === \"Labels\") {\n let defaultLabel = macroResult.defaultLabel;\n\n if(bindingPath.indexOf(\".\") === -1) {\n // support for shortcut\n let lastUnderscoreIndex = this.__printLayoutName.lastIndexOf(\"_\");\n let printLayoutNameWithoutSpecificVersion = this.__printLayoutName;\n if(lastUnderscoreIndex > -1) {\n printLayoutNameWithoutSpecificVersion =\n this.__printLayoutName.substr(0, lastUnderscoreIndex);\n }\n bindingPath = \"PrintLayouts.\" + printLayoutNameWithoutSpecificVersion + \".\" + bindingPath;\n }\n\n resolvedMacro = localization.resolve(bindingPath, defaultLabel);\n } else if (scope === \"Declarations\") {\n let boEndIndex = bindingPath.indexOf(\".\");\n let boName;\n\n if(boEndIndex === -1){\n // this means it is a top level binding like Declarations::data\n boName = bindingPath;\n boEndIndex = bindingPath.length;\n }else {\n boName = bindingPath.substring(0, boEndIndex);\n }\n\n let isBoDeclared = this.__isBoDeclared(boName);\n\n if(isBoDeclared){\n let deeperBindingPath = bindingPath.substring(boEndIndex + 1, bindingPath.length);\n let actualBo = _.get(bos, boName);\n\n if(deeperBindingPath.length > 0){\n resolvedMacro = _.get(actualBo, deeperBindingPath);\n } else {\n // this means it is a top level binding like Declarations::data\n resolvedMacro = actualBo;\n }\n\n if (!_.isNil(macroResult.numberFormat)) {\n this.__ensureAttributes(currentNode);\n resolvedMacro = this.__resolveNumberBinding(resolvedMacro, macroResult,currentNode.attributes);\n } if(!_.isNil(macroResult.dateTimeFormat)) {\n resolvedMacro = this.__resolveDateBinding(resolvedMacro, macroResult);\n } else {\n resolvedMacro = this.__replaceTogglesIfApplicable(resolvedMacro, macroResult, localization);\n }\n\n\n } else {\n this.__log.error(\"Usage of undeclared BO:\", boName);\n }\n }\n }\n\n return resolvedMacro;\n }\n\n __replaceMacrosInTextNodes(parentJsonElement, currentJsonElement, params){\n let xmlNodeText = currentJsonElement.text;\n let macroRegex = this.__macroHandler.getMacroRegex();\n let arrayMatches = xmlNodeText.match(macroRegex);\n\n if(!_.isNil(arrayMatches) && arrayMatches.length > 0) {\n _.forEach(arrayMatches, currentMatch => {\n let resolvedMacro = this.__resolveScalarBinding(parentJsonElement, currentMatch, params);\n if (!_.isNil(resolvedMacro)){\n if(_.isString(resolvedMacro)){\n resolvedMacro = resolvedMacro.replace(/\\$/g, \"$$$$\");\n }\n xmlNodeText = xmlNodeText.replace(currentMatch, resolvedMacro);\n currentJsonElement.text = xmlNodeText;\n }\n });\n }\n }\n\n __isImageDeclared(imageName){\n let isDeclared = false;\n\n _.forEach(this.__declarationsNode.elements, declaration => {\n if(declaration.name === \"DataDeclaration\") {\n if ((declaration.attributes.name === imageName) && (declaration.attributes.type === \"Image\" || declaration.attributes.type === \"Signature\")) {\n isDeclared = true;\n }\n }\n });\n\n return isDeclared;\n }\n\n __sanitizeImageAttributes(currentJsonElement/*, params*/) {\n let width = currentJsonElement.attributes.width;\n let height = currentJsonElement.attributes.height;\n let fit = currentJsonElement.attributes.fit;\n this.__integerStringRegExp.lastIndex = 0; // reset last index\n if(!this.__integerStringRegExp.test(height)){\n delete currentJsonElement.attributes.height;\n }\n this.__integerStringRegExp.lastIndex = 0; // reset last index\n if(!this.__integerStringRegExp.test(width)){\n delete currentJsonElement.attributes.width;\n }\n\n let fitRegExp = /^\\d+,\\d+$/g;\n if(!fitRegExp.test(fit)){\n delete currentJsonElement.attributes.fit;\n }\n if(!_.isNil(width) || !_.isNil(height)){\n delete currentJsonElement.attributes.fit;\n }\n }\n\n __processImageNode(currentJsonElement, params){\n this.__replaceMacrosInImageNodes(currentJsonElement, params);\n this.__sanitizeImageAttributes(currentJsonElement, params);\n }\n\n __replaceMacrosInImageNodes(currentJsonElement, params){\n let images = params.getImages();\n let bindingText = currentJsonElement.attributes.src;\n let macroResult = this.__macroHandler.parseMacro(bindingText);\n\n if (!_.isNil(macroResult) && !_.isNil(macroResult.scope) && !_.isNil(macroResult.path)) {\n let scope = macroResult.scope;\n let imageName = macroResult.path;\n if (scope === \"Declarations\") {\n if (this.__isImageDeclared(imageName)) {\n let imageObject = images[imageName];\n if(_.isNil(imageObject.src) && imageObject.type === \"Signature\") {\n currentJsonElement.name = \"p\";\n } else if (!_.isNil(imageObject) && !_.isNil(imageObject.src)\n && !_.isNil(imageObject.mimeType) && (!_.isNil(imageObject.imageId) || imageObject.type === \"Signature\")) {\n let dataUrlStart = \"data:\" + imageObject.mimeType +\";base64,\";\n let dataUrl = imageObject.src;\n\n if (!_.startsWith(dataUrl, dataUrlStart)) {\n dataUrl = dataUrlStart + dataUrl;\n }\n\n // => use imageName as id\n currentJsonElement.attributes.id = imageName;\n currentJsonElement.attributes.mimeType = imageObject.mimeType;\n\n currentJsonElement.attributes.src = dataUrl;\n if (imageObject.type === \"Signature\") {\n // https://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript\n const m = encodeURIComponent(dataUrl).match(/%[89ABab]/g);\n const sizeInBytes = dataUrl.length + (m ? m.length : 0);\n if (sizeInBytes > SIGNATURE_SIZE_LIMIT) {\n this.__log.error(\"Usage of signature larger than 200kb:\", imageName);\n this.__assignFallbackImage(currentJsonElement); \n }\n }\n } else {\n this.__log.error(\"Usage of undefined image:\", imageName);\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Usage of undeclared image:\", imageName);\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Invalid binding value:'\"+ bindingText + \"' found in image.\");\n this.__assignFallbackImage(currentJsonElement);\n }\n } else {\n this.__log.error(\"Invalid value:'\"+ bindingText + \"' found in image.\");\n this.__assignFallbackImage(currentJsonElement);\n }\n }\n\n __assignFallbackImage(currentJsonElement) {\n const errorFallbackImage = \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMi4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAzOCAzOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzggMzg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNGM0Y2Rjg7fQ0KCS5zdDF7ZmlsbDojQjRDN0Q0O30NCjwvc3R5bGU+DQo8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxOSIgY3k9IjE5IiByPSIxOSIvPg0KPGNpcmNsZSBjbGFzcz0ic3QxIiBjeD0iMjkuMSIgY3k9IjExLjQiIHI9IjIuMyIvPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0yMC43LDIyLjZsLTQtNC44bDAuNS0xLjdsLTIuMy00Yy0wLjEtMC4yLTAuMy0wLjItMC40LDBMNS4yLDI2LjljLTAuMSwwLjIsMCwwLjQsMC4yLDAuNGgxNUwxNywyMi41DQoJCQlMMjAuNywyMi42eiBNMzIuOCwyN2wtNy43LTljLTAuMS0wLjItMC40LTAuMy0wLjYtMC4xbC0yLjgsMmwxLjksNS41TDIwLDI0LjVsMS4yLDIuOGgxMS40QzMyLjksMjcuMywzMi45LDI3LjIsMzIuOCwyN3oiLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==\";\n if (!_.isNil(currentJsonElement) && !_.isUndefined(currentJsonElement.attributes)) {\n currentJsonElement.attributes.id = \"$$$fallbackImage$$$\";\n currentJsonElement.attributes.mimeType = \"image/svg+xml\";\n currentJsonElement.attributes.src = errorFallbackImage;\n } \n }\n\n __getCellId(eachName, listItemIndex, correlatedListItemIndex, columnIndex){\n let tcid = TableCellIdentifier.create(eachName, listItemIndex, correlatedListItemIndex, columnIndex);\n return tcid.toString();\n }\n\n}\n\nmodule.exports = ContractToIntermediateJSON;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/contractToIntermediateJSONClass.js?");
406
406
 
407
407
  /***/ }),
408
408
 
@@ -468,7 +468,7 @@ eval("/*\n * FILE_HEADER\n */\n\n\nconst LogClass = __webpack_require__(/*! ./lo
468
468
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
469
469
 
470
470
  "use strict";
471
- eval("/*\n * FILE_HEADER\n */\n\n\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst PdfPrinter = __webpack_require__(/*! pdfmake */ \"./node_modules/pdfmake/build/pdfmake.js\");\nconst Format = __webpack_require__(/*! ./formatClass */ \"./src/formatClass.js\");\nconst TableCellIdentifier = __webpack_require__(/*! ./tableCellIdentifier */ \"./src/tableCellIdentifier.js\");\n\n// required for webpack include (browser impl), used in conjunction with BrowserFonts definition\nconst BrowserFontsVfs = __webpack_require__(/*! pdfmake/build/vfs_fonts.js */ \"./node_modules/pdfmake/build/vfs_fonts.js\");\n\n// => use 'Roboto' font (pdfMake default) due to licensing (Apache 2.0)\n// https://fonts.google.com/specimen/Roboto || https://en.wikipedia.org/wiki/Roboto\nconst ServerFonts = {\n Roboto: {\n normal: './fonts/Roboto-Regular.ttf',\n bold: './fonts/Roboto-Medium.ttf',\n italics: './fonts/Roboto-Italic.ttf',\n bolditalics: './fonts/Roboto-MediumItalic.ttf'\n }\n};\n\nconst BrowserFonts = {\n Roboto: {\n normal: 'Roboto-Regular.ttf',\n bold: 'Roboto-Medium.ttf',\n italics: 'Roboto-Italic.ttf',\n bolditalics: 'Roboto-MediumItalic.ttf'\n }\n};\n\nconst TableLayouts = {\n lightHorizontalLinesMainItemsOnly: {\n hLineWidth: function (i, node) {\n // no frame for the table\n if (i === 0 || i === node.table.body.length) {\n return 0;\n }\n // bold seperator between header and body\n if (i === node.table.headerRows) {\n return 2;\n }\n // get first cell id for row ...\n let row = node.table.body[i];\n if (!_.isNil(row)) {\n let cellId = null;\n _.forEach(row, cell =>{\n if(_.has(cell, 'id')) {\n cellId = cell.id;\n return false;\n }\n });\n // seperator for between main rows (+ their child rows)\n let tcid = TableCellIdentifier.fromString(cellId);\n if (!_.isNil(tcid)) {\n if (tcid.isKnown() && tcid.belongsToMainRow()) {\n // seperator between main rows\n return 1;\n } else if (!tcid.isKnown()) {\n // seperator for 'unknown row'\n return 1;\n }\n }\n }\n // no seperator for all other cases, e.g. child rows\n return 0;\n },\n vLineWidth: function () {\n return 0;\n },\n hLineColor: function (i, node) {\n return i === node.table.headerRows ? 'black' : 'gray';\n }\n }\n};\n\nconst STYLE_HEADING1 = \"heading1\";\nconst STYLE_HEADING2 = \"heading2\";\nconst STYLE_PARAGRAPH = \"paragraph\";\n\nconst EXPORT_TYPE = {\n \"BASE_64\" : \"BASE_64\",\n \"DATA_URL\" : \"DATA_URL\",\n \"BLOB\" : \"BLOB\"\n};\nObject.freeze(EXPORT_TYPE);\n\nclass PdfConverter {\n constructor() {\n this.__pdfMake = null;\n if(typeof pdfMake !== \"undefined\")\n {\n this.__pdfMake = pdfMake;\n }\n\n this.__format = new Format();\n }\n\n __applyPageBreakData(pdfDefinition) {\n if (!_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak && \n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) && pdfDefinition.dynamicPageBreakData.length > 0) {\n // let table = pdfDefinition.content[0].table;\n let dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n \n // handle tables in body\n let tables = _.filter(pdfDefinition.content, {style: 'tableBody'});\n this.__applyPageBreakDataToTables(tables, dynamicPageBreakData);\n }\n } \n\n __applyPageBreakDataToTables(tables, dynamicPageBreakData) {\n\n if (!_.isNil(tables) && _.isArray(tables) && tables.length > 0 && !_.isNil(dynamicPageBreakData)) {\n for (let i = 0; i < tables.length; i++) {\n // get table\n let table = tables[i].table;\n if (!_.isNil(table) && !_.isUndefined(table.dynamicPageBreak) && table.dynamicPageBreak) {\n // loop rows\n let row = null;\n let cell = null;\n let breakRow = false;\n for (let j = 0; j < table.body.length; j++) { \n row = table.body[j];\n breakRow = false;\n // loop columns\n for (let k = 0; k < row.length; k++) {\n cell = row[k];\n // apply manual pageBreaks instead of dynamic ones\n if (!breakRow && _.has(cell, 'id')) {\n breakRow = _.includes(dynamicPageBreakData, cell.id);\n if (breakRow) {\n cell.pageBreak = 'before';\n } \n } else if (breakRow && _.has(cell, 'id')) {\n cell.pageBreak = 'before';\n }\n }\n }\n }\n }\n }\n\n }\n \n __initializePdfDefinition() {\n this.__definition = {\n \"content\": [],\n pageSize: 'A4',\n pageMargins: [ 40, 60, 40, 60 ],\n defaultStyle: {\n font: 'Roboto', //'Helvetica'\n },\n styles: {\n tableBody: {\n fontSize: 10,\n margin: [0, 5, 0, 15]\n },\n tableHeader: {\n fontSize: 10,\n bold: true\n },\n heading1: {\n fontSize: 18,\n bold: true,\n margin: [0, 0, 0, 10]\n },\n heading2: {\n fontSize: 14,\n bold: true,\n margin: [0, 10, 0, 5]\n },\n paragraph: {\n fontSize: 10,\n bold: false,\n italics: false\n }\n }\n };\n this.__automaticPageBreak = false;\n }\n\n __getThOrTdContent(thTdNode, settings) {\n let result = {};\n let text = \"\";\n\n if(!_.isNil(thTdNode.elements)){\n _.forEach(thTdNode.elements, childElement => {\n if(childElement.type === \"text\") {\n text = text + childElement.text;\n } else if (childElement.type === \"element\" && childElement.name === \"pageNumber\" && !_.isNil(settings.currentPage)) {\n text = text + settings.currentPage;\n } else if (childElement.type === \"element\" && childElement.name === \"img\") {\n result.image = this.__getImageDefinition(childElement, false);\n }\n });\n }\n result.text = text;\n\n return result;\n }\n\n __getHeadingDefinition(headingNode, headingStyle) {\n return this.__createHeadingOrParagraphText(headingNode, headingNode.elements[0].text, headingStyle);\n }\n\n __getParagraphDefinition(paragraphNode, paragraphStyle) {\n return this.__createHeadingOrParagraphText(paragraphNode, paragraphNode.elements[0].text, paragraphStyle);\n }\n\n __getImageDefinition(imageNode, useAlignment) {\n let img = {};\n\n if (!_.isNil(imageNode)){\n let atr = imageNode.attributes;\n let imageId = atr.id;\n let mimeType = atr.mimeType;\n\n if (!_.isNil(imageId) && this.__isImageCached(imageId)) {\n // => png / jpeg image already in cache => simply (re)use it ...\n img = {image: imageId};\n\n } else {\n\n if (mimeType === \"image/svg+xml\") {\n // => svg image handling (vector graphics)\n let src = atr.src.split(\",\");\n let data = src[1];\n let txt;\n\n if ((typeof window === 'object') && (typeof window.atob === 'function')) {\n // we do expect an utf8 string\n txt = decodeURIComponent(encodeURIComponent(window.atob(data)));\n } else {\n let buf = Buffer.from(data, 'base64');\n txt = buf.toString('utf8');\n }\n img = {svg: txt};\n\n } else {\n // => png / jpeg image handling (raster graphics)\n // add image to cache\n this.__addImageToCache(imageId, atr.src);\n img = {image: imageId}; \n\n }\n } \n\n if (!_.isNil(atr.width)) {\n img.width = parseInt(atr.width, 10);\n }\n if (!_.isNil(atr.height)) {\n img.height = parseInt(atr.height, 10);\n }\n if (!_.isNil(atr.fit)) {\n let fit = atr.fit.split(',');\n let w = parseInt(fit[0], 10);\n let h = parseInt(fit[1], 10);\n img.fit = [w,h];\n }\n\n if (!_.isNil(atr.alignment) && (true === useAlignment)) {\n let alignmentAttribute = this.__checkEnumAttribute(imageNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n\n if (!_.isNil(alignmentAttribute)) {\n img.alignment = alignmentAttribute;\n }\n }\n }\n\n return img;\n }\n\n __addImageToCache(imageId, imageData) {\n // init image cache if required\n if (!_.has(this.__definition, 'images')) { \n _.assign(this.__definition, {\n images: {}\n });\n }\n\n // add image to cache\n if (!_.has(this.__definition.images, imageId)) {\n this.__definition.images[imageId] = imageData;\n }\n }\n\n __isImageCached(imageId) {\n return (_.has(this.__definition, 'images') && _.has(this.__definition.images, imageId));\n }\n\n __checkSpansAndColumns(pdfTableDefinition) {\n let rows = 0;\n let columns = 0;\n\n let rowCount = pdfTableDefinition.table.body.length;\n let columnCount = pdfTableDefinition.table.widths.length;\n\n for (rows=0; rows<rowCount; rows++) {\n let row = pdfTableDefinition.table.body[rows];\n\n if (row.length > columnCount) {\n row.length = columnCount;\n } else if (row.length < columnCount) {\n do {\n row.push(\"\");\n } while(row.length < columnCount);\n }\n\n let maxRowSpan = rowCount-rows;\n\n for (columns=0; columns<columnCount; columns++) {\n let column = row[columns];\n let maxColSpan = columnCount-columns;\n\n if (!_.isString(column)) {\n if (!_.isNil(column[\"colSpan\"])) {\n if (column.colSpan > maxColSpan) {\n column.colSpan = maxColSpan;\n }\n if (column.colSpan < 2) {\n delete column.colSpan;\n }\n if ((Object.keys(column).length === 1) && !_.isNil(column[\"text\"])) {\n row[columns] = column.text;\n }\n }\n if (!_.isNil(column[\"rowSpan\"])) {\n if (column.rowSpan > maxRowSpan) {\n column.rowSpan = maxRowSpan;\n }\n if (column.rowSpan < 2) {\n delete column.rowSpan;\n }\n if ((Object.keys(column).length === 1) && !_.isNil(column[\"text\"])) {\n row[columns] = column.text;\n }\n }\n }\n }\n }\n }\n\n __checkIntegerAttribute(element, attributeName, allowedStrings, onlyPositiveNumbers, defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n let bAllowedStrings = !_.isNil(allowedStrings) && _.isArray(allowedStrings) && (allowedStrings.length > 0);\n let bPositiveNumbers = !_.isNil(onlyPositiveNumbers) && _.isBoolean(onlyPositiveNumbers) && (onlyPositiveNumbers === true);\n\n if (bAttribute) {\n let attribute = element.attributes[attributeName];\n let i = 0;\n\n if (bAllowedStrings && _.isString(attribute)){\n for (i=0; i<allowedStrings.length; i++) {\n if (attribute === allowedStrings[i]) {\n return attribute;\n }\n }\n }\n\n let parsed = parseInt(attribute, 10);\n\n if(!isNaN(parsed)) {\n if (bPositiveNumbers && (parsed < 0))\n {\n return defaultValue;\n } else {\n return parsed;\n }\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkBooleanAttribute(element, attributeName,defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if (attributeValue === \"true\") {\n return true;\n } else if (attributeValue === \"false\") {\n return false;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkEnumAttribute(element, attributeName,allowedEnumValues ,defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if(allowedEnumValues.includes(attributeValue)){\n return attributeValue;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkStringAttribute(element, attributeName, defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if(_.isString(attributeValue)){\n return attributeValue;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __applyNumberFormatAttribute(element, value) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && !_.isNil(element.attributes.numberFormat) && !_.isNil(element.attributes.decimalSeparator) && !_.isNil(element.attributes.thousandSeparator);\n\n if (bAttribute) {\n return this.__format.formatDecimalV2(value, element.attributes.numberFormat, element.attributes.decimalSeparator, element.attributes.thousandSeparator);\n } else {\n return value;\n }\n }\n\n __createHeadingOrParagraphText(currentNode, text, style) {\n let result = { \"text\": text };\n\n let boldAttribute = this.__checkBooleanAttribute(currentNode, 'bold', null);\n let italicsAttribute = this.__checkBooleanAttribute(currentNode, 'italics', null);\n let alignmentAttribute = this.__checkEnumAttribute(currentNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n\n if (_.isNil(boldAttribute) && _.isNil(italicsAttribute) && _.isNil(alignmentAttribute)) {\n result.style = style;\n } else {\n let styleJson = this.__definition.styles[style];\n\n if (!_.isNil(styleJson.fontSize)) {\n result.fontSize = styleJson.fontSize;\n }\n\n if (!_.isNil(styleJson.bold)) {\n result.bold = styleJson.bold;\n }\n\n if (!_.isNil(styleJson.italics)) {\n result.italics = styleJson.italics;\n }\n\n if (!_.isNil(styleJson.margin)) {\n result.margin = styleJson.margin;\n }\n\n if (!_.isNil(boldAttribute)) {\n result.bold = boldAttribute;\n }\n\n if (!_.isNil(italicsAttribute)) {\n result.italics = italicsAttribute;\n }\n\n if (!_.isNil(alignmentAttribute)) {\n result.alignment = alignmentAttribute;\n }\n }\n\n return result;\n }\n\n __createTableColumn(content, style, colSpan, rowSpan, alignment, bold, italics, cellId) {\n let bTextOnly = _.isNil(content.image) && _.isNil(style) && _.isNil(colSpan) && _.isNil(rowSpan)\n && _.isNil(alignment) && _.isNil(bold) && _.isNil(italics) && _.isNil(cellId);\n\n if (bTextOnly) {\n return content.text;\n }\n\n let column = {};\n\n //Image wins over text if both are defined.\n if (!_.isNil(content.image)) {\n column = content.image;\n } else {\n column.text = content.text;\n }\n\n if (!_.isNil(style)) {\n column.style = style;\n }\n if (!_.isNil(colSpan)) {\n column.colSpan = colSpan;\n }\n if (!_.isNil(rowSpan)) {\n column.rowSpan = rowSpan;\n }\n if (!_.isNil(alignment)) {\n column.alignment = alignment;\n }\n if (!_.isNil(bold)) {\n column.bold = bold;\n }\n if (!_.isNil(italics)) {\n column.italics = italics;\n }\n if (!_.isNil(cellId)) {\n column.id = cellId;\n }\n\n return column;\n }\n\n __getTableCellAttributes(thOrTdNode){\n let colSpanAttributeValue = this.__checkIntegerAttribute(thOrTdNode, 'colSpan', [], true, null);\n let rowSpanAttributeValue = this.__checkIntegerAttribute(thOrTdNode, 'rowSpan', [], true, null);\n let boldAttribute = this.__checkBooleanAttribute(thOrTdNode, 'bold', null);\n let italicsAttribute = this.__checkBooleanAttribute(thOrTdNode, 'italics', null);\n let alignmentAttribute = this.__checkEnumAttribute(thOrTdNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n let cellId = this.__checkStringAttribute(thOrTdNode, 'id', null);\n\n return [colSpanAttributeValue, rowSpanAttributeValue, alignmentAttribute, boldAttribute, italicsAttribute, cellId];\n }\n\n __evaluateTableDefinitionRowBreak(tableNode, dynamicPageBreak) {\n let dontBreakRows = this.__checkBooleanAttribute(tableNode, \"dontBreakRows\", true);\n // deactivate automatic row breaks for tables requiring dynamic page breaks\n if (dynamicPageBreak) {\n dontBreakRows = false;\n }\n return dontBreakRows;\n }\n\n __evaluateTableDefinitionDynamicPageBreak(tableNode) {\n let automaticPageBreak = this.__automaticPageBreak; // => coming from PrintV2 action configuration\n let dynamicPageBreak = (automaticPageBreak && this.__checkBooleanAttribute(tableNode, \"dynamicPageBreak\", false));\n if (dynamicPageBreak) {\n this.__configurePdfDefinitionForDynamicPageBreak();\n }\n return dynamicPageBreak;\n }\n\n __configurePdfDefinitionForDynamicPageBreak() {\n if (!_.has(this.__definition, 'pageBreakBefore')) {\n _.assign(this.__definition, {\n pageBreakBefore: this.__calculatePageBreakBefore.bind(this)\n });\n this.__definition.dynamicPageBreak = true;\n }\n }\n\n __getTableDefinition(tableNode, settings) {\n if(_.isNil(settings)) {settings = {};}\n\n let dynamicPageBreak = this.__evaluateTableDefinitionDynamicPageBreak(tableNode);\n let dontBreakRows = this.__evaluateTableDefinitionRowBreak(tableNode, dynamicPageBreak);\n let pdfTableDefinition = {\n style: \"tableBody\",\n table: {\n dontBreakRows: dontBreakRows,\n headerRows: 0,\n body: [],\n widths: []\n }\n };\n if (dynamicPageBreak) {\n pdfTableDefinition.table.dynamicPageBreak = true;\n }\n\n if(!_.isNil(settings.layout)) {\n pdfTableDefinition.layout = settings.layout;\n }\n if(!_.isNil(tableNode.attributes) && !_.isNil(tableNode.attributes.tableLayout)){\n pdfTableDefinition.layout = tableNode.attributes.tableLayout;\n }\n\n let bNoWidths = false;\n let theadNode = _.find(tableNode.elements, {name: 'thead'});\n let tbodyNode = _.find(tableNode.elements, {name: 'tbody'});\n\n if(!_.isNil(theadNode)) {\n _.forEach(theadNode.elements, headerTrNode=> {\n let row = [];\n _.forEach(headerTrNode.elements, value => {\n let attributeValues = this.__getTableCellAttributes(value);\n\n if (pdfTableDefinition.table.headerRows === 0) {\n let widthAttributeValue = this.__checkIntegerAttribute(value, 'width', ['*', 'auto'], true, 'auto');\n pdfTableDefinition.table.widths.push(widthAttributeValue);\n }\n let content = this.__getThOrTdContent(value, settings);\n row.push(this.__createTableColumn(content, 'tableHeader', ...attributeValues));\n });\n\n pdfTableDefinition.table.body.push(row);\n pdfTableDefinition.table.headerRows += 1;\n });\n } else if(!_.isNil(tbodyNode)) {\n bNoWidths = true;\n } else {\n pdfTableDefinition.table.widths = ['auto'];\n pdfTableDefinition.table.body = [['']];\n\n return pdfTableDefinition;\n }\n\n if(!_.isNil(tbodyNode)) {\n _.forEach(tbodyNode.elements, tbodyTrNode=> {\n let row = [];\n _.forEach(tbodyTrNode.elements, value => {\n let attributeValues = this.__getTableCellAttributes(value);\n\n if ((bNoWidths === true)) {\n let widthAttributeValue = this.__checkIntegerAttribute(value, 'width', ['*', 'auto'], true, 'auto');\n pdfTableDefinition.table.widths.push(widthAttributeValue);\n }\n let content = this.__getThOrTdContent(value, settings);\n content.text = this.__applyNumberFormatAttribute(value, content.text);\n\n row.push(this.__createTableColumn(content, null, ...attributeValues));\n });\n\n bNoWidths = false;\n pdfTableDefinition.table.body.push(row);\n });\n }\n\n this.__checkSpansAndColumns(pdfTableDefinition);\n \n return pdfTableDefinition;\n }\n\n __addBody(definition, json) {\n\n _.forEach(json.elements, contractElement => {\n if(contractElement.name === 'table') {\n let tableDefintion = this.__getTableDefinition(contractElement);\n definition.content.push(tableDefintion);\n } else if (contractElement.name === 'h1') {\n let h1Defintion = this.__getHeadingDefinition(contractElement, STYLE_HEADING1);\n definition.content.push(h1Defintion);\n } else if (contractElement.name === 'h2') {\n let h2Defintion = this.__getHeadingDefinition(contractElement, STYLE_HEADING2);\n definition.content.push(h2Defintion);\n } else if (contractElement.name === 'p') {\n let paragraphDefintion = this.__getParagraphDefinition(contractElement, STYLE_PARAGRAPH);\n definition.content.push(paragraphDefintion);\n } else if (contractElement.name === 'img' || contractElement.name === 'signature') {\n let imageDefintion = this.__getImageDefinition(contractElement, true);\n definition.content.push(imageDefintion);\n }\n });\n\n }\n\n __addHeader(definition, json) {\n let headerNode = _.find(json.elements, {name:\"header\"});\n\n if(!_.isNil(headerNode)) {\n let imageNode = _.find(headerNode.elements, {name:\"img\"});\n let tableNode = _.find(headerNode.elements, {name:\"table\"});\n\n //The coverage report only works correctly when isWriteDataEnabled() returns true in the Unit Tests.\n definition.header = (currentPage/*, pageCount, pageSize*/) => {\n let imageDefinition = (!_.isNil(imageNode)) ? this.__getImageDefinition(imageNode, true) : null;\n let tableDefinition = (!_.isNil(tableNode)) ? this.__getTableDefinition(tableNode, {\"layout\": \"noBorders\", \"currentPage\": currentPage}) : null;\n\n if (!_.isNil(imageDefinition) && !_.isNil(tableDefinition)) {\n return [imageDefinition, tableDefinition];\n } else if (!_.isNil(tableDefinition)) {\n return tableDefinition;\n } else if (!_.isNil(imageDefinition)) {\n return imageDefinition;\n } else {\n return null;\n }\n };\n }\n }\n\n __addFooter(definition, json) {\n let footerNode = _.find(json.elements, {name:\"footer\"});\n if(!_.isNil(footerNode)) {\n let tableNode = _.find(footerNode.elements, {name:\"table\"});\n let bTableNode = !_.isNil(tableNode);\n\n //The coverage report only works correctly when isWriteDataEnabled() returns true in the Unit Tests.\n definition.footer = (currentPage/*, pageCount, pageSize*/) => {\n let tableDefinition = bTableNode ? this.__getTableDefinition(tableNode, {\"layout\": \"noBorders\", \"currentPage\":currentPage}) : null;\n return tableDefinition;\n };\n }\n }\n\n __addDocumentMetadata(definition, documentProperties){\n definition.info = {};\n definition.info.creator = \"Consumer Goods Cloud\";\n definition.info.producer = \"Consumer Goods Cloud\";\n _.forEach(documentProperties, (value, key) => {\n definition.info[key] = value;\n });\n }\n\n __configureAutomaticPageBreak(reportLayout) {\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.automaticPageBreak)){\n this.__automaticPageBreak = reportLayout.attributes.automaticPageBreak;\n }\n }\n\n __addPageDimensionsOrientationMarginsWatermark(definition, reportLayout){\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.pageSize)){\n definition.pageSize = reportLayout.attributes.pageSize;\n }\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.watermark)){\n definition.watermark = {text: reportLayout.attributes.watermark, opacity:0.1, fontSize: 120};\n }\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.pageMargins)){\n definition.pageMargins = JSON.parse(reportLayout.attributes.pageMargins);\n }\n }\n\n __isLastMainRowOnPage(followingNodesOnPage) {\n let result = false;\n\n if (!_.isNil(followingNodesOnPage) && _.isArray(followingNodesOnPage)) {\n result = (followingNodesOnPage.length === 0) || !(_.some(followingNodesOnPage, y => {\n let tcid = TableCellIdentifier.fromString(y.id);\n return (tcid.isKnown() && tcid.isFirstMainRowCell());\n }));\n }\n\n return result;\n }\n\n __isFirstNodeOnNextPageFirstMainRowCell(nodesOnNextPage) {\n let result = false;\n\n if (!_.isNil(nodesOnNextPage) && _.isArray(nodesOnNextPage) && nodesOnNextPage.length > 0) {\n let firstNodeOnNextPage = _.first(nodesOnNextPage);\n let tcid = TableCellIdentifier.fromString(firstNodeOnNextPage.id);\n result = (tcid.isKnown() && tcid.isFirstMainRowCell());\n }\n\n return result;\n }\n\n __calculatePageBreakForParentChildRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode) {\n let doPageBreak = false;\n\n // dynamic pageBreak logic for parent-child rows\n if (!_.isNil(previousNodesOnPage) && _.isArray(previousNodesOnPage) && previousNodesOnPage.length > 0) {\n let previousNode = _.last(previousNodesOnPage);\n let tcidPreviousNode = TableCellIdentifier.fromString(previousNode.id);\n\n // => check if we are the first cell of a main row ...\n if (tcidCurrentNode.isFirstMainRowCell()) {\n\n if (!_.isNil(followingNodesOnPage) && _.isArray(followingNodesOnPage)) {\n\n let isCurrentNodeLastHeadOnPageX = this.__isLastMainRowOnPage(followingNodesOnPage);\n let isFirstNodeOnNextPageFirstMainRowCellX = this.__isFirstNodeOnNextPageFirstMainRowCell(nodesOnNextPage);\n let isPageBreakRelevant = (tcidPreviousNode.isKnown() && (tcidPreviousNode.isFirstMainRowCell() || tcidPreviousNode.isSubsequentMainRowCell() || tcidPreviousNode.isFirstCorrelatedRowCell() || tcidPreviousNode.isSubsequentCorrelatedRowCell()));\n\n doPageBreak = (isCurrentNodeLastHeadOnPageX && !isFirstNodeOnNextPageFirstMainRowCellX && isPageBreakRelevant);\n }\n\n } else if (tcidCurrentNode.isSubsequentMainRowCell()) {\n\n doPageBreak = (!tcidPreviousNode.isFirstMainRowCell() && !tcidPreviousNode.isSubsequentMainRowCell() || !(tcidCurrentNode.sharesMainRow(tcidPreviousNode)));\n }\n }\n\n return doPageBreak;\n }\n\n\n __calculatePageBreakForBrokenRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode) {\n let doPageBreak = false;\n\n // => check if we are the first cell of a row ...\n if (tcidCurrentNode.isFirstMainRowCell() || tcidCurrentNode.isFirstCorrelatedRowCell()) {\n // check for 'broken' row ...\n // => check if i am the last row\n let isLastRowOnPageX = (_.every(followingNodesOnPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n return !tcidNode.isKnown() || (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode));\n }));\n\n if (isLastRowOnPageX) {\n // check if any cell of the current row extends over more than one page => pageBreak!\n // => check current cell (first cell of row)\n doPageBreak = (!_.isNil(currentNode.pageNumbers) && _.isArray(currentNode.pageNumbers) && currentNode.pageNumbers.length > 1);\n if (!doPageBreak) {\n // => check following cells on current page and cells on next page for page overlaps (other cells of row)\n doPageBreak = (_.some(followingNodesOnPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n if (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode)) {\n return !_.isEqual(currentNode.pageNumbers, node.pageNumbers);\n }\n return false;\n }) || (_.some(nodesOnNextPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n if (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode)) {\n return !_.isEqual(currentNode.pageNumbers, node.pageNumbers);\n }\n return false;\n })));\n }\n }\n } else if (tcidCurrentNode.isSubsequentMainRowCell() || tcidCurrentNode.isSubsequentCorrelatedRowCell()) {\n // => check other row cells ...\n if (!_.isNil(previousNodesOnPage) && _.isArray(previousNodesOnPage) && previousNodesOnPage.length > 0) {\n // check if last previous node is NOT precursor cell of current one => pageBreak!\n let previousNode = _.last(previousNodesOnPage);\n let tcidPreviousNode = TableCellIdentifier.fromString(previousNode.id);\n doPageBreak = !tcidCurrentNode.isPreviousCell(tcidPreviousNode);\n }\n }\n\n return doPageBreak;\n }\n\n __calculatePageBreakBefore(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage) {\n let doPageBreak = false;\n \n // looking for potential pageBreaks only makes sense if there are elements to be placed there (=> might require adaption for subtotals)\n if (!_.isNil(nodesOnNextPage) && _.isArray(nodesOnNextPage) && nodesOnNextPage.length > 0) {\n\n // get table cell identifier\n let tcidCurrentNode = TableCellIdentifier.fromString(currentNode.id);\n if (!_.isNil(tcidCurrentNode) && tcidCurrentNode.isKnown()) {\n\n // check for dynamic pageBreak of parent-child rows\n doPageBreak = this.__calculatePageBreakForParentChildRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode); \n if (!doPageBreak) { \n // check for dynamic pageBreak of 'broken' row\n doPageBreak = this.__calculatePageBreakForBrokenRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode); \n }\n \n if (doPageBreak && !_.isUndefined(this.__definition.dynamicPageBreak) && this.__definition.dynamicPageBreak) { \n if (tcidCurrentNode.isFirstMainRowCell() || tcidCurrentNode.isFirstCorrelatedRowCell()) {\n // cache dynamic pageBreak to turn them into manual ones during the second run ...\n this.__definition.dynamicPageBreakData.push(tcidCurrentNode.toString()); \n } else {\n doPageBreak = false;\n }\n }\n\n }\n } \n\n return doPageBreak;\n }\n\n __createPdfDocument(pdfDefinition, pdfCreationFunction) {\n // clone pdf definition if applicable ...\n let pdfDefinitionCopy = null;\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isNil(pdfCreationFunction) && _.isFunction(pdfCreationFunction)) {\n // init empty dynamicPageBreakData ...\n _.assign(pdfDefinition, {\n dynamicPageBreakData: []\n });\n\n pdfDefinitionCopy = _.cloneDeep(pdfDefinition);\n // skip header / footer for initial layouting and move them to final layouting\n if (!_.isUndefined(pdfDefinition.header)) {\n let headerFunction = pdfDefinition.header;\n _.assign(pdfDefinitionCopy, {\n 'header': headerFunction\n });\n _.unset(pdfDefinition, 'header');\n }\n if (!_.isUndefined(pdfDefinition.footer)) {\n let footerFunction = pdfDefinition.footer;\n _.assign(pdfDefinitionCopy, {\n 'footer': footerFunction\n });\n _.unset(pdfDefinition, 'footer');\n }\n // remove dynamic pageBreak logic\n _.unset(pdfDefinitionCopy, 'pageBreakBefore');\n }\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinition;\n\n // exec pdfCreation function ...\n pdfCreationFunction.apply(this, [pdfDefinition]);\n\n return pdfDefinitionCopy;\n }\n\n __createFinalPdfDocument(pdfDefinition, pdfDefinitionCopy, pdfCreationFunction) {\n // perform pageBreak magic ...\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) &&\n !_.isNil(pdfDefinitionCopy) && !_.isUndefined(pdfDefinitionCopy.dynamicPageBreak) && pdfDefinitionCopy.dynamicPageBreak &&\n !_.isNil(pdfCreationFunction) && _.isFunction(pdfCreationFunction)) {\n\n // transfer dynamicPageBreakData from pdf definition to its copy\n pdfDefinitionCopy.dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n\n this.__applyPageBreakData(pdfDefinitionCopy);\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinitionCopy;\n\n // exec pdfCreation function ...\n pdfCreationFunction.apply(this, [pdfDefinitionCopy]);\n }\n }\n\n toPdfDefinition(json/*, format*/) {\n this.__initializePdfDefinition();\n this.__addDocumentMetadata(this.__definition, json.documentProperties);\n let reportLayout = json.reportLayout;\n this.__configureAutomaticPageBreak(reportLayout);\n this.__addPageDimensionsOrientationMarginsWatermark(this.__definition, reportLayout);\n this.__addHeader(this.__definition, reportLayout);\n this.__addBody(this.__definition, reportLayout);\n this.__addFooter(this.__definition,reportLayout);\n return this.__definition;\n }\n\n writeToStream(pdfDefinition, stream) {\n // setup pdf creator ...\n let printer = new PdfPrinter(ServerFonts);\n let pdfDoc = null; \n let pdfCreatorFunction = (definition) => {\n printer = new PdfPrinter(ServerFonts);\n pdfDoc = printer.createPdfKitDocument(definition, {\n tableLayouts: TableLayouts\n });\n };\n\n // create pdf and prepare for recreation if applicable ...\n let pdfDefinitionCopy = this.__createPdfDocument(pdfDefinition, pdfCreatorFunction);\n\n // recreate pdf if applicable ...\n this.__createFinalPdfDocument(pdfDefinition, pdfDefinitionCopy, pdfCreatorFunction);\n\n pdfDoc.pipe(stream);\n pdfDoc.end();\n\n return (pdfDefinitionCopy || pdfDefinition);\n }\n\n async __createPdfDocumentAsync(pdfDefinition) {\n return new Promise((resolve, reject) => {\n try {\n \n // clone pdf definition if applicable ...\n let pdfDefinitionCopy = null;\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak) {\n // init empty dynamicPageBreakData ...\n _.assign(pdfDefinition, {\n dynamicPageBreakData: []\n });\n\n pdfDefinitionCopy = _.cloneDeep(pdfDefinition);\n // skip header / footer for initial layouting and move them to final layouting\n if (!_.isUndefined(pdfDefinition.header)) {\n let headerFunction = pdfDefinition.header;\n _.assign(pdfDefinitionCopy, {\n 'header': headerFunction\n });\n _.unset(pdfDefinition, 'header');\n }\n if (!_.isUndefined(pdfDefinition.footer)) {\n let footerFunction = pdfDefinition.footer;\n _.assign(pdfDefinitionCopy, {\n 'footer': footerFunction\n });\n _.unset(pdfDefinition, 'footer');\n }\n // remove dynamic pageBreak logic\n _.unset(pdfDefinitionCopy, 'pageBreakBefore');\n }\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinition; \n \n resolve(pdfDefinitionCopy);\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async __createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy) {\n return new Promise((resolve, reject) => {\n try {\n // perform pageBreak magic ...\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) &&\n !_.isNil(pdfDefinitionCopy) && !_.isUndefined(pdfDefinitionCopy.dynamicPageBreak) && pdfDefinitionCopy.dynamicPageBreak) {\n\n // transfer dynamicPageBreakData from pdf definition to its copy\n pdfDefinitionCopy.dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n\n this.__applyPageBreakData(pdfDefinitionCopy);\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinitionCopy; \n }\n resolve();\n } catch (error) {\n reject(error);\n }\n }); \n }\n\n async __getPdfDataAsync(type, pdfDefinition, previousResult = null) {\n return new Promise((resolve, reject) => {\n try {\n if (_.isNil(pdfDefinition) && !_.isNil(previousResult)) {\n resolve(previousResult);\n } else {\n let pdfDocGenerator = this.__pdfMake.createPdf(pdfDefinition, TableLayouts, BrowserFonts, BrowserFontsVfs.pdfMake.vfs);\n\n switch (type) {\n case EXPORT_TYPE.BASE_64:\n pdfDocGenerator.getBase64((data) => {\n resolve(data);\n });\n break;\n\n case EXPORT_TYPE.BLOB:\n pdfDocGenerator.getBlob((blob) => {\n resolve(blob);\n });\n break;\n\n case EXPORT_TYPE.DATA_URL:\n pdfDocGenerator.getDataUrl((dataUrl) => {\n resolve(dataUrl);\n });\n break;\n\n default: \n throw new Error('Unknown export type: ' + type ); \n }\n }\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async toBase64(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.BASE_64, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.BASE_64, pdfDefinitionCopy, firstResult); \n });\n \n }\n\n async toBlob(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.BLOB, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.BLOB, pdfDefinitionCopy, firstResult); \n });\n }\n \n async toDataUrl(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.DATA_URL, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.DATA_URL, pdfDefinitionCopy, firstResult); \n });\n \n }\n\n static isValidFormat(format) {\n let supportedFormats = ['A3', 'A4', 'A5', 'Legal', 'Letter', 'Tabloid'];\n return supportedFormats.includes(format);\n }\n\n}\n\n\nmodule.exports = PdfConverter;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/pdfConverterClass.js?");
471
+ eval("/*\n * FILE_HEADER\n */\n\n\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst PdfPrinter = __webpack_require__(/*! pdfmake */ \"./node_modules/pdfmake/build/pdfmake.js\");\nconst Format = __webpack_require__(/*! ./formatClass */ \"./src/formatClass.js\");\nconst TableCellIdentifier = __webpack_require__(/*! ./tableCellIdentifier */ \"./src/tableCellIdentifier.js\");\n\n// required for webpack include (browser impl), used in conjunction with BrowserFonts definition\nconst BrowserFontsVfs = __webpack_require__(/*! pdfmake/build/vfs_fonts.js */ \"./node_modules/pdfmake/build/vfs_fonts.js\");\n\n// => use 'Roboto' font (pdfMake default) due to licensing (Apache 2.0)\n// https://fonts.google.com/specimen/Roboto || https://en.wikipedia.org/wiki/Roboto\nconst ServerFonts = {\n Roboto: {\n normal: './fonts/Roboto-Regular.ttf',\n bold: './fonts/Roboto-Medium.ttf',\n italics: './fonts/Roboto-Italic.ttf',\n bolditalics: './fonts/Roboto-MediumItalic.ttf'\n }\n};\n\nconst BrowserFonts = {\n Roboto: {\n normal: 'Roboto-Regular.ttf',\n bold: 'Roboto-Medium.ttf',\n italics: 'Roboto-Italic.ttf',\n bolditalics: 'Roboto-MediumItalic.ttf'\n },\n PrintFont: {\n normal: 'Regular',\n bold: 'Bold',\n italics: 'Italic',\n bolditalics: 'BoldItalic'\n }\n};\n\nconst TableLayouts = {\n lightHorizontalLinesMainItemsOnly: {\n hLineWidth: function (i, node) {\n // no frame for the table\n if (i === 0 || i === node.table.body.length) {\n return 0;\n }\n // bold seperator between header and body\n if (i === node.table.headerRows) {\n return 2;\n }\n // get first cell id for row ...\n let row = node.table.body[i];\n if (!_.isNil(row)) {\n let cellId = null;\n _.forEach(row, cell =>{\n if(_.has(cell, 'id')) {\n cellId = cell.id;\n return false;\n }\n });\n // seperator for between main rows (+ their child rows)\n let tcid = TableCellIdentifier.fromString(cellId);\n if (!_.isNil(tcid)) {\n if (tcid.isKnown() && tcid.belongsToMainRow()) {\n // seperator between main rows\n return 1;\n } else if (!tcid.isKnown()) {\n // seperator for 'unknown row'\n return 1;\n }\n }\n }\n // no seperator for all other cases, e.g. child rows\n return 0;\n },\n vLineWidth: function () {\n return 0;\n },\n hLineColor: function (i, node) {\n return i === node.table.headerRows ? 'black' : 'gray';\n }\n }\n};\n\nconst STYLE_HEADING1 = \"heading1\";\nconst STYLE_HEADING2 = \"heading2\";\nconst STYLE_PARAGRAPH = \"paragraph\";\n\nconst EXPORT_TYPE = {\n \"BASE_64\" : \"BASE_64\",\n \"DATA_URL\" : \"DATA_URL\",\n \"BLOB\" : \"BLOB\"\n};\nObject.freeze(EXPORT_TYPE);\n\nclass PdfConverter {\n constructor() {\n this.__pdfMake = null;\n if(typeof pdfMake !== \"undefined\")\n {\n this.__pdfMake = pdfMake;\n }\n\n this.__format = new Format();\n }\n\n __applyPageBreakData(pdfDefinition) {\n if (!_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak && \n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) && pdfDefinition.dynamicPageBreakData.length > 0) {\n // let table = pdfDefinition.content[0].table;\n let dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n \n // handle tables in body\n let tables = _.filter(pdfDefinition.content, {style: 'tableBody'});\n this.__applyPageBreakDataToTables(tables, dynamicPageBreakData);\n }\n } \n\n __applyPageBreakDataToTables(tables, dynamicPageBreakData) {\n\n if (!_.isNil(tables) && _.isArray(tables) && tables.length > 0 && !_.isNil(dynamicPageBreakData)) {\n for (let i = 0; i < tables.length; i++) {\n // get table\n let table = tables[i].table;\n if (!_.isNil(table) && !_.isUndefined(table.dynamicPageBreak) && table.dynamicPageBreak) {\n // loop rows\n let row = null;\n let cell = null;\n let breakRow = false;\n for (let j = 0; j < table.body.length; j++) { \n row = table.body[j];\n breakRow = false;\n // loop columns\n for (let k = 0; k < row.length; k++) {\n cell = row[k];\n // apply manual pageBreaks instead of dynamic ones\n if (!breakRow && _.has(cell, 'id')) {\n breakRow = _.includes(dynamicPageBreakData, cell.id);\n if (breakRow) {\n cell.pageBreak = 'before';\n } \n } else if (breakRow && _.has(cell, 'id')) {\n cell.pageBreak = 'before';\n }\n }\n }\n }\n }\n }\n\n }\n \n __initializePdfDefinition() {\n this.__definition = {\n \"content\": [],\n pageSize: 'A4',\n pageMargins: [ 40, 60, 40, 60 ],\n defaultStyle: {\n font: 'Roboto', //'Helvetica'\n },\n styles: {\n tableBody: {\n fontSize: 10,\n margin: [0, 5, 0, 15]\n },\n tableHeader: {\n fontSize: 10,\n bold: true\n },\n heading1: {\n fontSize: 18,\n bold: true,\n margin: [0, 0, 0, 10]\n },\n heading2: {\n fontSize: 14,\n bold: true,\n margin: [0, 10, 0, 5]\n },\n paragraph: {\n fontSize: 10,\n bold: false,\n italics: false\n }\n }\n };\n this.__automaticPageBreak = false;\n }\n\n __getThOrTdContent(thTdNode, settings) {\n let result = {};\n let text = \"\";\n\n if(!_.isNil(thTdNode.elements)){\n _.forEach(thTdNode.elements, childElement => {\n if(childElement.type === \"text\") {\n text = text + childElement.text;\n } else if (childElement.type === \"element\" && childElement.name === \"pageNumber\" && !_.isNil(settings.currentPage)) {\n text = text + settings.currentPage;\n } else if (childElement.type === \"element\" && childElement.name === \"img\") {\n result.image = this.__getImageDefinition(childElement, false);\n }\n });\n }\n result.text = text;\n\n return result;\n }\n\n __getHeadingDefinition(headingNode, headingStyle) {\n return this.__createHeadingOrParagraphText(headingNode, headingNode.elements[0].text, headingStyle);\n }\n\n __getParagraphDefinition(paragraphNode, paragraphStyle) {\n return this.__createHeadingOrParagraphText(paragraphNode, paragraphNode.elements[0].text, paragraphStyle);\n }\n\n __getImageDefinition(imageNode, useAlignment) {\n let img = {};\n\n if (!_.isNil(imageNode)){\n let atr = imageNode.attributes;\n let imageId = atr.id;\n let mimeType = atr.mimeType;\n\n if (!_.isNil(imageId) && this.__isImageCached(imageId)) {\n // => png / jpeg image already in cache => simply (re)use it ...\n img = {image: imageId};\n\n } else {\n\n if (mimeType === \"image/svg+xml\") {\n // => svg image handling (vector graphics)\n let src = atr.src.split(\",\");\n let data = src[1];\n let txt;\n\n if ((typeof window === 'object') && (typeof window.atob === 'function')) {\n // we do expect an utf8 string\n txt = decodeURIComponent(encodeURIComponent(window.atob(data)));\n } else {\n let buf = Buffer.from(data, 'base64');\n txt = buf.toString('utf8');\n }\n img = {svg: txt};\n\n } else {\n // => png / jpeg image handling (raster graphics)\n // add image to cache\n this.__addImageToCache(imageId, atr.src);\n img = {image: imageId}; \n\n }\n } \n\n if (!_.isNil(atr.width)) {\n img.width = parseInt(atr.width, 10);\n }\n if (!_.isNil(atr.height)) {\n img.height = parseInt(atr.height, 10);\n }\n if (!_.isNil(atr.fit)) {\n let fit = atr.fit.split(',');\n let w = parseInt(fit[0], 10);\n let h = parseInt(fit[1], 10);\n img.fit = [w,h];\n }\n\n if (!_.isNil(atr.alignment) && (true === useAlignment)) {\n let alignmentAttribute = this.__checkEnumAttribute(imageNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n\n if (!_.isNil(alignmentAttribute)) {\n img.alignment = alignmentAttribute;\n }\n }\n }\n\n return img;\n }\n\n __addImageToCache(imageId, imageData) {\n // init image cache if required\n if (!_.has(this.__definition, 'images')) { \n _.assign(this.__definition, {\n images: {}\n });\n }\n\n // add image to cache\n if (!_.has(this.__definition.images, imageId)) {\n this.__definition.images[imageId] = imageData;\n }\n }\n\n __isImageCached(imageId) {\n return (_.has(this.__definition, 'images') && _.has(this.__definition.images, imageId));\n }\n\n __checkSpansAndColumns(pdfTableDefinition) {\n let rows = 0;\n let columns = 0;\n\n let rowCount = pdfTableDefinition.table.body.length;\n let columnCount = pdfTableDefinition.table.widths.length;\n\n for (rows=0; rows<rowCount; rows++) {\n let row = pdfTableDefinition.table.body[rows];\n\n if (row.length > columnCount) {\n row.length = columnCount;\n } else if (row.length < columnCount) {\n do {\n row.push(\"\");\n } while(row.length < columnCount);\n }\n\n let maxRowSpan = rowCount-rows;\n\n for (columns=0; columns<columnCount; columns++) {\n let column = row[columns];\n let maxColSpan = columnCount-columns;\n\n if (!_.isString(column)) {\n if (!_.isNil(column[\"colSpan\"])) {\n if (column.colSpan > maxColSpan) {\n column.colSpan = maxColSpan;\n }\n if (column.colSpan < 2) {\n delete column.colSpan;\n }\n if ((Object.keys(column).length === 1) && !_.isNil(column[\"text\"])) {\n row[columns] = column.text;\n }\n }\n if (!_.isNil(column[\"rowSpan\"])) {\n if (column.rowSpan > maxRowSpan) {\n column.rowSpan = maxRowSpan;\n }\n if (column.rowSpan < 2) {\n delete column.rowSpan;\n }\n if ((Object.keys(column).length === 1) && !_.isNil(column[\"text\"])) {\n row[columns] = column.text;\n }\n }\n }\n }\n }\n }\n\n __checkIntegerAttribute(element, attributeName, allowedStrings, onlyPositiveNumbers, defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n let bAllowedStrings = !_.isNil(allowedStrings) && _.isArray(allowedStrings) && (allowedStrings.length > 0);\n let bPositiveNumbers = !_.isNil(onlyPositiveNumbers) && _.isBoolean(onlyPositiveNumbers) && (onlyPositiveNumbers === true);\n\n if (bAttribute) {\n let attribute = element.attributes[attributeName];\n let i = 0;\n\n if (bAllowedStrings && _.isString(attribute)){\n for (i=0; i<allowedStrings.length; i++) {\n if (attribute === allowedStrings[i]) {\n return attribute;\n }\n }\n }\n\n let parsed = parseInt(attribute, 10);\n\n if(!isNaN(parsed)) {\n if (bPositiveNumbers && (parsed < 0))\n {\n return defaultValue;\n } else {\n return parsed;\n }\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkBooleanAttribute(element, attributeName,defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if (attributeValue === \"true\") {\n return true;\n } else if (attributeValue === \"false\") {\n return false;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkEnumAttribute(element, attributeName,allowedEnumValues ,defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if(allowedEnumValues.includes(attributeValue)){\n return attributeValue;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __checkStringAttribute(element, attributeName, defaultValue) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && _.isString(attributeName) && (attributeName !== \"\") && !_.isNil(element.attributes[attributeName]);\n\n if(bAttribute) {\n let attributeValue = element.attributes[attributeName];\n if(_.isString(attributeValue)){\n return attributeValue;\n } else {\n return defaultValue;\n }\n } else {\n return defaultValue;\n }\n }\n\n __applyNumberFormatAttribute(element, value) {\n let bElement = !_.isNil(element) && _.isObject(element.attributes);\n let bAttribute = bElement && !_.isNil(element.attributes.numberFormat) && !_.isNil(element.attributes.decimalSeparator) && !_.isNil(element.attributes.thousandSeparator);\n\n if (bAttribute) {\n return this.__format.formatDecimalV2(value, element.attributes.numberFormat, element.attributes.decimalSeparator, element.attributes.thousandSeparator);\n } else {\n return value;\n }\n }\n\n __createHeadingOrParagraphText(currentNode, text, style) {\n let result = { \"text\": text };\n\n let boldAttribute = this.__checkBooleanAttribute(currentNode, 'bold', null);\n let italicsAttribute = this.__checkBooleanAttribute(currentNode, 'italics', null);\n let alignmentAttribute = this.__checkEnumAttribute(currentNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n\n if (_.isNil(boldAttribute) && _.isNil(italicsAttribute) && _.isNil(alignmentAttribute)) {\n result.style = style;\n } else {\n let styleJson = this.__definition.styles[style];\n\n if (!_.isNil(styleJson.fontSize)) {\n result.fontSize = styleJson.fontSize;\n }\n\n if (!_.isNil(styleJson.bold)) {\n result.bold = styleJson.bold;\n }\n\n if (!_.isNil(styleJson.italics)) {\n result.italics = styleJson.italics;\n }\n\n if (!_.isNil(styleJson.margin)) {\n result.margin = styleJson.margin;\n }\n\n if (!_.isNil(boldAttribute)) {\n result.bold = boldAttribute;\n }\n\n if (!_.isNil(italicsAttribute)) {\n result.italics = italicsAttribute;\n }\n\n if (!_.isNil(alignmentAttribute)) {\n result.alignment = alignmentAttribute;\n }\n }\n\n return result;\n }\n\n __createTableColumn(content, style, colSpan, rowSpan, alignment, bold, italics, cellId) {\n let bTextOnly = _.isNil(content.image) && _.isNil(style) && _.isNil(colSpan) && _.isNil(rowSpan)\n && _.isNil(alignment) && _.isNil(bold) && _.isNil(italics) && _.isNil(cellId);\n\n if (bTextOnly) {\n return content.text;\n }\n\n let column = {};\n\n //Image wins over text if both are defined.\n if (!_.isNil(content.image)) {\n column = content.image;\n } else {\n column.text = content.text;\n }\n\n if (!_.isNil(style)) {\n column.style = style;\n }\n if (!_.isNil(colSpan)) {\n column.colSpan = colSpan;\n }\n if (!_.isNil(rowSpan)) {\n column.rowSpan = rowSpan;\n }\n if (!_.isNil(alignment)) {\n column.alignment = alignment;\n }\n if (!_.isNil(bold)) {\n column.bold = bold;\n }\n if (!_.isNil(italics)) {\n column.italics = italics;\n }\n if (!_.isNil(cellId)) {\n column.id = cellId;\n }\n\n return column;\n }\n\n __getTableCellAttributes(thOrTdNode){\n let colSpanAttributeValue = this.__checkIntegerAttribute(thOrTdNode, 'colSpan', [], true, null);\n let rowSpanAttributeValue = this.__checkIntegerAttribute(thOrTdNode, 'rowSpan', [], true, null);\n let boldAttribute = this.__checkBooleanAttribute(thOrTdNode, 'bold', null);\n let italicsAttribute = this.__checkBooleanAttribute(thOrTdNode, 'italics', null);\n let alignmentAttribute = this.__checkEnumAttribute(thOrTdNode, 'alignment', [\"left\", \"center\", \"right\"], null);\n let cellId = this.__checkStringAttribute(thOrTdNode, 'id', null);\n\n return [colSpanAttributeValue, rowSpanAttributeValue, alignmentAttribute, boldAttribute, italicsAttribute, cellId];\n }\n\n __evaluateTableDefinitionRowBreak(tableNode, dynamicPageBreak) {\n let dontBreakRows = this.__checkBooleanAttribute(tableNode, \"dontBreakRows\", true);\n // deactivate automatic row breaks for tables requiring dynamic page breaks\n if (dynamicPageBreak) {\n dontBreakRows = false;\n }\n return dontBreakRows;\n }\n\n __evaluateTableDefinitionDynamicPageBreak(tableNode) {\n let automaticPageBreak = this.__automaticPageBreak; // => coming from PrintV2 action configuration\n let dynamicPageBreak = (automaticPageBreak && this.__checkBooleanAttribute(tableNode, \"dynamicPageBreak\", false));\n if (dynamicPageBreak) {\n this.__configurePdfDefinitionForDynamicPageBreak();\n }\n return dynamicPageBreak;\n }\n\n __configurePdfDefinitionForDynamicPageBreak() {\n if (!_.has(this.__definition, 'pageBreakBefore')) {\n _.assign(this.__definition, {\n pageBreakBefore: this.__calculatePageBreakBefore.bind(this)\n });\n this.__definition.dynamicPageBreak = true;\n }\n }\n\n __getTableDefinition(tableNode, settings) {\n if(_.isNil(settings)) {settings = {};}\n\n let dynamicPageBreak = this.__evaluateTableDefinitionDynamicPageBreak(tableNode);\n let dontBreakRows = this.__evaluateTableDefinitionRowBreak(tableNode, dynamicPageBreak);\n let pdfTableDefinition = {\n style: \"tableBody\",\n table: {\n dontBreakRows: dontBreakRows,\n headerRows: 0,\n body: [],\n widths: []\n }\n };\n if (dynamicPageBreak) {\n pdfTableDefinition.table.dynamicPageBreak = true;\n }\n\n if(!_.isNil(settings.layout)) {\n pdfTableDefinition.layout = settings.layout;\n }\n if(!_.isNil(tableNode.attributes) && !_.isNil(tableNode.attributes.tableLayout)){\n pdfTableDefinition.layout = tableNode.attributes.tableLayout;\n }\n\n let bNoWidths = false;\n let theadNode = _.find(tableNode.elements, {name: 'thead'});\n let tbodyNode = _.find(tableNode.elements, {name: 'tbody'});\n\n if(!_.isNil(theadNode)) {\n _.forEach(theadNode.elements, headerTrNode=> {\n let row = [];\n _.forEach(headerTrNode.elements, value => {\n let attributeValues = this.__getTableCellAttributes(value);\n\n if (pdfTableDefinition.table.headerRows === 0) {\n let widthAttributeValue = this.__checkIntegerAttribute(value, 'width', ['*', 'auto'], true, 'auto');\n pdfTableDefinition.table.widths.push(widthAttributeValue);\n }\n let content = this.__getThOrTdContent(value, settings);\n row.push(this.__createTableColumn(content, 'tableHeader', ...attributeValues));\n });\n\n pdfTableDefinition.table.body.push(row);\n pdfTableDefinition.table.headerRows += 1;\n });\n } else if(!_.isNil(tbodyNode)) {\n bNoWidths = true;\n } else {\n pdfTableDefinition.table.widths = ['auto'];\n pdfTableDefinition.table.body = [['']];\n\n return pdfTableDefinition;\n }\n\n if(!_.isNil(tbodyNode)) {\n _.forEach(tbodyNode.elements, tbodyTrNode=> {\n let row = [];\n _.forEach(tbodyTrNode.elements, value => {\n let attributeValues = this.__getTableCellAttributes(value);\n\n if ((bNoWidths === true)) {\n let widthAttributeValue = this.__checkIntegerAttribute(value, 'width', ['*', 'auto'], true, 'auto');\n pdfTableDefinition.table.widths.push(widthAttributeValue);\n }\n let content = this.__getThOrTdContent(value, settings);\n content.text = this.__applyNumberFormatAttribute(value, content.text);\n\n row.push(this.__createTableColumn(content, null, ...attributeValues));\n });\n\n bNoWidths = false;\n pdfTableDefinition.table.body.push(row);\n });\n }\n\n this.__checkSpansAndColumns(pdfTableDefinition);\n \n return pdfTableDefinition;\n }\n\n __addBody(definition, json) {\n\n _.forEach(json.elements, contractElement => {\n if(contractElement.name === 'table') {\n let tableDefintion = this.__getTableDefinition(contractElement);\n definition.content.push(tableDefintion);\n } else if (contractElement.name === 'h1') {\n let h1Defintion = this.__getHeadingDefinition(contractElement, STYLE_HEADING1);\n definition.content.push(h1Defintion);\n } else if (contractElement.name === 'h2') {\n let h2Defintion = this.__getHeadingDefinition(contractElement, STYLE_HEADING2);\n definition.content.push(h2Defintion);\n } else if (contractElement.name === 'p') {\n let paragraphDefintion = this.__getParagraphDefinition(contractElement, STYLE_PARAGRAPH);\n definition.content.push(paragraphDefintion);\n } else if (contractElement.name === 'img' || contractElement.name === 'signature') {\n let imageDefintion = this.__getImageDefinition(contractElement, true);\n definition.content.push(imageDefintion);\n }\n });\n\n }\n\n __addHeader(definition, json) {\n let headerNode = _.find(json.elements, {name:\"header\"});\n\n if(!_.isNil(headerNode)) {\n let imageNode = _.find(headerNode.elements, {name:\"img\"});\n let tableNode = _.find(headerNode.elements, {name:\"table\"});\n\n //The coverage report only works correctly when isWriteDataEnabled() returns true in the Unit Tests.\n definition.header = (currentPage/*, pageCount, pageSize*/) => {\n let imageDefinition = (!_.isNil(imageNode)) ? this.__getImageDefinition(imageNode, true) : null;\n let tableDefinition = (!_.isNil(tableNode)) ? this.__getTableDefinition(tableNode, {\"layout\": \"noBorders\", \"currentPage\": currentPage}) : null;\n\n if (!_.isNil(imageDefinition) && !_.isNil(tableDefinition)) {\n return [imageDefinition, tableDefinition];\n } else if (!_.isNil(tableDefinition)) {\n return tableDefinition;\n } else if (!_.isNil(imageDefinition)) {\n return imageDefinition;\n } else {\n return null;\n }\n };\n }\n }\n\n __addFooter(definition, json) {\n let footerNode = _.find(json.elements, {name:\"footer\"});\n if(!_.isNil(footerNode)) {\n let tableNode = _.find(footerNode.elements, {name:\"table\"});\n let bTableNode = !_.isNil(tableNode);\n\n //The coverage report only works correctly when isWriteDataEnabled() returns true in the Unit Tests.\n definition.footer = (currentPage/*, pageCount, pageSize*/) => {\n let tableDefinition = bTableNode ? this.__getTableDefinition(tableNode, {\"layout\": \"noBorders\", \"currentPage\":currentPage}) : null;\n return tableDefinition;\n };\n }\n }\n\n __addPrintFont(definition, printFont) {\n if (_.isObject(printFont)\n && (\"Regular\" in printFont)\n && (\"Bold\" in printFont)\n && (\"Italic\" in printFont)\n && (\"BoldItalic\" in printFont)) {\n definition.defaultStyle.font = \"PrintFont\";\n definition.printFont = printFont;\n }\n }\n\n __addDocumentMetadata(definition, documentProperties){\n definition.info = {};\n definition.info.creator = \"Consumer Goods Cloud\";\n definition.info.producer = \"Consumer Goods Cloud\";\n _.forEach(documentProperties, (value, key) => {\n definition.info[key] = value;\n });\n }\n\n __configureAutomaticPageBreak(reportLayout) {\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.automaticPageBreak)){\n this.__automaticPageBreak = reportLayout.attributes.automaticPageBreak;\n }\n }\n\n __addPageDimensionsOrientationMarginsWatermark(definition, reportLayout){\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.pageSize)){\n definition.pageSize = reportLayout.attributes.pageSize;\n }\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.watermark)){\n definition.watermark = {text: reportLayout.attributes.watermark, opacity:0.1, fontSize: 120};\n }\n if(!_.isNil(reportLayout.attributes) && !_.isNil(reportLayout.attributes.pageMargins)){\n definition.pageMargins = JSON.parse(reportLayout.attributes.pageMargins);\n }\n }\n\n __isLastMainRowOnPage(followingNodesOnPage) {\n let result = false;\n\n if (!_.isNil(followingNodesOnPage) && _.isArray(followingNodesOnPage)) {\n result = (followingNodesOnPage.length === 0) || !(_.some(followingNodesOnPage, y => {\n let tcid = TableCellIdentifier.fromString(y.id);\n return (tcid.isKnown() && tcid.isFirstMainRowCell());\n }));\n }\n\n return result;\n }\n\n __isFirstNodeOnNextPageFirstMainRowCell(nodesOnNextPage) {\n let result = false;\n\n if (!_.isNil(nodesOnNextPage) && _.isArray(nodesOnNextPage) && nodesOnNextPage.length > 0) {\n let firstNodeOnNextPage = _.first(nodesOnNextPage);\n let tcid = TableCellIdentifier.fromString(firstNodeOnNextPage.id);\n result = (tcid.isKnown() && tcid.isFirstMainRowCell());\n }\n\n return result;\n }\n\n __calculatePageBreakForParentChildRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode) {\n let doPageBreak = false;\n\n // dynamic pageBreak logic for parent-child rows\n if (!_.isNil(previousNodesOnPage) && _.isArray(previousNodesOnPage) && previousNodesOnPage.length > 0) {\n let previousNode = _.last(previousNodesOnPage);\n let tcidPreviousNode = TableCellIdentifier.fromString(previousNode.id);\n\n // => check if we are the first cell of a main row ...\n if (tcidCurrentNode.isFirstMainRowCell()) {\n\n if (!_.isNil(followingNodesOnPage) && _.isArray(followingNodesOnPage)) {\n\n let isCurrentNodeLastHeadOnPageX = this.__isLastMainRowOnPage(followingNodesOnPage);\n let isFirstNodeOnNextPageFirstMainRowCellX = this.__isFirstNodeOnNextPageFirstMainRowCell(nodesOnNextPage);\n let isPageBreakRelevant = (tcidPreviousNode.isKnown() && (tcidPreviousNode.isFirstMainRowCell() || tcidPreviousNode.isSubsequentMainRowCell() || tcidPreviousNode.isFirstCorrelatedRowCell() || tcidPreviousNode.isSubsequentCorrelatedRowCell()));\n\n doPageBreak = (isCurrentNodeLastHeadOnPageX && !isFirstNodeOnNextPageFirstMainRowCellX && isPageBreakRelevant);\n }\n\n } else if (tcidCurrentNode.isSubsequentMainRowCell()) {\n\n doPageBreak = (!tcidPreviousNode.isFirstMainRowCell() && !tcidPreviousNode.isSubsequentMainRowCell() || !(tcidCurrentNode.sharesMainRow(tcidPreviousNode)));\n }\n }\n\n return doPageBreak;\n }\n\n\n __calculatePageBreakForBrokenRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode) {\n let doPageBreak = false;\n\n // => check if we are the first cell of a row ...\n if (tcidCurrentNode.isFirstMainRowCell() || tcidCurrentNode.isFirstCorrelatedRowCell()) {\n // check for 'broken' row ...\n // => check if i am the last row\n let isLastRowOnPageX = (_.every(followingNodesOnPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n return !tcidNode.isKnown() || (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode));\n }));\n\n if (isLastRowOnPageX) {\n // check if any cell of the current row extends over more than one page => pageBreak!\n // => check current cell (first cell of row)\n doPageBreak = (!_.isNil(currentNode.pageNumbers) && _.isArray(currentNode.pageNumbers) && currentNode.pageNumbers.length > 1);\n if (!doPageBreak) {\n // => check following cells on current page and cells on next page for page overlaps (other cells of row)\n doPageBreak = (_.some(followingNodesOnPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n if (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode)) {\n return !_.isEqual(currentNode.pageNumbers, node.pageNumbers);\n }\n return false;\n }) || (_.some(nodesOnNextPage, node => {\n let tcidNode = TableCellIdentifier.fromString(node.id);\n if (tcidNode.isKnown() && tcidCurrentNode.sharesCorrelatedRow(tcidNode)) {\n return !_.isEqual(currentNode.pageNumbers, node.pageNumbers);\n }\n return false;\n })));\n }\n }\n } else if (tcidCurrentNode.isSubsequentMainRowCell() || tcidCurrentNode.isSubsequentCorrelatedRowCell()) {\n // => check other row cells ...\n if (!_.isNil(previousNodesOnPage) && _.isArray(previousNodesOnPage) && previousNodesOnPage.length > 0) {\n // check if last previous node is NOT precursor cell of current one => pageBreak!\n let previousNode = _.last(previousNodesOnPage);\n let tcidPreviousNode = TableCellIdentifier.fromString(previousNode.id);\n doPageBreak = !tcidCurrentNode.isPreviousCell(tcidPreviousNode);\n }\n }\n\n return doPageBreak;\n }\n\n __calculatePageBreakBefore(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage) {\n let doPageBreak = false;\n \n // looking for potential pageBreaks only makes sense if there are elements to be placed there (=> might require adaption for subtotals)\n if (!_.isNil(nodesOnNextPage) && _.isArray(nodesOnNextPage) && nodesOnNextPage.length > 0) {\n\n // get table cell identifier\n let tcidCurrentNode = TableCellIdentifier.fromString(currentNode.id);\n if (!_.isNil(tcidCurrentNode) && tcidCurrentNode.isKnown()) {\n\n // check for dynamic pageBreak of parent-child rows\n doPageBreak = this.__calculatePageBreakForParentChildRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode); \n if (!doPageBreak) { \n // check for dynamic pageBreak of 'broken' row\n doPageBreak = this.__calculatePageBreakForBrokenRows(currentNode, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage, tcidCurrentNode); \n }\n \n if (doPageBreak && !_.isUndefined(this.__definition.dynamicPageBreak) && this.__definition.dynamicPageBreak) { \n if (tcidCurrentNode.isFirstMainRowCell() || tcidCurrentNode.isFirstCorrelatedRowCell()) {\n // cache dynamic pageBreak to turn them into manual ones during the second run ...\n this.__definition.dynamicPageBreakData.push(tcidCurrentNode.toString()); \n } else {\n doPageBreak = false;\n }\n }\n\n }\n } \n\n return doPageBreak;\n }\n\n __createPdfDocument(pdfDefinition, pdfCreationFunction) {\n // clone pdf definition if applicable ...\n let pdfDefinitionCopy = null;\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isNil(pdfCreationFunction) && _.isFunction(pdfCreationFunction)) {\n // init empty dynamicPageBreakData ...\n _.assign(pdfDefinition, {\n dynamicPageBreakData: []\n });\n\n pdfDefinitionCopy = _.cloneDeep(pdfDefinition);\n // skip header / footer for initial layouting and move them to final layouting\n if (!_.isUndefined(pdfDefinition.header)) {\n let headerFunction = pdfDefinition.header;\n _.assign(pdfDefinitionCopy, {\n 'header': headerFunction\n });\n _.unset(pdfDefinition, 'header');\n }\n if (!_.isUndefined(pdfDefinition.footer)) {\n let footerFunction = pdfDefinition.footer;\n _.assign(pdfDefinitionCopy, {\n 'footer': footerFunction\n });\n _.unset(pdfDefinition, 'footer');\n }\n // remove dynamic pageBreak logic\n _.unset(pdfDefinitionCopy, 'pageBreakBefore');\n }\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinition;\n\n // exec pdfCreation function ...\n pdfCreationFunction.apply(this, [pdfDefinition]);\n\n return pdfDefinitionCopy;\n }\n\n __createFinalPdfDocument(pdfDefinition, pdfDefinitionCopy, pdfCreationFunction) {\n // perform pageBreak magic ...\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) &&\n !_.isNil(pdfDefinitionCopy) && !_.isUndefined(pdfDefinitionCopy.dynamicPageBreak) && pdfDefinitionCopy.dynamicPageBreak &&\n !_.isNil(pdfCreationFunction) && _.isFunction(pdfCreationFunction)) {\n\n // transfer dynamicPageBreakData from pdf definition to its copy\n pdfDefinitionCopy.dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n\n this.__applyPageBreakData(pdfDefinitionCopy);\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinitionCopy;\n\n // exec pdfCreation function ...\n pdfCreationFunction.apply(this, [pdfDefinitionCopy]);\n }\n }\n\n toPdfDefinition(json/*, format*/) {\n this.__initializePdfDefinition();\n this.__addPrintFont(this.__definition, json.printFont);\n this.__addDocumentMetadata(this.__definition, json.documentProperties);\n let reportLayout = json.reportLayout;\n this.__configureAutomaticPageBreak(reportLayout);\n this.__addPageDimensionsOrientationMarginsWatermark(this.__definition, reportLayout);\n this.__addHeader(this.__definition, reportLayout);\n this.__addBody(this.__definition, reportLayout);\n this.__addFooter(this.__definition,reportLayout);\n return this.__definition;\n }\n\n writeToStream(pdfDefinition, stream) {\n // setup pdf creator ...\n let printer = new PdfPrinter(ServerFonts);\n let pdfDoc = null; \n let pdfCreatorFunction = (definition) => {\n printer = new PdfPrinter(ServerFonts);\n pdfDoc = printer.createPdfKitDocument(definition, {\n tableLayouts: TableLayouts\n });\n };\n\n // create pdf and prepare for recreation if applicable ...\n let pdfDefinitionCopy = this.__createPdfDocument(pdfDefinition, pdfCreatorFunction);\n\n // recreate pdf if applicable ...\n this.__createFinalPdfDocument(pdfDefinition, pdfDefinitionCopy, pdfCreatorFunction);\n\n pdfDoc.pipe(stream);\n pdfDoc.end();\n\n return (pdfDefinitionCopy || pdfDefinition);\n }\n\n async __createPdfDocumentAsync(pdfDefinition) {\n return new Promise((resolve, reject) => {\n try {\n \n // clone pdf definition if applicable ...\n let pdfDefinitionCopy = null;\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak) {\n // init empty dynamicPageBreakData ...\n _.assign(pdfDefinition, {\n dynamicPageBreakData: []\n });\n\n pdfDefinitionCopy = _.cloneDeep(pdfDefinition);\n // skip header / footer for initial layouting and move them to final layouting\n if (!_.isUndefined(pdfDefinition.header)) {\n let headerFunction = pdfDefinition.header;\n _.assign(pdfDefinitionCopy, {\n 'header': headerFunction\n });\n _.unset(pdfDefinition, 'header');\n }\n if (!_.isUndefined(pdfDefinition.footer)) {\n let footerFunction = pdfDefinition.footer;\n _.assign(pdfDefinitionCopy, {\n 'footer': footerFunction\n });\n _.unset(pdfDefinition, 'footer');\n }\n // remove dynamic pageBreak logic\n _.unset(pdfDefinitionCopy, 'pageBreakBefore');\n }\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinition; \n \n resolve(pdfDefinitionCopy);\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async __createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy) {\n return new Promise((resolve, reject) => {\n try {\n // perform pageBreak magic ...\n if (!_.isNil(pdfDefinition) && !_.isUndefined(pdfDefinition.dynamicPageBreak) && pdfDefinition.dynamicPageBreak &&\n !_.isUndefined(pdfDefinition.dynamicPageBreakData) && _.isArray(pdfDefinition.dynamicPageBreakData) &&\n !_.isNil(pdfDefinitionCopy) && !_.isUndefined(pdfDefinitionCopy.dynamicPageBreak) && pdfDefinitionCopy.dynamicPageBreak) {\n\n // transfer dynamicPageBreakData from pdf definition to its copy\n pdfDefinitionCopy.dynamicPageBreakData = pdfDefinition.dynamicPageBreakData;\n\n this.__applyPageBreakData(pdfDefinitionCopy);\n\n // set pdf definition to be layouted ...\n this.__definition = pdfDefinitionCopy; \n }\n resolve();\n } catch (error) {\n reject(error);\n }\n }); \n }\n\n async __getPdfDataAsync(type, pdfDefinition, previousResult = null) {\n return new Promise((resolve, reject) => {\n try {\n if (_.isNil(pdfDefinition) && !_.isNil(previousResult)) {\n resolve(previousResult);\n } else {\n let pdfDocGenerator = null;\n if (pdfDefinition.defaultStyle.font !== \"PrintFont\") {\n pdfDocGenerator = this.__pdfMake.createPdf(pdfDefinition, TableLayouts, BrowserFonts, BrowserFontsVfs.pdfMake.vfs);\n }\n else {\n pdfDocGenerator = this.__pdfMake.createPdf(pdfDefinition, TableLayouts, BrowserFonts, pdfDefinition.printFont);\n }\n \n switch (type) {\n case EXPORT_TYPE.BASE_64:\n pdfDocGenerator.getBase64((data) => {\n resolve(data);\n });\n break;\n\n case EXPORT_TYPE.BLOB:\n pdfDocGenerator.getBlob((blob) => {\n resolve(blob);\n });\n break;\n\n case EXPORT_TYPE.DATA_URL:\n pdfDocGenerator.getDataUrl((dataUrl) => {\n resolve(dataUrl);\n });\n break;\n\n default: \n throw new Error('Unknown export type: ' + type ); \n }\n }\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async toBase64(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.BASE_64, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.BASE_64, pdfDefinitionCopy, firstResult); \n });\n \n }\n\n async toBlob(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.BLOB, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.BLOB, pdfDefinitionCopy, firstResult); \n });\n }\n \n async toDataUrl(pdfDefinition) {\n // hint: works only in browser avoid calling it in node.js\n let pdfDefinitionCopy = null;\n let firstResult = null; \n \n return this.__createPdfDocumentAsync(pdfDefinition).then((pdfDef) => {\n pdfDefinitionCopy = pdfDef;\n return this.__getPdfDataAsync(EXPORT_TYPE.DATA_URL, pdfDefinition);\n }).then((pdfData) => {\n firstResult = pdfData;\n return this.__createFinalPdfDocumentAsync(pdfDefinition, pdfDefinitionCopy);\n }).then(() => {\n return this.__getPdfDataAsync(EXPORT_TYPE.DATA_URL, pdfDefinitionCopy, firstResult); \n });\n \n }\n\n static isValidFormat(format) {\n let supportedFormats = ['A3', 'A4', 'A5', 'Legal', 'Letter', 'Tabloid'];\n return supportedFormats.includes(format);\n }\n\n}\n\n\nmodule.exports = PdfConverter;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/pdfConverterClass.js?");
472
472
 
473
473
  /***/ }),
474
474
 
@@ -479,7 +479,7 @@ eval("/*\n * FILE_HEADER\n */\n\n\nconst _ = __webpack_require__(/*! lodash */ \
479
479
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
480
480
 
481
481
  "use strict";
482
- eval("/*\n * FILE_HEADER\n */\n\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst ClassLocalization = __webpack_require__(/*! ./localizationClass */ \"./src/localizationClass.js\");\nconst ClassToggles = __webpack_require__(/*! ./togglesClass */ \"./src/togglesClass.js\");\nconst STORAGE_KEYS = {\n \"BOS\": \"BOS\",\n \"LOCALIZATION\": \"LOCALIZATION\",\n \"IMAGES\": \"IMAGES\",\n \"TOGGLES\":\"TOGGLES\",\n \"METADATA\": \"METADATA\"\n};\nObject.freeze(STORAGE_KEYS);\n\nconst METADATA_KEYS = {\n \"LOCALE\": \"locale\",\n \"WATERMARK\": \"watermark\",\n \"AUTOMATIC_PAGEBREAK\": \"automaticPageBreak\"\n};\nObject.freeze(METADATA_KEYS);\nconst DATATYPE_ERROR_MSG = \"Wrong datatype.\";\n\nclass PrintEngineParams {\n\n constructor(bos = {}, images = {}, localization = {}, toggles = new ClassToggles({}), metadata = {}) {\n //Optional parameters for constructor via destructuring assignments including call without parameters.\n this.__checkInputSetBOs(bos);\n this.__checkInputSetImages(images);\n this.__checkInputSetLocalization(localization);\n this.__checkInputSetToggles(toggles);\n this.__checkInputSetMetadata(metadata);\n this.__internalStorage = {};\n this.__internalStorage[STORAGE_KEYS.BOS] = bos;\n this.__internalStorage[STORAGE_KEYS.IMAGES] = images;\n this.__internalStorage[STORAGE_KEYS.LOCALIZATION] = localization;\n this.__internalStorage[STORAGE_KEYS.TOGGLES] = toggles;\n this.__internalStorage[STORAGE_KEYS.METADATA] = metadata;\n }\n\n __checkInputSetBOs(boDictionary){\n if(!_.isObject(boDictionary) || _.isArray(boDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setBOs(boDictionary) {\n this.__checkInputSetBOs(boDictionary);\n this.__internalStorage[STORAGE_KEYS.BOS] = boDictionary;\n }\n\n getMetadataKeys(){\n return METADATA_KEYS;\n }\n\n getBOs(){\n return this.__internalStorage[STORAGE_KEYS.BOS];\n }\n __checkInputSetMetadata(metadataDictionary){\n if(!_.isObject(metadataDictionary) || _.isArray(metadataDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n getMetadata(){\n return this.__internalStorage[STORAGE_KEYS.METADATA];\n }\n\n setMetadata(metadataDictionary) {\n this.__checkInputSetMetadata(metadataDictionary);\n this.__internalStorage[STORAGE_KEYS.METADATA] = metadataDictionary;\n }\n\n __checkInputSetImages(imageDictionary){\n if(!_.isObject(imageDictionary) || _.isArray(imageDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setImages(imageDictionary){\n this.__checkInputSetImages(imageDictionary);\n this.__internalStorage[STORAGE_KEYS.IMAGES] = imageDictionary;\n }\n\n getImages(){\n return this.__internalStorage[STORAGE_KEYS.IMAGES];\n }\n\n __checkInputSetLocalization(localizationDictionary){\n if(!_.isObject(localizationDictionary) || _.isArray(localizationDictionary) ||\n localizationDictionary instanceof ClassLocalization) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setLocalization(localizationDictionary){\n this.__checkInputSetLocalization(localizationDictionary);\n this.__internalStorage[STORAGE_KEYS.LOCALIZATION] = localizationDictionary;\n }\n\n getLocalization(){\n return this.__internalStorage[STORAGE_KEYS.LOCALIZATION];\n }\n\n __checkInputSetToggles(togglesDictionary){\n if(! (togglesDictionary instanceof ClassToggles)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setToggles(togglesDictionary){\n this.__checkInputSetToggles(togglesDictionary);\n this.__internalStorage[STORAGE_KEYS.TOGGLES] = togglesDictionary;\n }\n\n getToggles(){\n return this.__internalStorage[STORAGE_KEYS.TOGGLES];\n }\n\n serialize(){\n let storageClone = _.cloneDeep(this.__internalStorage);\n delete storageClone[STORAGE_KEYS.LOCALIZATION];\n storageClone[STORAGE_KEYS.TOGGLES] = storageClone[STORAGE_KEYS.TOGGLES].getToggleBuffer();\n return JSON.stringify(storageClone);\n }\n}\n\nmodule.exports = PrintEngineParams;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/printEngineParams.js?");
482
+ eval("/*\n * FILE_HEADER\n */\n\nconst _ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\nconst ClassLocalization = __webpack_require__(/*! ./localizationClass */ \"./src/localizationClass.js\");\nconst ClassToggles = __webpack_require__(/*! ./togglesClass */ \"./src/togglesClass.js\");\nconst STORAGE_KEYS = {\n \"BOS\": \"BOS\",\n \"LOCALIZATION\": \"LOCALIZATION\",\n \"IMAGES\": \"IMAGES\",\n \"TOGGLES\":\"TOGGLES\",\n \"METADATA\": \"METADATA\",\n \"PRINTFONT\": \"PRINTFONT\"\n};\nObject.freeze(STORAGE_KEYS);\n\nconst METADATA_KEYS = {\n \"LOCALE\": \"locale\",\n \"WATERMARK\": \"watermark\",\n \"AUTOMATIC_PAGEBREAK\": \"automaticPageBreak\"\n};\nObject.freeze(METADATA_KEYS);\nconst DATATYPE_ERROR_MSG = \"Wrong datatype.\";\n\nclass PrintEngineParams {\n\n constructor(bos = {}, images = {}, localization = {}, toggles = new ClassToggles({}), metadata = {}, printfont = {}) {\n //Optional parameters for constructor via destructuring assignments including call without parameters.\n this.__checkInputSetBOs(bos);\n this.__checkInputSetImages(images);\n this.__checkInputSetLocalization(localization);\n this.__checkInputSetToggles(toggles);\n this.__checkInputSetMetadata(metadata);\n this.__checkInputSetPrintFont(printfont);\n this.__internalStorage = {};\n this.__internalStorage[STORAGE_KEYS.BOS] = bos;\n this.__internalStorage[STORAGE_KEYS.IMAGES] = images;\n this.__internalStorage[STORAGE_KEYS.LOCALIZATION] = localization;\n this.__internalStorage[STORAGE_KEYS.TOGGLES] = toggles;\n this.__internalStorage[STORAGE_KEYS.METADATA] = metadata;\n this.__internalStorage[STORAGE_KEYS.PRINTFONT] = printfont;\n }\n\n __checkInputSetBOs(boDictionary){\n if(!_.isObject(boDictionary) || _.isArray(boDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setBOs(boDictionary) {\n this.__checkInputSetBOs(boDictionary);\n this.__internalStorage[STORAGE_KEYS.BOS] = boDictionary;\n }\n\n getMetadataKeys(){\n return METADATA_KEYS;\n }\n\n getBOs(){\n return this.__internalStorage[STORAGE_KEYS.BOS];\n }\n __checkInputSetMetadata(metadataDictionary){\n if(!_.isObject(metadataDictionary) || _.isArray(metadataDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n getMetadata(){\n return this.__internalStorage[STORAGE_KEYS.METADATA];\n }\n\n setMetadata(metadataDictionary) {\n this.__checkInputSetMetadata(metadataDictionary);\n this.__internalStorage[STORAGE_KEYS.METADATA] = metadataDictionary;\n }\n \n getPrintFont(){\n return this.__internalStorage[STORAGE_KEYS.PRINTFONT];\n }\n __checkInputSetPrintFont(printFont){\n if(!_.isObject(printFont)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setPrintFont(printFont) {\n this.__checkInputSetPrintFont(printFont);\n this.__internalStorage[STORAGE_KEYS.PRINTFONT] = printFont;\n }\n\n __checkInputSetImages(imageDictionary){\n if(!_.isObject(imageDictionary) || _.isArray(imageDictionary)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setImages(imageDictionary){\n this.__checkInputSetImages(imageDictionary);\n this.__internalStorage[STORAGE_KEYS.IMAGES] = imageDictionary;\n }\n\n getImages(){\n return this.__internalStorage[STORAGE_KEYS.IMAGES];\n }\n\n __checkInputSetLocalization(localizationDictionary){\n if(!_.isObject(localizationDictionary) || _.isArray(localizationDictionary) ||\n localizationDictionary instanceof ClassLocalization) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setLocalization(localizationDictionary){\n this.__checkInputSetLocalization(localizationDictionary);\n this.__internalStorage[STORAGE_KEYS.LOCALIZATION] = localizationDictionary;\n }\n\n getLocalization(){\n return this.__internalStorage[STORAGE_KEYS.LOCALIZATION];\n }\n\n __checkInputSetToggles(togglesDictionary){\n if(! (togglesDictionary instanceof ClassToggles)) {\n throw new Error(DATATYPE_ERROR_MSG);\n }\n }\n setToggles(togglesDictionary){\n this.__checkInputSetToggles(togglesDictionary);\n this.__internalStorage[STORAGE_KEYS.TOGGLES] = togglesDictionary;\n }\n\n getToggles(){\n return this.__internalStorage[STORAGE_KEYS.TOGGLES];\n }\n\n serialize(){\n let storageClone = _.cloneDeep(this.__internalStorage);\n delete storageClone[STORAGE_KEYS.LOCALIZATION];\n storageClone[STORAGE_KEYS.TOGGLES] = storageClone[STORAGE_KEYS.TOGGLES].getToggleBuffer();\n return JSON.stringify(storageClone);\n }\n}\n\nmodule.exports = PrintEngineParams;\n\n\n//# sourceURL=webpack://__FWPrinting/./src/printEngineParams.js?");
483
483
 
484
484
  /***/ }),
485
485