@medicus.ai/medicus-report-pdf-generator 1.3.13 → 1.3.15

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.
Files changed (178) hide show
  1. package/.vscode/settings.json +3 -3
  2. package/HANDOVER.md +146 -0
  3. package/README.md +26 -26
  4. package/app/i18n.config.js +20 -20
  5. package/app/i18n_wellbeing.config.js +20 -20
  6. package/app/services/localeService.js +28 -28
  7. package/assets/Mediclinic/css/nasco_report.css +6297 -6297
  8. package/assets/Mediclinic/css/nasco_report_rtl.css +851 -851
  9. package/assets/Mediclinic/css/style.css +33 -33
  10. package/assets/Mediclinic/js/js.js +112 -112
  11. package/assets/Mediclinic/js/raphael.js +8437 -8437
  12. package/assets/arabic.css +275 -275
  13. package/assets/charts.min.js +7 -7
  14. package/assets/corporate_report/css/report.css +582 -582
  15. package/assets/corporate_report/css/report_rtl.css +144 -144
  16. package/assets/corporate_report/js/js.js +14 -14
  17. package/assets/data/data.json +124 -124
  18. package/assets/data/data2.json +675 -675
  19. package/assets/data/pdf_data_with_history.json +1255 -1255
  20. package/assets/doctor-note-insight-icon.svg +11 -11
  21. package/assets/imc/css/style.css +61 -61
  22. package/assets/jquery-1.4.min.js +151 -151
  23. package/assets/jquery-2.1.0.min.js +3 -3
  24. package/assets/js.js +201 -201
  25. package/assets/medicus_pdf/arabic.css +444 -444
  26. package/assets/medicus_pdf/styles.css +2427 -2427
  27. package/assets/print.css +4 -4
  28. package/assets/qr-code-style.css +68 -68
  29. package/assets/qrcode.min.js +3 -3
  30. package/assets/raphael-min.js +10 -10
  31. package/assets/sanusx/css/sanusx_report.css +707 -707
  32. package/assets/sizing.js +2 -2
  33. package/assets/styles.css +1545 -1545
  34. package/assets/styles.min.css +1358 -1358
  35. package/assets/translation.js +30 -30
  36. package/assets/wellbeing/css/nasco_report.css +3472 -3472
  37. package/assets/wellbeing/css/nasco_report_rtl.css +679 -679
  38. package/assets/wellbeing/js/js.js +111 -111
  39. package/assets/wellbeing/js/raphael.js +8437 -8437
  40. package/base64/cairo-font.js +2 -2
  41. package/config/Mediclinic.json +37 -37
  42. package/config/Najeeb.ai.json +35 -35
  43. package/config/Pha.json +64 -64
  44. package/config/bionext.json +37 -37
  45. package/config/default.json +35 -35
  46. package/config/diagnostikare.json +34 -34
  47. package/config/maisonsante.json +39 -39
  48. package/config/nasco.json +35 -35
  49. package/config/sanitas.json +35 -35
  50. package/config/sanusx.json +34 -34
  51. package/config/test.txt +4 -4
  52. package/data converter.js +53 -53
  53. package/docs/01-architecture-overview.md +149 -0
  54. package/docs/02-configuration-and-whitelabelling.md +111 -0
  55. package/docs/03-templating-and-rendering.md +71 -0
  56. package/docs/04-report-pipelines.md +128 -0
  57. package/docs/05-questions-and-data-model.md +67 -0
  58. package/docs/06-internationalization.md +59 -0
  59. package/docs/07-email-notifications.md +59 -0
  60. package/docs/08-known-issues-and-technical-debt.md +41 -0
  61. package/docs/README.md +22 -0
  62. package/downloadfile.js +6 -6
  63. package/example.txt +1188 -1188
  64. package/index.js +481 -480
  65. package/lib/big_integral_questionnaire.js +370 -298
  66. package/lib/corporate_report_generator.js +399 -399
  67. package/lib/pdf_generator.min.js +525 -525
  68. package/lib/sanusx_report_generator.js +533 -533
  69. package/lib/sendEmail.js +343 -343
  70. package/lib/template.js +2227 -2227
  71. package/lib/wellbeing_report_generator.js +2164 -2134
  72. package/locales/ar-AE.json +169 -169
  73. package/locales/ar-SA.json +169 -169
  74. package/locales/ar.json +397 -397
  75. package/locales/de.json +169 -169
  76. package/locales/en.json +229 -229
  77. package/locales/fr.json +168 -168
  78. package/locales/pt.json +165 -165
  79. package/locales/wellbeing/ar-AE.json +149 -149
  80. package/locales/wellbeing/ar-SA.json +257 -257
  81. package/locales/wellbeing/de.json +301 -301
  82. package/locales/wellbeing/en.json +423 -423
  83. package/locales/wellbeing/es.json +133 -133
  84. package/locales/wellbeing/fr.json +121 -121
  85. package/locales/wellbeing/it-IT.json +121 -121
  86. package/locales/wellbeing/pt.json +121 -121
  87. package/locales/wellbeing/tr.json +121 -121
  88. package/locales/wellbeing/zh-CN.json +121 -121
  89. package/locales/zh-CN.json +165 -165
  90. package/package.json +47 -47
  91. package/preview-big-integral.js +75 -75
  92. package/run.js +15 -15
  93. package/templates/Mediclinic/blocks/first-section.html +205 -205
  94. package/templates/Mediclinic/blocks/footer.html +7 -7
  95. package/templates/Mediclinic/blocks/header-template.html +5 -5
  96. package/templates/Mediclinic/blocks/header.html +20 -20
  97. package/templates/Mediclinic/blocks/patient-table.html +38 -38
  98. package/templates/Mediclinic/blocks/pdf-header.html +9 -9
  99. package/templates/Mediclinic/blocks/tips.html +8 -8
  100. package/templates/Mediclinic/ltr_no_pages.html +37 -37
  101. package/templates/Mediclinic/rtl_no_pages.html +42 -42
  102. package/templates/Mediclinic/wellbeing_template.html +41 -41
  103. package/templates/Pha/blocks/first-section.html +207 -207
  104. package/templates/Pha/blocks/footer.html +7 -7
  105. package/templates/Pha/blocks/header-template.html +5 -5
  106. package/templates/Pha/blocks/header.html +20 -20
  107. package/templates/Pha/blocks/patient-table.html +38 -38
  108. package/templates/Pha/blocks/pdf-header.html +8 -8
  109. package/templates/Pha/blocks/tips.html +8 -8
  110. package/templates/Pha/ltr_no_pages.html +37 -37
  111. package/templates/Pha/rtl_no_pages.html +42 -42
  112. package/templates/Pha/wellbeing_template.html +41 -41
  113. package/templates/base.html +23 -23
  114. package/templates/blocks/ar-first-page-header.html +68 -68
  115. package/templates/blocks/ar-footer.html +54 -54
  116. package/templates/blocks/ar-header.html +67 -67
  117. package/templates/blocks/biomarker-compact-ar.html +27 -27
  118. package/templates/blocks/biomarker-compact.html +27 -27
  119. package/templates/blocks/biomarker-details.html +41 -41
  120. package/templates/blocks/biomarker-insight.html +22 -22
  121. package/templates/blocks/biomarker-min.html +9 -9
  122. package/templates/blocks/doctor-note.html +11 -11
  123. package/templates/blocks/footer.html +35 -35
  124. package/templates/blocks/header-first-page.html +46 -46
  125. package/templates/blocks/header.html +49 -49
  126. package/templates/blocks/panel-compact.html +38 -38
  127. package/templates/blocks/panel-details.html +16 -16
  128. package/templates/blocks/report-summary.html +16 -16
  129. package/templates/blocks/section-title.html +4 -4
  130. package/templates/blocks/signature.html +16 -16
  131. package/templates/blocks/summary-insight.html +15 -15
  132. package/templates/blocks/summary.html +9 -9
  133. package/templates/corporate_report/cover_page.html +23 -23
  134. package/templates/corporate_report/footer.html +9 -9
  135. package/templates/corporate_report/ltr_no_pages.html +20 -20
  136. package/templates/corporate_report/page.html +24 -24
  137. package/templates/corporate_report/participant_analytics.html +5 -5
  138. package/templates/corporate_report/rtl_no_pages.html +20 -20
  139. package/templates/empty.html +10 -10
  140. package/templates/first_page_head.html +157 -157
  141. package/templates/imc/first-header-template.html +53 -53
  142. package/templates/ltr.html +1368 -1368
  143. package/templates/ltr_no_pages.html +89 -89
  144. package/templates/maisonsante/blocks/first-section.html +151 -207
  145. package/templates/maisonsante/blocks/footer.html +7 -7
  146. package/templates/maisonsante/blocks/header-template.html +5 -5
  147. package/templates/maisonsante/blocks/header.html +20 -20
  148. package/templates/maisonsante/blocks/patient-table.html +38 -38
  149. package/templates/maisonsante/blocks/pdf-header.html +8 -8
  150. package/templates/maisonsante/blocks/tips.html +8 -8
  151. package/templates/maisonsante/ltr_no_pages.html +38 -38
  152. package/templates/maisonsante/rtl_no_pages.html +43 -43
  153. package/templates/maisonsante/wellbeing_template.html +41 -41
  154. package/templates/no_pages.html +1224 -1224
  155. package/templates/popup/popup-template.html +129 -129
  156. package/templates/rtl_no_pages.html +92 -92
  157. package/templates/sanusx/blocks/footer.html +6 -6
  158. package/templates/sanusx/blocks/header.html +20 -20
  159. package/templates/sanusx/blocks/personal-details.html +123 -123
  160. package/templates/sanusx/blocks/predictions-section.html +57 -57
  161. package/templates/sanusx/blocks/recommendations-section.html +6 -6
  162. package/templates/sanusx/blocks/super-power-section.html +29 -29
  163. package/templates/sanusx/blocks/tips.html +8 -8
  164. package/templates/sanusx/ltr_no_pages.html +26 -26
  165. package/templates/sanusx/rtl_no_pages.html +36 -36
  166. package/templates/template.html +1377 -1377
  167. package/templates/wellbeing/blocks/first-section.html +106 -106
  168. package/templates/wellbeing/blocks/footer.html +7 -7
  169. package/templates/wellbeing/blocks/header.html +37 -37
  170. package/templates/wellbeing/blocks/tips.html +8 -8
  171. package/templates/wellbeing/ltr_no_pages.html +35 -35
  172. package/templates/wellbeing/rtl_no_pages.html +36 -36
  173. package/templates/wellbeing/wellbeing_template.html +43 -43
  174. package/test.js +74 -74
  175. package/testing-reports/mediclinic/arabic-data.js +0 -439
  176. package/testing-reports/mediclinic/doctor-data.js +0 -523
  177. package/testing-reports/mediclinic/with-wellbeing.js +0 -415
  178. package/testing-reports/pha/notes-example.js +0 -397
package/lib/template.js CHANGED
@@ -1,2227 +1,2227 @@
1
- const LocaleService = require('../app/services/localeService.js');
2
- const i18n = require('../app/i18n_wellbeing.config.js');
3
- fetchFileContents
4
- const fs = require("fs");
5
- const path = require('path');
6
-
7
- let useMonospace = false;
8
- function setUseMonospace(val) { useMonospace = val; }
9
- const INDENTATION = {
10
- PANEL_TITLE: 12,
11
- BIOMARKER_DETAILS: 12,
12
- INSIGHT: 12,
13
- PAGE_WIDTH: 794
14
- };
15
- var localeService = new LocaleService(i18n);
16
-
17
-
18
- function renderHTMLTemplate(html, isRtl, data, isIMCLab, lang = 'en') {
19
- let dir = isRtl ? 'rtl' : 'ltr';
20
- let language = isRtl ? 'ar' : 'en';
21
- let IMCclass = isIMCLab ? 'imc-lab' : '';
22
- let styles = fetchFileContents(path.resolve(__dirname, '../assets/medicus_pdf/styles.css'));
23
- if (isRtl) {
24
- styles += fetchFileContents(path.resolve(__dirname, '../assets/medicus_pdf/arabic.css'));
25
- }
26
- let bodyClass = "";
27
- if (!data['approvalDate']) {
28
- bodyClass = "draft-bg";
29
- if (lang !== 'en' && ['ar', 'de', 'fr', 'nl-NL', 'pt', 'pt_BR', 'ar-SA'].includes(lang)) {
30
-
31
- bodyClass = "draft-bg-" + lang;
32
- }
33
- }
34
- if (data['labType'] === 18) {
35
- bodyClass += ' smart-donor';
36
- }
37
- const fs = require('fs');
38
- let content = `
39
- <div>
40
- <div id="page_template" class="all-pages ${IMCclass}">
41
-
42
- ${html}
43
- </div>
44
- </div>
45
- `;
46
-
47
- return content;
48
- }
49
-
50
- function returnIntroTemplate(data, isIMCLab) {
51
- let profileItemHTML = '';
52
- let classProfileItemContainer = "size4";
53
- let widthProfileItemContainer = '';
54
- let introWidth = '';
55
- if (isIMCLab || (!process.env.SHOW_QR_BOX || process.env.SHOW_QR_BOX === "false")) {
56
- classProfileItemContainer = '';
57
- widthProfileItemContainer = 'width:100%';
58
- introWidth = 'width:100%';
59
-
60
- let length = 100 / 3;
61
- let items = data['profileItems'];
62
- if (data['reportProperties'] && isIMCLab) {
63
- for (const [key, value] of Object.entries(data['reportProperties'])) {
64
- items.push({title: key, value: value});
65
- }
66
- }
67
-
68
- for (const item of items) {
69
- profileItemHTML += `<div style='width: ${length}%; padding: 10px 0' class='grid-col size2'>
70
- <div class='info-label'>${item.title}</div>
71
- <div class='info-value'>${item.value}</div>
72
- </div>`;
73
- }
74
- } else {
75
- let index = 0;
76
- while (index < data['profileItems'].length - 1) {
77
- let newDivId = 'sub' + index;
78
- profileItemHTML += `<div id="${newDivId}" class='grid-row'>`;
79
-
80
- for (let j = 0; j < 2; j++) {
81
- let valueId = newDivId + '-' + j;
82
- let profileItemValue = data['profileItems'][index]['value'];
83
- let classAttr = /[A-Za-z]/.test(profileItemValue) && /[0-9]/.test(profileItemValue) ? "ltr_dir" : "";
84
-
85
- profileItemHTML += `<div id='${valueId}' class='grid-col size2'>
86
- <div class='info-label'>${data['profileItems'][index]['title']}</div>
87
- <div class='info-value ${classAttr}'>${profileItemValue}</div>
88
- </div>`;
89
- index++;
90
- }
91
- profileItemHTML += `</div>`;
92
- }
93
-
94
- if (data['profileItems'].length % 2 !== 0) {
95
- let newDivId = 'sub' + index;
96
- let valueId = newDivId + '-0';
97
- let profileItemValue = data['profileItems'][index]['value'];
98
- let classAttr = /[A-Za-z]/.test(profileItemValue) && /[0-9]/.test(profileItemValue) ? "ltr_dir" : "";
99
-
100
- profileItemHTML += `<div id="${newDivId}" class='grid-row'>
101
- <div id='${valueId}' class='grid-col size2'>
102
- <div class='info-label'>${data['profileItems'][index]['title']}</div>
103
- <div class='info-value ${classAttr}'>${profileItemValue}</div>
104
- </div>
105
- </div>`;
106
- }
107
- }
108
-
109
- let moreLink = localeService.t('Access your detailed report online') + " ";
110
- moreLink = moreLink.replace("{link}", `<strong><a href='${data['moreLink']}' target='_blank'>${data['moreLink']}</a></strong>`);
111
-
112
- if (data['patientPin'] && data['showPIN'] && data['qrCode']) {
113
- moreLink += localeService.t("Scan this report’s QR code or enter this PIN code:") + "";
114
- } else if (data['patientPin'] && data['showPIN']) {
115
- moreLink += localeService.t("Enter this PIN code:") + "";
116
- } else if (data['qrCode']) {
117
- moreLink += localeService.t("Scan this report’s QR code:") + "";
118
- }
119
-
120
- let content = '';
121
- let pin = data['patientPin'] || '';
122
- let qrImage = data['qrCode'] || '';
123
- let displayPin = pin ? 'inline-block' : 'none';
124
- let displayQR = qrImage ? 'inline-block' : 'none';
125
- let reportTitle = data['labType'] === 18 ? 'Smart Report' : localeService.t('Smart Report');
126
-
127
- content += `<div class="intro" style="${introWidth}">
128
- <div class="grid-row">
129
- <div class="profile-items-container grid-col ${classProfileItemContainer}" style="${widthProfileItemContainer}">
130
- <div id="report-title-header-ltr" class="report-title">
131
- <div class="heading">${reportTitle}</div>
132
- </div>
133
- <div id="patient-container">${profileItemHTML}</div>
134
- </div>`;
135
-
136
- if (!isIMCLab && (process.env.SHOW_QR_BOX === "true" || process.env.SHOW_QR_BOX === true) && data['labType'] !== 18) {
137
- content += `<div class="plus-info-container grid-col size4">
138
- <div class="plus-info">
139
- <div class="grid-row">
140
- <div class="grid-col size3">
141
- <div id="more-link" class="description">${moreLink}
142
- <span id="pin" class="pin" style="display:${displayPin}">${pin}</span>
143
- </div>
144
- </div>
145
- <div class="grid-col size1 qr-container">
146
- <div id="qrcode" class="pin-qr" style="display:${displayQR}"><img src="${qrImage}"></div>
147
- </div>
148
- </div>
149
- </div>
150
- </div>`;
151
- }
152
-
153
- content += '</div></div>';
154
-
155
- return content;
156
- }
157
-
158
-
159
- function renderDoctorNotes(doctorNotes, bioIndentStyle = '') {
160
- let doctorNotesHTML = '';
161
- if (doctorNotes && doctorNotes.length > 0) {
162
- doctorNotes.forEach(note => {
163
- let noteId = 'doctor-note-' + Math.random().toString(36).substr(2, 9);
164
- doctorNotesHTML += `<div class="card-wrapper card-wrapper-note" style="${bioIndentStyle}">
165
- <div id="${noteId}" class="card">
166
- <div class="icon">
167
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
168
- <g fill="none" fill-rule="evenodd">
169
- <rect width="32" height="32" fill="" rx="16"/>
170
- <g fill="#295C42" fill-rule="nonzero">
171
- <path d="M20.613 23.8a.31.31 0 0 0 .217-.088l1.845-1.8a.296.296 0 0 0 .09-.212v-12c0-.166-.137-.3-.307-.3h-1.845a.304.304 0 0 0-.308.3c0 .166.138.3.308.3h1.537v11.576l-1.755 1.712a.295.295 0 0 0 0 .424.31.31 0 0 0 .218.088z"/>
172
- <path d="M11.387 10c.17 0 .308-.134.308-.3 0-.166-.138-.3-.308-.3H9.542a.304.304 0 0 0-.307.3v12.9c0 .662.552 1.2 1.23 1.2h8.918c.17 0 .307-.134.307-.3v-1.8c0-.136.137-.3.308-.3h1.23c.17 0 .307-.134.307-.3 0-.166-.137-.3-.307-.3h-1.23c-.492 0-.923.42-.923.9v1.5h-8.61a.608.608 0 0 1-.615-.6V10h1.537zM16 8.2c-.34 0-.615.27-.615.6 0 .33.276.6.615.6.34 0 .615-.27.615-.6 0-.33-.276-.6-.615-.6z"/>
173
- <path d="M22.458 8.2h-1.845a.304.304 0 0 0-.308.3c0 .166.138.3.308.3h1.845c.448 0 .922.463.922.9v13.8c0 .437-.474.9-.922.9H9.542c-.448 0-.922-.463-.922-.9V9.7c0-.437.474-.9.922-.9h1.845c.17 0 .308-.134.308-.3 0-.166-.138-.3-.308-.3H9.542c-.79 0-1.537.729-1.537 1.5v13.8c0 .771.747 1.5 1.537 1.5h12.916c.79 0 1.537-.729 1.537-1.5V9.7c0-.771-.747-1.5-1.537-1.5z"/>
174
- </g>
175
- </g>
176
- </svg>
177
- </div>
178
- <div class="text text-with-icon">
179
- <div class="from-doctor">${localeService.t('noteFromDoctor')} ${note.createdByName}</div>
180
- <div class="note-content">${note.content.replace(/\n/g, "<br/>")}</div>
181
- </div>
182
- </div>
183
- </div>`;
184
- });
185
- }
186
- return doctorNotesHTML;
187
- }
188
-
189
- function renderDoctorNotesCompactView(doctorNotes, bioIndentStyle = '') {
190
- let doctorNotesHTML = '';
191
- if (doctorNotes && doctorNotes.length > 0) {
192
- doctorNotes.forEach(note => {
193
- doctorNotesHTML += `<div class="note-content">${note.content}</div>`;
194
- });
195
- }
196
- return doctorNotesHTML;
197
- }
198
-
199
- function renderOneBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent) {
200
-
201
- let biomarkerDetailsHtml = '';
202
-
203
- if (biomarker.showInDetailsView === true) {
204
- const biomarkerCssId = "dtl-bio-" + "db" + biomarker.id;
205
- let customBiomarkerValueFontSize = '';
206
- const rawFormattedValue = biomarker.formattedValue || biomarker.lingualReading || '';
207
- const formattedValue = rawFormattedValue.replace(/,/g, "");
208
- if (formattedValue.length > 35) {
209
- customBiomarkerValueFontSize = 'font-size:12px;';
210
- }
211
- const biomarkerName = biomarker.name || '';
212
- const biomarkerStatus = biomarker.status || '';
213
- const insightsCount = biomarker.insightsCount || '';
214
- let CircleStyles = '';
215
- if (!isComponent) {
216
- CircleStyles = GetBiomarkerCircleStyle(biomarker.color, biomarkerName, biomarkerStatus, insightsCount, '', false);
217
- }
218
-
219
- // Indent biomarker details based on panel level
220
- let firstGridColClass = 'size5';
221
- let firstGridColstyle = '';
222
- let lastGridColClass = 'size3';
223
- let bioIndentStyle = '';
224
- let bioIndentNoteStyle = '';
225
- if (nestingLevel > 0) {
226
- // Replace classes
227
- firstGridColClass = 'size5';
228
- lastGridColClass = 'size3';
229
- const extraPadding = ((nestingLevel + 10 % 10) + 1);
230
- const levelNesting = extraPadding + nestingLevel * (INDENTATION.BIOMARKER_DETAILS || 0);
231
- const levelNestingNote = extraPadding + nestingLevel * (INDENTATION.BIOMARKER_DETAILS || 0) + 16;
232
- bioIndentStyle = 'padding-left: ' + levelNesting + 'px;';
233
- bioIndentNoteStyle = 'padding-left: ' + levelNestingNote + 'px';
234
- }
235
-
236
- // IMC special case (large biomarker value)
237
- let biomarkerClass = '';
238
- if (rawFormattedValue.length >= 125) {
239
- biomarkerClass = 'large-value-class';
240
- firstGridColClass = '';
241
- // firstGridColstyle = 'width:100%';
242
- }
243
-
244
- const biomarkerValue = rawFormattedValue.replace(/\r\n/g, "<br/>");
245
-
246
- if (!biomarker.showScale) {
247
- biomarkerClass += ' hide-scale ';
248
- }
249
-
250
- const biomarkerColor = (biomarker.color && !biomarker.isNormal) ? "color:" + biomarker.color + ";" : "";
251
- let rangesPlaceholder = "";
252
- let currentRange = "";
253
- let currentPercent = 50;
254
- let biomarkerTitleClass = '';
255
- if (biomarker.ranges.length > 1 && biomarker.showScale) {
256
- biomarkerTitleClass = 'has-ranges';
257
- } else {
258
- biomarkerTitleClass = 'has-no-ranges';
259
- }
260
-
261
- let childRangesPlaceholder = "";
262
- let childCurrentRange = "";
263
- let heightCount = 1;
264
- let bioValue = "";
265
- let bioUnit = "";
266
- let afterBioInfoValue = '';
267
- let History = '';
268
- if (biomarker.childrenBiomarkers && biomarker.childrenBiomarkers.length > 0) {
269
- biomarker.childrenBiomarkers.forEach(bio => {
270
- if (bio.ranges.length > 1) {
271
- heightCount++;
272
- // Viewing the biomarker and unit of children after the parent ones
273
- bioValue = bio.formattedValue.replace(/\n/g, "<br />");
274
- let bioValue_style = "";
275
- if (bioValue.length > 33) {
276
- bioValue_style = "font-size:12px;";
277
- }
278
- bioUnit = bio.unit;
279
- childCurrentRange = '';
280
- // Generating the children ranges and display them under the parent ones
281
- childRangesPlaceholder += '<div class="extra-range-placeholder">'
282
- + convertToRangeLayout(bio.ranges, bio, isRtl)
283
- + '</div>';
284
- biomarkerTitleClass = 'has-ranges';
285
- afterBioInfoValue += '<div class="colorable pair">'
286
- + '<span class="biomarker-extra-value" style="' + bioValue_style + '">' + bioValue + '</span>'
287
- + '<span class="biomarker-extra-unit">' + bioUnit + '</span>'
288
- + '</div>';
289
- } else {
290
- biomarkerTitleClass = 'has-no-ranges';
291
- }
292
- History = renderBiomarkerHistoryChart(bio);
293
- });
294
- } else {
295
- History = renderBiomarkerHistoryChart(biomarker);
296
- }
297
-
298
- if (heightCount > 1) {
299
- biomarkerClass += 'hide-flag';
300
- }
301
-
302
- // Set biomarker history chart
303
- const biomarkerFullName = biomarker.fullName || '';
304
- let bioName = biomarkerFullName.replace(/\r\n/g, '');
305
- bioName = bioName.replace(/\n/g, '');
306
- const rangePlaceHolder = convertToRangeLayout(biomarker.ranges, biomarker, isRtl);
307
- let biomarkerSampleHTML = '';
308
- let biomInfoStyle = '';
309
- let biomValueAlignStyle = '';
310
- if (biomarker.sample) {
311
- biomarkerSampleHTML += '<div class="biom-sample">'
312
- + '<span class="biom-sample-value">'
313
- + biomarker.sample
314
- + '</span>'
315
- + '</div>';
316
- // biomValueAlignStyle = "vertical-align: bottom;";
317
- }
318
- if (!biomarker.sample && (rangePlaceHolder || childRangesPlaceholder || History)) {
319
- biomInfoStyle = "margin-top:15px;";
320
- }
321
-
322
- // Configurations
323
- let biomarkerScale = '';
324
- if (isChildBiomarker) {
325
- // biomarkerScale = 'transform:scale(.9);';
326
- }
327
- let biomCircleCss = '';
328
- if (isComponent) {
329
- biomCircleCss = 'opacity: 0;';
330
- biomarkerSampleHTML = '';
331
- bioName = '';
332
- }
333
- let panelNesting = '';
334
- let biomarkerTitleWidth = 'width:260px';
335
- if (nestingLevel > 0) {
336
- const extraPadding = ((nestingLevel + 10 % 10) + 1);
337
- const levelNesting = extraPadding + nestingLevel * (INDENTATION.PANEL_TITLE || 0);
338
- panelNesting = 'padding-left: ' + levelNesting + 'px;box-sizing:border-box;';
339
- const width = 260 - levelNesting;
340
- biomarkerTitleWidth = 'width:' + width + 'px';
341
- }
342
-
343
- if ((!rangePlaceHolder || !biomarker.showScale) && !childRangesPlaceholder && !History) {
344
- biomarkerScale += 'width:100%;';
345
- }
346
- const doctorNotesArr = biomarker.doctorNotes || [];
347
- const doctorNotes = renderDoctorNotes(doctorNotesArr, bioIndentNoteStyle);
348
- let bioNameStyle = '';
349
- let isValueNumber = !isNaN(Number(biomarkerValue));
350
- let valueDataType = isValueNumber ? "number" : "string";
351
- const safeBioName = bioName
352
- .replace(/\(/g, '&#x28;')
353
- .replace(/\)/g, '&#x29;');
354
- biomarkerDetailsHtml += '<div data-id="' + biomarkerCssId + '" class="biomarker-container grid-col ' + biomarkerClass + '">'
355
- + '<div class="grid-col ' + firstGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + panelNesting + '">'
356
- + '<div class="biomarker">'
357
- + '<div class="biom-circle" style="' + removeWhiteSpace(CircleStyles) + ';' + biomCircleCss + '">' + biomarkerName + '</div>'
358
- + '<div class="biom-info ' + biomarkerTitleClass + '" style="' + biomInfoStyle + '">'
359
- + '<div class="title ' + biomarkerTitleClass + '" style="' + biomarkerTitleWidth + '">' + biomarkerSampleHTML + '<span class="bio-name-info b" dir="ltr" style="unicode-bidi:isolate;">' + safeBioName + '</span></div>'
360
- + '<div class="value colorable" style="' + biomValueAlignStyle + '">'
361
- + biomarkerSampleHTML
362
- + '<div class="pair parent-pair">'
363
- + '<span class="value-text" data-type="' + valueDataType + '">'
364
- + '<span style="' + customBiomarkerValueFontSize + biomarkerColor + '">' + biomarkerValue + '</span>'
365
- + '</span>'
366
- + '<span dir="ltr" class="unit colorable">' + biomarker.unit + '</span>'
367
- + '</div>'
368
- + '</div>'
369
- + afterBioInfoValue
370
- + '</div>'
371
- + '</div>'
372
- + '</div>';
373
-
374
- if (rangePlaceHolder || childRangesPlaceholder || History) {
375
- biomarkerDetailsHtml += '<div class="grid-col ' + lastGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + '">';
376
- let biomBarClass = "";
377
- if (biomarker.ranges.length > 6) {
378
- biomBarClass = "big";
379
- }
380
-
381
- biomarkerDetailsHtml += '<div class="biom-bar ' + biomBarClass + '">';
382
- }
383
- if (rangePlaceHolder && biomarker.showScale) {
384
- biomarkerDetailsHtml += '<div class="range-placeholder c">' + convertToRangeLayout(biomarker.ranges, biomarker, isRtl) + '</div>';
385
- }
386
-
387
- if (childRangesPlaceholder) {
388
- biomarkerDetailsHtml += childRangesPlaceholder;
389
- }
390
- if (rangePlaceHolder || childRangesPlaceholder || History) {
391
- biomarkerDetailsHtml += '</div><!-- close biom-bar -->';
392
- // History chart start
393
- biomarkerDetailsHtml += '<div class="grid-col history-chart-col">';
394
-
395
- if (History) {
396
- biomarkerDetailsHtml += '<div class="chart">'
397
- + History
398
- + '</div>';
399
- }
400
-
401
- biomarkerDetailsHtml += '</div> <!-- end chart grid col -->';
402
- // History chart end
403
- biomarkerDetailsHtml += '</div> <!-- close last grid-col -->';
404
- }
405
- if (doctorNotes) {
406
- biomarkerDetailsHtml += '<div class="grid-row biomarker-insights b">'
407
- + doctorNotes
408
- + '</div>';
409
- }
410
-
411
- biomarkerDetailsHtml += '</div> <!-- end first close biomarker-container-->';
412
- }
413
- return biomarkerDetailsHtml;
414
- }
415
-
416
- function renderBiomarkerDetails(panel, isRtl, counter) {
417
- let content = '';
418
- panel.biomarkers.forEach((biomarker, biomarkrCounter) => {
419
- content += renderOneBiomarker(biomarker, biomarkrCounter, panel, isRtl, counter);
420
- });
421
- return content;
422
- }
423
-
424
- function renderPanelTitle(panel, counter, biomarkrCounter) {
425
- let panelTitle = '';
426
- let panelSubTitle = '';
427
- let content = '';
428
- let checkShowInDetailsView = panel.biomarkers.find(biomarker => biomarker.showInDetailsView === true);
429
-
430
- if (panel.biomarkers && checkShowInDetailsView && biomarkrCounter === 0) {
431
- if (panel.panelDetails && panel.panelDetails.length > 1) {
432
- panelTitle = panel.panelDetails[0].name;
433
- let newPanelDetails = panel.panelDetails;
434
-
435
- if (newPanelDetails.length > 0) {
436
- newPanelDetails.forEach((panelDetail, index) => {
437
- if (index > 0) {
438
- let marginLeft = (index + 1) * 10;
439
- let name = panelDetail.name ? panelDetail.name : '';
440
- panelSubTitle += `<div class="panel-subtitle-item" style="padding-left:${marginLeft}px">${name}</div>`;
441
- }
442
- });
443
- }
444
- } else {
445
- panelTitle = panel.panelDetails && panel.panelDetails[0] && panel.panelDetails[0].name ? panel.panelDetails[0].name : panel.name;
446
- }
447
-
448
- let detailsTitle = '';
449
- if (counter === 0) {
450
- detailsTitle = `<div id="details-section" class="section">
451
- <div class="resume-title">${localeService.t('Details')}</div>
452
- </div>`;
453
- }
454
-
455
- content += `${detailsTitle}
456
- <div class="panel-title">${panelTitle}</div>
457
- <div class="panel-subtitle">${panelSubTitle}</div>`;
458
- }
459
- return content;
460
- }
461
-
462
- function renderPanelDetails(panel, biomarkerDetailsHtml, counter, isRtl) {
463
- const panelCssId = `dtl-pnl-dp${panel.panelId.toLowerCase()}`;
464
- const checkShowInDetailsView = panel.biomarkers.find(biomarker => biomarker.showInDetailsView === true);
465
- let detailsSectionHTML = '';
466
- let panelSubTitle = '';
467
-
468
- if (panel.biomarkers && checkShowInDetailsView) {
469
- let panelTitle = panel.panelDetails && panel.panelDetails.length > 1 ? panel.panelDetails[0].name : panel.name;
470
-
471
- if (panel.panelDetails && panel.panelDetails.length > 1) {
472
- panel.panelDetails.forEach((panelDetail, index) => {
473
- if (index > 0) {
474
- let marginLeft = (index + 1) * 10;
475
- let name = panelDetail.name ? panelDetail.name : '';
476
- panelSubTitle += `<div class="panel-subtitle-item" style="padding-left:${marginLeft}px">${name}</div>`;
477
- }
478
- });
479
- }
480
-
481
- let detailsTitle = '';
482
- if (counter === 0) {
483
- detailsTitle = `<div id="details-section" class="section">
484
- <div class="resume-title">${localeService.t('Details')}</div>
485
- </div>`;
486
- }
487
-
488
- let panelInsights = renderRelatedInsights(panel.insights, panel, true, `dtl-pnl-insight-${panel.panelId}`, isRtl);
489
- let doctorNotes = renderDoctorNotes(panel.doctorNotes);
490
- let bioInsights = (doctorNotes || panelInsights) ? `<div class="grid-row biomarker-insights">${doctorNotes}${panelInsights}</div>` : '';
491
-
492
- detailsSectionHTML += `<div id="${panelCssId}" class="section panel sectionPanel">
493
- <div class="panel-wrapper">
494
- <div class="section-content">
495
- <div class="panel-container">${biomarkerDetailsHtml}</div>
496
- ${bioInsights}
497
- </div>
498
- </div>
499
- </div>`;
500
- }
501
- return detailsSectionHTML;
502
- }
503
-
504
- function renderOneInsight(insight, nestingLevel, isRtl) {
505
- const INDENTATION = {INSIGHT: 10}; // Assuming this is defined globally
506
- let insightHtml = '';
507
- const insightCssId = "insight-" + "cb" + insight.id + Math.floor(Math.random() * 3);
508
- let insightClass = "";
509
- let content = '';
510
- let tolerance = "";
511
- let introductoryStatement = "";
512
- let nodes = "";
513
- let doctorContentClass = "hide-me";
514
- let insightImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAMAAAAjXV6yAAAAdVBMVEUAAACAma+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama/V/8ZIAAAAJnRSTlMATE389dtK6SQE7uTUOc/HlysO3qeJbwnBf1kzaBwWuZ+zYEJ1UwEot4sAAATfSURBVHja7NprcqpAEIbhDwckclPu4iUajb3/JZ78MDWQ1CmhKtKN9LOEt2bGGWwopZRSSimllFJKKaWUUkoppZRSSimllFJKKfVCFs+2vMZxelxhohx6NuMGfh4lp80trTE9Do3G5MnZecfEODQqNyqW01pHDo0tWB+OmA6HxmeSz+nsNIc4mOQwlY3mEA/3tMckOMQlbzJMgENs3I83yGcDbZxn2VXNeRv59EuyhHg20AJPlcXVh2eoKzxAuhECWWm1dqnDb6Q/0kYJZNW7bTdRUEK2kQJZ9SGiNreUvYZGC2QdC5dagk9IxhAIdelTi7+DYByBgItHLZ7kSzVPINx8alkLft4zBcI+IssUcg9qrkBYhGQFco8htkCoArISsZuML9CqdMk6S91kfIGQbckKY8jEGAjXkKwPoUuIMxA2ZPlCP32wBkpzsk4ylxBrIGzkn0K8gWKfrDMk4g304ZEVyD2G2AKhCshKxG4yvkCr0iXrLHWT8QVCtiUrjCETYyBcQ7I+hC4hzkDYkOUL/fTBGijNyTrJXEKsgbCRfwrxBop9ss6QiDfQh0dWIPcYYguEKiArEbvJ+AKtSpess9RNxhcI2ZasMIZMjIFwDcn6ELqEOANhQ5Yv9NMHa6A0J+skcwmxBsJG/inEGyj2yTpDIt5AHx5ZgdxjiC0QqoCsROwm4wu0Kl2yzlI3GV8gZFuywhgyMQbCNSTrQ+gS4gyEDVm+0E8frIHSnKyTzCXEGggb+acQb6DYJ+sMiXgDfXhkBXKPIbZAqAKyErGbjC/QqnTJOkvdZHyBkG3JCmPIxBgI15CsD6FLiDMQNmT5Qj99sAZKc7JOMpcQayBs5J9CvIFin6wzJOIN9OGRFcg9htgCoQrISsRuMr5Aq9Il6yx1k/EFQrYlK4whE2MgXEOyPoQuIc5A2JDlC/30wRoozck6yVxCrIGwkX8K8QaKfbLOkIg30IdHViD3GGILhCogKxG7yfgCrUqXrLPUTcYXCNmWrDCGTIyBcA3J+hC6hDgDYUOWL/TTB2ugNCfrJHMJsQbCRv4pxBso9sk6QyLeQB8eWYHcY4gtEKqArETsJuMLtCpdss5SNxlfIGRbssIYMjEGwjUk60PoEuIMhA1ZvtBPH6yB0pysk8wlxBoIG/mnEG+g2CfrDIl4A314ZAVyjyG2QKgCshKxm4wv0Kp0yTpL3WR8gZBtyQpjyMQYCNeQrA+hS4gzEDZk+UI/fbAGSnOyTjKXEGsgbOSfQryBYp+sMyTiDfThkRXIPYbYAqEKyErEbjK+QKvSJessdZPxBUK2JSuMIRNjIFxDsj6ELiHOQNiQ5Qv99MEaKM3JOsn+3yX0DgAAAAAAAAAAAPi3Dx06cIqm05GlAAAAAElFTkSuQmCC";
515
- let insightTextClass = "";
516
- let relatedBiomarkers = "";
517
-
518
- if (insight.title) {
519
- content += `<div class='title'>${insight.title}</div>`;
520
- }
521
-
522
- if (insight.introductoryStatement) {
523
- introductoryStatement = insight.introductoryStatement + " ";
524
- } else {
525
- introductoryStatement = insight.content;
526
- }
527
-
528
- let insightCssIdStyle = '';
529
- if (nestingLevel > 0) {
530
- const extraPadding = ((nestingLevel + 10 % 10) + 1)
531
- const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT;
532
- insightCssIdStyle = `padding-left: ${levelNesting}px`;
533
- }
534
-
535
- function renderOneInsight(insight, nestingLevel, isRtl) {
536
- const INDENTATION = {INSIGHT: 10}; // Assuming this is defined globally
537
- let insightHtml = '';
538
- const insightCssId = "insight-" + "cb" + insight.id.toLowerCase() + Math.floor(Math.random() * 3);
539
- let insightClass = "";
540
- let content = '';
541
- let tolerance = "";
542
- let introductoryStatement = "";
543
- let nodes = "";
544
- let doctorContentClass = "hide-me";
545
- let insightImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAMAAAAjXV6yAAAAdVBMVEUAAACAma+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama/V/8ZIAAAAJnRSTlMATE389dtK6SQE7uTUOc/HlysO3qeJbwnBf1kzaBwWuZ+zYEJ1UwEot4sAAATfSURBVHja7NprcqpAEIbhDwckclPu4iUajb3/JZ78MDWQ1CmhKtKN9LOEt2bGGWwopZRSSimllFJKKaWUUkoppZRSSimllFJKKfVCFs+2vMZxelxhohx6NuMGfh4lp80trTE9Do3G5MnZecfEODQqNyqW01pHDo0tWB+OmA6HxmeSz+nsNIc4mOQwlY3mEA/3tMckOMQlbzJMgENs3I83yGcDbZxn2VXNeRv59EuyhHg20AJPlcXVh2eoKzxAuhECWWm1dqnDb6Q/0kYJZNW7bTdRUEK2kQJZ9SGiNreUvYZGC2QdC5dagk9IxhAIdelTi7+DYByBgItHLZ7kSzVPINx8alkLft4zBcI+IssUcg9qrkBYhGQFco8htkCoArISsZuML9CqdMk6S91kfIGQbckKY8jEGAjXkKwPoUuIMxA2ZPlCP32wBkpzsk4ylxBrIGzkn0K8gWKfrDMk4g304ZEVyD2G2AKhCshKxG4yvkCr0iXrLHWT8QVCtiUrjCETYyBcQ7I+hC4hzkDYkOUL/fTBGijNyTrJXEKsgbCRfwrxBop9ss6QiDfQh0dWIPcYYguEKiArEbvJ+AKtSpess9RNxhcI2ZasMIZMjIFwDcn6ELqEOANhQ5Yv9NMHa6A0J+skcwmxBsJG/inEGyj2yTpDIt5AHx5ZgdxjiC0QqoCsROwm4wu0Kl2yzlI3GV8gZFuywhgyMQbCNSTrQ+gS4gyEDVm+0E8frIHSnKyTzCXEGggb+acQb6DYJ+sMiXgDfXhkBXKPIbZAqAKyErGbjC/QqnTJOkvdZHyBkG3JCmPIxBgI15CsD6FLiDMQNmT5Qj99sAZKc7JOMpcQayBs5J9CvIFin6wzJOIN9OGRFcg9htgCoQrISsRuMr5Aq9Il6yx1k/EFQrYlK4whE2MgXEOyPoQuIc5A2JDlC/30wRoozck6yVxCrIGwkX8K8QaKfbLOkIg30IdHViD3GGILhCogKxG7yfgCrUqXrLPUTcYXCNmWrDCGTIyBcA3J+hC6hDgDYUOWL/TTB2ugNCfrJHMJsQbCRv4pxBso9sk6QyLeQB8eWYHcY4gtEKqArETsJuMLtCpdss5SNxlfIGRbssIYMjEGwjUk60PoEuIMhA1ZvtBPH6yB0pysk8wlxBoIG/mnEG+g2CfrDIl4A314ZAVyjyG2QKgCshKxm4wv0Kp0yTpL3WR8gZBtyQpjyMQYCNeQrA+hS4gzEDZk+UI/fbAGSnOyTjKXEGsgbOSfQryBYp+sMyTiDfThkRXIPYbYAqEKyErEbjK+QKvSJessdZPxBUK2JSuMIRNjIFxDsj6ELiHOQNiQ5Qv99MEaKM3JOsn+3yX0DgAAAAAAAAAAAPi3Dx06cIqm05GlAAAAAElFTkSuQmCC";
546
- let insightTextClass = "";
547
- let relatedBiomarkers = "";
548
-
549
- if (insight.title) {
550
- content += `<div class='title'>${insight.title}</div>`;
551
- }
552
-
553
- if (insight.introductoryStatement) {
554
- introductoryStatement = insight.introductoryStatement + " ";
555
- } else {
556
- introductoryStatement = insight.content;
557
- }
558
-
559
- let insightCssIdStyle = '';
560
- if (nestingLevel > 0) {
561
- const extraPadding = ((nestingLevel + 10 % 10) + 1)
562
- const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT;
563
- insightCssIdStyle = `padding-left: ${levelNesting}px`;
564
- }
565
-
566
- if (insight.introductoryStatement || insight.summary || insight.testExplained || insight.suggestion) {
567
- const introductoryStatementContent = introductoryStatement ? introductoryStatement.replace(/(\r\n|\n|\r)/gm, '') : '';
568
- const insightSummaryContent = insight.summary ? insight.summary : '';
569
- const testExplainedContent = insight.testExplained ? insight.testExplained : '';
570
- const suggestionContent = insight.suggestion ? insight.suggestion : '';
571
- content += `<div class="html-content inline-text introductory-statement">${introductoryStatementContent}</div>`;
572
- content += `<div class="html-content inline-text testExplained">${testExplainedContent}</div>`;
573
- content += `<div class="html-content inline-text">${insightSummaryContent}</div>`;
574
- content += `<div class="html-content">${suggestionContent}</div>`;
575
-
576
- if (isRtl) {
577
- content = content.replace(/&rlm;/g, '');
578
- content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
579
- content = content.replace(/\)/g, ') &rlm;</span>');
580
- }
581
- } else {
582
- content += introductoryStatement;
583
- if (isRtl) {
584
- content = content.replace(/&rlm;/g, '');
585
- content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
586
- content = content.replace(/\)/g, ') &rlm;</span>');
587
- }
588
- }
589
-
590
- if (insight.detailedList) {
591
- const parsedTable = parseTable(insight.detailedList);
592
- nodes = renderDetails(parsedTable, isRtl);
593
- doctorContentClass = "";
594
- }
595
-
596
- if (introductoryStatement) {
597
- const span = "<span>";
598
- if (introductoryStatement.includes(span)) {
599
- tolerance = stripTags(introductoryStatement, '<span>');
600
- introductoryStatement = introductoryStatement.replace(/<(.|\n)*?>/g, '');
601
- introductoryStatement = introductoryStatement.replace(tolerance, '');
602
- }
603
- }
604
-
605
- if (insight.color === "") {
606
- insightClass = "tip";
607
- } else {
608
- insightClass = color(insight.color);
609
- }
610
-
611
- // if (insight.icon !== "") {
612
- // insightImage = getFileAsBase64(insight.icon, 'image/png');
613
- // }
614
-
615
- if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
616
- insightTextClass = "insight-text";
617
- // Loop through relatedBiomarkers and generate HTML (commented out in PHP)
618
- }
619
-
620
- let insightCssIdCss = '';
621
- if (nestingLevel > 0) {
622
- const extraPadding = ((nestingLevel + 10 % 10) + 1)
623
- const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT + 16;
624
- insightCssIdCss = `padding-left: ${levelNesting}px`;
625
- }
626
-
627
- let svgColor = color(insight.color);
628
- if (insight.isMedical) {
629
- insightClass += ' medical-insight';
630
- svgColor = ' medical-insight';
631
- }
632
-
633
- if (content || insight.disclaimer || insight.toleranceText) {
634
- insightHtml += `<div id="${insightCssId}" class="card-wrapper" style="${insightCssIdCss}" insight-id="${insight.id}">
635
- <div class="card insight-card a ${insightClass}">
636
- <div class="insight-container">`;
637
- if (insight.isMedical || insight.insightType === 'doctor-visit') {
638
- insightHtml += `<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" class="card-svg ${svgColor}">
639
- <path d="M17 0C15.8955 0 15 0.895447 15 2V15H2C0.895508 15 0 15.8954 0 17V35C0 36.1046 0.895508 37 2 37H15V50C15 51.1046 15.8955 52 17 52H35C36.1045 52 37 51.1046 37 50V37H50C51.1045 37 52 36.1046 52 35V17C52 15.8954 51.1045 15 50 15H37V2C37 0.895416 36.1045 0 35 0H17Z" fill="white"/>
640
- </svg>`;
641
- }
642
- insightHtml += `<div class="text ${insightTextClass}">${content}</div>`;
643
-
644
- if (insight.disclaimer) {
645
- insightHtml += `<div class="insight-disclaimer" style="padding: 10px 0px 0">${insight.disclaimer}</div>`;
646
- }
647
- if (insight.toleranceText) {
648
- insightHtml += `<div class="insight-tolerance" style="padding: 10px 0px 0">${insight.toleranceText}</div>`;
649
- }
650
- insightHtml += `</div>`;
651
- if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
652
- insightHtml += `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>`;
653
- }
654
- insightHtml += `</div></div>`;
655
- }
656
-
657
- if (nodes || tolerance) {
658
- insightHtml += `<div class="card-wrapper detailed-list-card-wrapper" style="${insightCssIdCss}">
659
- <div class="detailed-list-wrapper">
660
- <div class="detailed-list-title-wrapper">
661
- <div class="doctor-only-content-container ${doctorContentClass}">
662
- <h4 class="doctor-only-content-header">${getLocale('doctorContent')}</h4>
663
- <span class="doctor-only-content-subheader">${getLocale('mostCommonCausesOfTheObservedValue')}</span>
664
- </div>`;
665
- if (nodes) {
666
- insightHtml += `<ul class="detailed-list">${nodes}</ul>`;
667
- }
668
- if (tolerance) {
669
- insightHtml += `<div class="tolerance-text">${tolerance}</div>`;
670
- }
671
- insightHtml += `</div></div></div>`;
672
- }
673
-
674
- return insightHtml;
675
- }
676
-
677
- if (insight.introductoryStatement || insight.summary || insight.testExplained || insight.suggestion) {
678
- const introductoryStatementContent = introductoryStatement ? introductoryStatement.replace(/(\r\n|\n|\r)/gm, '') : '';
679
- const insightSummaryContent = insight.summary ? insight.summary : '';
680
- const testExplainedContent = insight.testExplained ? insight.testExplained : '';
681
- const suggestionContent = insight.suggestion ? insight.suggestion : '';
682
- content += `<div class="html-content inline-text introductory-statement">${introductoryStatementContent}</div>`;
683
- content += `<div class="html-content inline-text testExplained">${testExplainedContent}</div>`;
684
- content += `<div class="html-content inline-text">${insightSummaryContent}</div>`;
685
- content += `<div class="html-content">${suggestionContent}</div>`;
686
-
687
- if (isRtl) {
688
- content = content.replace(/&rlm;/g, '');
689
- content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
690
- content = content.replace(/\)/g, ') &rlm;</span>');
691
- }
692
- } else {
693
- content += introductoryStatement;
694
- if (isRtl) {
695
- content = content.replace(/&rlm;/g, '');
696
- content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
697
- content = content.replace(/\)/g, ') &rlm;</span>');
698
- }
699
- }
700
-
701
- if (insight.detailedList) {
702
- const parsedTable = parseTable(insight.detailedList);
703
- nodes = renderDetails(parsedTable, isRtl);
704
- doctorContentClass = "";
705
- }
706
-
707
- if (introductoryStatement) {
708
- const span = "<span>";
709
- if (introductoryStatement.includes(span)) {
710
- tolerance = stripTags(introductoryStatement, '<span>');
711
- introductoryStatement = introductoryStatement.replace(/<(.|\n)*?>/g, '');
712
- introductoryStatement = introductoryStatement.replace(tolerance, '');
713
- }
714
- }
715
-
716
- if (insight.color === "") {
717
- insightClass = "tip";
718
- } else {
719
- insightClass = color(insight.color);
720
- }
721
-
722
- // if (insight.icon !== "") {
723
- // insightImage = getFileAsBase64(insight.icon, 'image/png');
724
- // }
725
-
726
- if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
727
- insightTextClass = "insight-text";
728
- // Loop through relatedBiomarkers and generate HTML (commented out in PHP)
729
- }
730
-
731
- let insightCssIdCss = '';
732
- if (nestingLevel > 0) {
733
- const extraPadding = ((nestingLevel + 10 % 10) + 1)
734
- const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT + 16;
735
- insightCssIdCss = `padding-left: ${levelNesting}px`;
736
- }
737
-
738
- let svgColor = color(insight.color);
739
- if (insight.isMedical) {
740
- insightClass += ' medical-insight';
741
- svgColor = ' medical-insight';
742
- }
743
-
744
- if (content || insight.disclaimer || insight.toleranceText) {
745
- insightHtml += `<div id="${insightCssId}" class="card-wrapper" style="${insightCssIdCss}" insight-id="${insight.id}">
746
- <div class="card insight-card a ${insightClass}">
747
- <div class="insight-container">`;
748
- if (insight.isMedical || insight.insightType === 'doctor-visit') {
749
- insightHtml += `<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" class="card-svg ${svgColor}">
750
- <path d="M17 0C15.8955 0 15 0.895447 15 2V15H2C0.895508 15 0 15.8954 0 17V35C0 36.1046 0.895508 37 2 37H15V50C15 51.1046 15.8955 52 17 52H35C36.1045 52 37 51.1046 37 50V37H50C51.1045 37 52 36.1046 52 35V17C52 15.8954 51.1045 15 50 15H37V2C37 0.895416 36.1045 0 35 0H17Z" fill="white"/>
751
- </svg>`;
752
- }
753
- insightHtml += `<div class="text ${insightTextClass}">${content}</div>`;
754
-
755
- if (insight.disclaimer) {
756
- insightHtml += `<div class="insight-disclaimer" style="padding: 10px 0px 0">${insight.disclaimer}</div>`;
757
- }
758
- if (insight.toleranceText) {
759
- insightHtml += `<div class="insight-tolerance" style="padding: 10px 0px 0">${insight.toleranceText}</div>`;
760
- }
761
- insightHtml += `</div>`;
762
- if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
763
- insightHtml += `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>`;
764
- }
765
- insightHtml += `</div></div>`;
766
- }
767
-
768
- if (nodes || tolerance) {
769
- insightHtml += `<div class="card-wrapper detailed-list-card-wrapper" style="${insightCssIdCss}">
770
- <div class="detailed-list-wrapper">
771
- <div class="detailed-list-title-wrapper">
772
- <div class="doctor-only-content-container ${doctorContentClass}">
773
- <h4 class="doctor-only-content-header">${getLocale('doctorContent')}</h4>
774
- <span class="doctor-only-content-subheader">${getLocale('mostCommonCausesOfTheObservedValue')}</span>
775
- </div>`;
776
- if (nodes) {
777
- insightHtml += `<ul class="detailed-list">${nodes}</ul>`;
778
- }
779
- if (tolerance) {
780
- insightHtml += `<div class="tolerance-text">${tolerance}</div>`;
781
- }
782
- insightHtml += `</div></div></div>`;
783
- }
784
-
785
- return insightHtml;
786
- }
787
-
788
- function renderRelatedInsights(insights, panel, showRelatedBiomarkers = true, idPrefix = '', isRtl = false) {
789
- let content = '';
790
- if (insights.length > 0) {
791
- insights.forEach((insight, insightIndex) => {
792
- content += renderOneInsight(insight, insightIndex, idPrefix, panel, showRelatedBiomarkers, isRtl);
793
- });
794
- }
795
- return content;
796
- }
797
-
798
- function setSummaryStatInsightText(summary) {
799
- let insightsHasStackedArray = [];
800
- let temp2 = [...summary];
801
-
802
- summary.forEach((summaryCheckedItem, index) => {
803
- if (summaryCheckedItem.stackedInsight !== null) {
804
- let test = {};
805
-
806
- temp2.forEach((stackedElement, i) => {
807
- if (stackedElement && stackedElement.id === summaryCheckedItem.stackedInsight) {
808
- test.index = i;
809
- summaryCheckedItem.content += `<br><br>${stackedElement.content}`;
810
- test.elm = summaryCheckedItem;
811
- insightsHasStackedArray[index] = stackedElement;
812
- summary[test.index] = test.elm;
813
- summary.splice(i, 1);
814
- }
815
- });
816
- }
817
- });
818
- return summary;
819
- }
820
-
821
- function returnSummaryTemplate(summaryI, isRtl) {
822
- let summary = setSummaryStatInsightText(summaryI);
823
- let insightIndex = 0;
824
- let insightHtml = '';
825
-
826
- summary.forEach(insight => {
827
- let insightCssId = `smry-insight${insightIndex}`;
828
- let relatedBiomarkers = '';
829
- let insightCssClass = insight.isClinicalReading ? "cr" : insight.color === "" ? "tip" : color(insight.color);
830
-
831
- let insightText = insight.title ? `<div class="title">${insight.title}</div>` : '';
832
- let insightContent = insight.content;
833
-
834
- if (isRtl) {
835
- insightContent = insightContent.replace(/&rlm;/g, '')
836
- .replace(/\(/g, '&rlm; <span class="ltr-dir">(')
837
- .replace(/\)/g, ') &rlm;</span>');
838
- }
839
-
840
- insightText += insightContent.replace(/(\r\n|\n|\r)/gm, '<br/>');
841
-
842
- if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
843
- insight.relatedBiomarkers.forEach(item => {
844
- relatedBiomarkers += GetRelatedBiomarker(
845
- GetBiomarkerCircleStyle(item.color, item.name, item.status, item.insightsCount, 'nothing', true, item),
846
- item.name,
847
- item.fullName
848
- );
849
- });
850
- }
851
-
852
- let svgColor = color(insight.color);
853
- if (insight.isMedical) {
854
- insightCssClass += ' medical-insight';
855
- svgColor = ' medical-insight';
856
- }
857
-
858
- insightHtml += `<div id="${insightCssId}" class="card insight-card ${insightCssClass}">
859
- <div class="insight-container">
860
- <div class="text">${insightText}</div>
861
- ${insight.toleranceText ? `<div class="text" style="color: #FF3200; padding: 10px 0px 5px 0px;">${insight.toleranceText}</div>` : ''}
862
- </div>
863
- ${insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0 ? `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>` : ''}
864
- </div>`;
865
-
866
- insightIndex++;
867
- });
868
- return insightHtml;
869
- }
870
-
871
- function renderSummary(data, isRtl) {
872
- let content = '';
873
- let reportNotes = '';
874
- let summary = '';
875
-
876
- if ((data.summary) || (data.doctorNote && data.doctorNote.length > 0)) {
877
- if (data.doctorNote && data.doctorNote.length > 0) {
878
- reportNotes = renderDoctorNotes(data.doctorNote);
879
- }
880
- if (data.summary) {
881
- summary = returnSummaryTemplate(data.summary, isRtl);
882
- }
883
- if (reportNotes && summary)
884
- content += `
885
- <div id="summary" class="section">
886
- <div class="resume-title">${localeService.t('Summary')}</div>
887
- <div class="section-content report-notes">${reportNotes}${summary}</div>
888
- </div>
889
- `;
890
- }
891
- return content;
892
- }
893
-
894
- function renderReportSummary(data, isIMCLab) {
895
- let content = '';
896
- if (data.reportSummary || (data.reportProperties && !isIMCLab)) {
897
- content += '<br/><br/>';
898
- let reportProperties = '';
899
-
900
- content += '<div id="report-summary" class="section">';
901
- if (data.reportSummary) {
902
- content += `
903
- <div class="resume-title report-summary-title">${localeService.t('reportSummary')}</div>
904
- <div class="section-content report-summary-content">${data.reportSummary}</div>
905
- `;
906
- }
907
- /* add report properties */
908
- if (data.reportProperties && !isIMCLab) {
909
- for (const [key, value] of Object.entries(data.reportProperties)) {
910
- reportProperties += `<p>${key}: ${value}</p>`;
911
- }
912
- content += `
913
- <div class="resume-title report-properties-title">${localeService.t('reportProperties')}</div>
914
- <div class="section-content report-properties-content">${reportProperties}</div>
915
- `;
916
- }
917
- content += '</div>';
918
- }
919
-
920
- return content;
921
- }
922
-
923
- function renderSignature(data, isRtl, isIMCLab) {
924
- let content = '';
925
- let textAlign = isRtl ? 'right' : 'left';
926
-
927
- // Lab address
928
- let labAddress = '';
929
- if (data.labAddress && data.labType !== 18) {
930
- labAddress = data.labAddress.replace(/\n/g, "<br>");
931
- }
932
- content += `<td style="vertical-align: middle;"><div style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;word-break: break-all;">
933
- ${labAddress}
934
- </div></td>`;
935
-
936
- // Lab tel, email, and site
937
- let labPhone = data.labPhoneNumber ? `${localeService.t('Tel:')} ${data.labPhoneNumber}<br/>` : '';
938
- let labEmail = data.labEmailAddress ? `${localeService.t('Email:')} ${data.labEmailAddress}<br/>` : '';
939
- let labSite = data.moreLink ? `${data.moreLink}<br/>` : '';
940
-
941
- if (data.labType === 18) {
942
- labSite = "Web: http://www.smartdonor.it/<br/>";
943
- }
944
-
945
- if (labPhone || labEmail || labSite) {
946
- content += `<td style="vertical-align: middle;"><div style="width: 2px; height: 100%;background:rgba(157,169,180, 0.5);min-height: 70px ">&nbsp;</div></td>`;
947
- }
948
- content += `<td style="vertical-align: middle;"><div style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;">
949
- ${labPhone}
950
- ${labEmail}
951
- ${labSite}
952
- </div></td>`;
953
-
954
- if (data.labType !== 18 && !isIMCLab && (data.labStamp || (data.signatures && data.signatures.length > 0))) {
955
- content += `<td style="vertical-align: middle;"><div style="width: 2px; height: 100%;background:rgba(157,169,180, 0.5);min-height: 70px">&nbsp;</div></td>`;
956
- }
957
-
958
- if (data.labType !== 18 && data.signatures && data.signatures.length > 0) {
959
- data.signatures.forEach((signature, index) => {
960
- let approvedBy = signature.name;
961
- content += `<td style="vertical-align: middle;">
962
- <div class="approved-by-doctor" style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;">
963
- <div>${localeService.t('Report approved by')}</div>
964
- <div>${approvedBy}</div>
965
- <img class="signature-image" style="height: 50px;width: auto;" src="${signature.signatureURL}">
966
- </div>
967
- </td>`;
968
- });
969
- }
970
-
971
- if (data.labStamp && data.labType !== 18) {
972
- content += `<td><img class="stamp-image a" style="padding: 0 25px;align-self: center;justify-self: center;webkit-align-items:center" src="${data.labStamp}" width="100"></td>`;
973
- }
974
-
975
- if (data.labType === 18) {
976
- content += `<td><img class="stamp-image" style="float: right;padding: 0 25px;align-self: center;justify-self: center;webkit-align-items:center" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAADVCAYAAADtl80QAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAbXUAAHOgAAD83QAAg2QAAHDoAADsaAAAMD4AABCQ5OyZ6gAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAALNxJREFUeNrtXQeYVEW2np4ZZphhhiGIBAUZkkQJIohhwYyKWdeEiCKICUwYFhMgGFcFAwKKomDCgBl1FdEVFHFVQAQFBQWRIEpwhkndr477t+9u29NdVTfVvXP+7zvfvrfL3K7410l1KiODwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwdDD/wGlgtpU8FGP8wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNS0xMS0yMFQxMjo1MzoyOSswMTowMF975WEAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTUtMTEtMjBUMTI6NTM6MjkrMDE6MDAuJl3dAAAAAElFTkSuQmCC" width="100"></td>`;
977
- }
978
-
979
- return content;
980
- }
981
-
982
- function showDetailsView(data, isRtl) {
983
- let biomarkerDetailsHtml = '';
984
- let detailsSectionHTML = '';
985
- let shouldShowSectionTitle = false;
986
-
987
- data.panels.forEach((panel, counter) => {
988
- /* Biomarker details template */
989
- biomarkerDetailsHtml = renderBiomarkerDetails(panel, isRtl, counter);
990
-
991
- /* Panel details template */
992
- let checkShowInDetailsView = panel.biomarkers?.some(biomarker => biomarker.showInDetailsView);
993
-
994
- if (panel.biomarkers && checkShowInDetailsView) {
995
- shouldShowSectionTitle = true;
996
- detailsSectionHTML += renderPanelDetails(panel, biomarkerDetailsHtml, counter, isRtl);
997
- }
998
- });
999
-
1000
- return detailsSectionHTML;
1001
- }
1002
-
1003
- function renderReportItems(items, isCompactView = false, isRtl = false, isRectified = false, isAllergyItem = false, allergyClasses = [], forPatient = false) {
1004
- let content = '';
1005
- let counter = 0;
1006
- let itemCounter = 0;
1007
-
1008
- items.forEach((dashboardItem, index) => {
1009
- let nestingLevel = dashboardItem.nestingLevel;
1010
-
1011
- if (isAllergyItem) {
1012
- if (itemCounter === 0) {
1013
- itemCounter++;
1014
- return;
1015
- }
1016
- if (nestingLevel > 0) {
1017
- nestingLevel -= 1;
1018
- }
1019
- }
1020
-
1021
- let showInCompactView = dashboardItem.showInCompactView || false;
1022
- let showInDetailsView = dashboardItem.showInDetailsView || false;
1023
-
1024
- if (dashboardItem.biomarker || (dashboardItem.panel && dashboardItem.panel.panelBiomarker)) {
1025
- let bio = dashboardItem.biomarker || dashboardItem.panel.panelBiomarker;
1026
- counter++;
1027
- let indentationStyle = nestingLevel > 0 ? `margin-left:${nestingLevel * INDENTATION.BIOMARKER_DETAILS}px` : '';
1028
-
1029
- let biomarkerContent = isAllergyItem
1030
- ? renderAllergyBiomarker(bio, nestingLevel, dashboardItem.isChildBiomarker, isCompactView, false, isRtl, showInDetailsView, isRectified, allergyClasses, forPatient)
1031
- : renderBiomarkerV3(bio, nestingLevel, dashboardItem.isChildBiomarker, isCompactView, false, isRtl, showInDetailsView, isRectified);
1032
-
1033
- if (counter > 1 && !isCompactView && biomarkerContent) {
1034
- content += '<hr class="biomarker-container-line b">';
1035
- }
1036
- content += biomarkerContent;
1037
- } else if (dashboardItem.panel && !dashboardItem.panel.panelBiomarker && showInCompactView && isCompactView) {
1038
- content += renderPanelV3(dashboardItem.panel, nestingLevel, isCompactView, itemCounter, isRectified, isAllergyItem);
1039
- } else if (dashboardItem.panel && !dashboardItem.panel.panelBiomarker && showInDetailsView && !isCompactView) {
1040
- counter = 0;
1041
- content += renderPanelV3(dashboardItem.panel, nestingLevel, isCompactView, itemCounter, isRectified, isAllergyItem);
1042
- } else if (dashboardItem.insight && dashboardItem.showInDetailsView && !isCompactView) {
1043
- content += renderOneInsight(dashboardItem.insight, nestingLevel, isRtl);
1044
- } else if (dashboardItem.biomarkerAttachment) {
1045
- content += renderAttachmentsV3(dashboardItem.biomarkerAttachment, nestingLevel);
1046
- } else if (dashboardItem.biomarkerDetailsList) {
1047
- content += renderDetailsListV3(dashboardItem.biomarkerDetailsList, nestingLevel, isCompactView);
1048
- } else if (dashboardItem.biomarkerNote) {
1049
- content += renderNoteV3(dashboardItem.biomarkerNote, nestingLevel, isCompactView, showInCompactView, showInDetailsView);
1050
- }
1051
- itemCounter++;
1052
- });
1053
-
1054
- return content;
1055
- }
1056
-
1057
- function renderBiomarkerV3(biomarker, nestingLevel, isChildBiomarker, isCompactView, isComponent = false, isRtl = false, showInDetailsView = true, isRectified = false) {
1058
- let biomarkerCssId = `biomarker-${("db" + String(biomarker?.id ?? "")).toLowerCase()}${Math.floor(Math.random() * 3)}`;
1059
- let newBiomarkerHtml = '';
1060
- let biomarkerClass = '';
1061
- let biomarkerParentValueStyle = '';
1062
- let indentationStyle = '';
1063
-
1064
- if (isCompactView && biomarker.showInCompactView) {
1065
- let biomarkerName = biomarker.fullName || '';
1066
- let biomarkerUnit = biomarker.unit;
1067
- let biomarkerValue = biomarker.formattedValue || biomarker.lingualReading || '';
1068
- let rangePlaceholder = '';
1069
- let referenceRange = '';
1070
- let biomarkerLast = '-';
1071
- let biomarkerPast = '-';
1072
-
1073
- if (shouldRenderReadingLevels(biomarker)) {
1074
- rangePlaceholder = convertToMiniRangeLayout(biomarker.ranges, biomarker, isRtl);
1075
- }
1076
-
1077
- if (biomarker.showScale) {
1078
- let normalRangeObject = biomarker.ranges.find(range => range.isReference) || {};
1079
- let formattedNormalRangeMin = normalRangeObject.formattedMinValue || null;
1080
- let formattedNormalRangeMax = normalRangeObject.formattedMaxValue || null;
1081
- referenceRange = biomarker.ranges.length > 1 ? getRange(biomarker) : '';
1082
- }
1083
-
1084
- if (biomarker.color && !biomarker.isNormal) {
1085
- biomarkerClass = 'risk';
1086
- biomarkerParentValueStyle = `color:${biomarker.color};`;
1087
- }
1088
-
1089
- if (nestingLevel > 0) {
1090
- let extraPadding = ((nestingLevel + 10 % 10) + 1);
1091
- let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1092
- indentationStyle = `padding-left: ${levelNesting}px`;
1093
- }
1094
-
1095
- if (biomarker.history && biomarker.history.length > 1) {
1096
- let lastBiomarkerIndex = biomarker.history.length - 2;
1097
- biomarkerLast = biomarker.history[lastBiomarkerIndex]?.formattedValue || 0;
1098
- biomarkerPast = biomarker.history[lastBiomarkerIndex]?.formattedDate || '-';
1099
- }
1100
-
1101
- if (biomarkerValue.length > 35) {
1102
- biomarkerParentValueStyle += "font-size:12px;line-height:1";
1103
- }
1104
-
1105
- let firstTdWidth = '';
1106
- let tdWidth = '';
1107
- if (isCompactView) {
1108
- let fullPanelWidth = INDENTATION.PAGE_WIDTH;
1109
- let tdCount = 7;
1110
- let RectifiedTd = isRectified ? 32 : 0;
1111
- tdWidth = (fullPanelWidth - RectifiedTd) / tdCount;
1112
- firstTdWidth = `${tdWidth * 2}px`;
1113
- }
1114
-
1115
- let biomarkerNameAlign = isRtl ? 'text-align: right;' : '';
1116
- const RTL_FLAG = '\u200E';
1117
-
1118
- let isValueNumber = !isNaN(Number(biomarkerValue.replace(/\r\n/g, "<br/>")));
1119
- let valueDataType = isValueNumber ? "number" : "string";
1120
- let cleanBioMarkerUnit =
1121
- biomarkerUnit
1122
- .replace(/[\u200E\u200F\u202A-\u202E]/g, '')
1123
- .replace(/\(/g, '&#x28;')
1124
- .replace(/\)/g, '&#x29;');
1125
-
1126
- newBiomarkerHtml += `<table id="${biomarkerCssId}" style="width:100%; margin: 0 auto;" class="biomarker-block biomarker-block-row ${biomarkerClass}">
1127
- <tr>
1128
- <td style="width:${firstTdWidth}">
1129
- <div class="biom-title name" style="direction: ltr;${biomarkerNameAlign}${indentationStyle}">${biomarkerName}</div>
1130
- <div class="biom-method method"></div>
1131
- </td>
1132
- <td class="biom-value value" style="width:max-content;">
1133
- <span class="biom-parent-value value-text" data-type="${valueDataType}" style="${biomarkerParentValueStyle}">${biomarkerValue.replace(/\r\n/g, "<br/>")}</span>
1134
- </td>
1135
- ${biomarkerUnit ?
1136
- `<td class="biom-unit unit">
1137
- <div class="biom-parent-unit unit-text" style="padding: 0 6px; font-family: 'Open Sans', sans-serif !important; direction: ltr !important; unicode-bidi: isolate !important;" dir="ltr">
1138
- ${cleanBioMarkerUnit}
1139
- </div>
1140
- </td>` : ''}
1141
- ${rangePlaceholder ? `<td class="biom-bar listing" style="width:${tdWidth}px; margin: 0;"><div class="range-placeholder b">${rangePlaceholder}</div></td>` : ''}
1142
- <td class="biom-range biom-range-data" style="width:${tdWidth}px; margin: 0;"><div class="parent-reference-range range">${referenceRange}</div></td>
1143
- </tr>
1144
- </table>`;
1145
- }
1146
- // ${isRtl ?RTL_FLAG+cleanBioMarkerUnit:cleanBioMarkerUnit}
1147
- if (!isCompactView && showInDetailsView) {
1148
- newBiomarkerHtml += renderOneBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent);
1149
- }
1150
-
1151
- if (biomarker.components && biomarker.components.length > 0) {
1152
- biomarker.components.forEach(comp => {
1153
- comp.showInDetailsView = biomarker.showInDetailsView;
1154
- comp.showInCompactView = biomarker.showInCompactView;
1155
- comp.formattedNormalRangeMin = comp.min;
1156
- comp.formattedNormalRangeMax = comp.max;
1157
- newBiomarkerHtml += renderBiomarkerV3(comp, nestingLevel, isChildBiomarker, isCompactView, true, isRtl, showInDetailsView, isRectified);
1158
- });
1159
- }
1160
-
1161
- if ((isCompactView && biomarker.showInCompactView) || (!isCompactView && biomarker.showInDetailsView)) {
1162
- if (biomarker.notes && biomarker.notes.length > 0) {
1163
- let BioNoteStyle = '';
1164
- if (nestingLevel > 0) {
1165
- let extraPadding = ((nestingLevel + 10 % 10) + 1);
1166
- let paddingLeft = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + 8;
1167
- BioNoteStyle = isRtl ? `padding-right:${paddingLeft}px` : `padding-left:${paddingLeft}px`;
1168
- }
1169
- let notesPadding = isRtl ? 'padding: 0px 16px 10px 0px;' : 'padding: 0px 0px 10px 16px;';
1170
- let classNote = !isCompactView ? 'note-margin-bottom' : '';
1171
- if (useMonospace) {
1172
- const monoStyle = `font-style: normal; font-size: 13px; color: #333; font-family: 'Roboto Mono', monospace; white-space: pre-wrap; overflow-x: auto;`;
1173
- newBiomarkerHtml += `<div class="biomarker-notes 2 ${classNote}" style="${notesPadding} ${BioNoteStyle}"><span style="${monoStyle}">${biomarker.notes.join('<br/>')}</span></div>`;
1174
- } else {
1175
- newBiomarkerHtml += `<div class="biomarker-notes 2 ${classNote}" style="color: #8F8F92; ${notesPadding} ${BioNoteStyle}">${biomarker.notes.join('<br/>')}</div>`;
1176
- }
1177
- }
1178
- }
1179
-
1180
- return newBiomarkerHtml;
1181
- }
1182
-
1183
- function renderPanelV3(panel, nestingLevel, isCompactView, counter = null, isRectified = false, isAllergyItem = false) {
1184
-
1185
-
1186
- const panelCssId = `panel-${panel.panelId}db${Math.floor(Math.random() * 3)}`;
1187
- const notes = panel.notes ? panel.notes.join('') : '';
1188
- let classPanel = '';
1189
- if (panel.doctorNotes && isCompactView) {
1190
- classPanel = 'panel-with-notes';
1191
- }
1192
- let panelWidth = isCompactView ? 'width:687px' : '';
1193
- const colspan = isCompactView ? '1' : '4';
1194
- const doctorNotesCompactView = panel.doctorNotes && isCompactView
1195
- ? `<div class="section-content">${renderDoctorNotesCompactView(panel.doctorNotes, nestingLevel)}</div>`
1196
- : '';
1197
- const doctorNotesDetailsView = panel.doctorNotes && !isCompactView
1198
- ? `<div class="section-content-details-view">${renderDoctorNotes(panel.doctorNotes, nestingLevel)}</div>`
1199
- : '';
1200
- let newBiomarkerHtml = '';
1201
- let panelClass = '';
1202
- if (counter === 0 && !isCompactView) {
1203
- panelClass = "first-panel";
1204
- }
1205
-
1206
- newBiomarkerHtml += `<!--panel-->
1207
- <div id="${panelCssId}" class="section panel dontend ${panelClass}">`;
1208
-
1209
- let panelNesting = "margin-left: auto;";
1210
- let panelTitleFontSize = '';
1211
- if (nestingLevel > 0) {
1212
- const extraPadding = ((nestingLevel + 10 % 10) + 1);
1213
- const levelNesting = extraPadding + nestingLevel * INDENTATION.PANEL_TITLE;
1214
- panelWidth = INDENTATION.PAGE_WIDTH - levelNesting;
1215
- panelNesting = `margin-left: ${levelNesting}px;width: ${panelWidth}px;`;
1216
- panelTitleFontSize = 'font-size:18px';
1217
- }
1218
-
1219
- let firstTdWidth = '';
1220
- const RectifiedTd = isRectified ? 32 : 0;
1221
- let tdWidth = 0; // Declare tdWidth here to make it accessible outside the if block
1222
- if (isCompactView) {
1223
- const fullPanelWidth = INDENTATION.PAGE_WIDTH;
1224
- const tdCount = 7;
1225
-
1226
- tdWidth = (fullPanelWidth - RectifiedTd) / tdCount;
1227
- firstTdWidth = `${tdWidth * 2}px`;
1228
- }
1229
-
1230
- newBiomarkerHtml += `<div class="section-content-header ${classPanel}">
1231
- <div class="panel-title" style="box-sizing: border-box; ${panelNesting}">
1232
- <table style="width:100%; margin: 0 auto;" class="biomarker-block panel-section-header-new">
1233
- <tr>`;
1234
-
1235
- let allergyItemPanelClass = '';
1236
- if (isAllergyItem && isAllergyItem) {
1237
- allergyItemPanelClass = 'allergy-panel';
1238
- if (panel.icon) {
1239
- newBiomarkerHtml += `<td class="panel-icon-container">
1240
- <img src="${panel.icon}"/>
1241
- </td>`;
1242
- }
1243
- }
1244
-
1245
- newBiomarkerHtml += `<td colspan="${colspan}" style="width:${firstTdWidth}">
1246
- <div class="panel-title-text ${allergyItemPanelClass}" style="${panelTitleFontSize}">${panel.name}</div>
1247
- ${doctorNotesCompactView}`;
1248
-
1249
- if (notes) {
1250
- newBiomarkerHtml += `<div class="panel-notes">${notes}</div>`;
1251
- }
1252
-
1253
- newBiomarkerHtml += `</td>`;
1254
-
1255
- if (isCompactView) {
1256
- newBiomarkerHtml += `<td class="biom-range" style="width:${tdWidth}px; margin: 0;">
1257
- ${localeService.t('Reference range')}
1258
- </td>
1259
-
1260
- `;
1261
- if (isRectified) {
1262
- newBiomarkerHtml += `<td class="biom-past" style="width:${RectifiedTd}px"></td>`;
1263
- }
1264
- }
1265
-
1266
- newBiomarkerHtml += `</tr></table>
1267
- </div>
1268
- </div>
1269
- ${doctorNotesDetailsView}
1270
- <!--panel END-->`;
1271
-
1272
- return newBiomarkerHtml;
1273
- }
1274
-
1275
- function renderAttachmentsV3(attachment, nestingLevel) {
1276
- if (!attachment) return '';
1277
-
1278
- let blockStyle = '';
1279
- if (nestingLevel > 0) {
1280
- let extraPadding = ((nestingLevel + 10 % 10) + 1);
1281
- let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1282
- blockStyle = `padding-left: ${levelNesting}px`;
1283
- }
1284
-
1285
- let ext = attachment.split(';');
1286
- if (ext[0].toLowerCase() === "data:application/pdf") {
1287
- return '';
1288
- } else {
1289
- let pos = attachment.indexOf(';');
1290
- let type = attachment.substring(0, pos).split(';');
1291
- if (type[0].includes('svg')) {
1292
- return `<div style="${blockStyle}" class="biomarker-attachment-item">${attachment}</div>`;
1293
- } else {
1294
- return `<img ${nestingLevel} style="${blockStyle};max-width:320px" class="biomarker-attachment-item" src="${attachment}" />`;
1295
- }
1296
- }
1297
- }
1298
-
1299
- function renderDetailsListV3(detailsList, nestingLevel, isCompactView) {
1300
- let maxRowLength = -1;
1301
- let content = '';
1302
- let itemsCount = detailsList.map(item => item.length);
1303
- let colspanColumn = Math.max(...itemsCount);
1304
-
1305
- detailsList.forEach(row => {
1306
- content += "<tr>";
1307
- row.forEach(item => {
1308
- let colspan = row.length === 1 ? colspanColumn : 1;
1309
- content += `<td colspan="${colspan}">${item}</td>`;
1310
- });
1311
-
1312
- if (row.length > maxRowLength) {
1313
- maxRowLength = row.length;
1314
- }
1315
-
1316
- if (row.length < maxRowLength) {
1317
- content += (row.length < colspanColumn && row.length !== 1) ? fillEmptyCellsWith(maxRowLength - row.length) : '';
1318
- }
1319
- content += "</tr>";
1320
- });
1321
-
1322
- let blockStyle = '';
1323
- let plusPadding = isCompactView ? 8 : 18;
1324
- if (nestingLevel > 0) {
1325
- let extraPadding = ((nestingLevel + 10 % 10) + 1);
1326
- let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + plusPadding;
1327
- blockStyle = `padding-left: ${levelNesting}px`;
1328
- }
1329
-
1330
- return `<div style="${blockStyle}" class="biomarker-details-table" style="padding:0 ${plusPadding}px"><table>${content}</table></div>`;
1331
- }
1332
-
1333
- function renderNoteV3(note, nestingLevel, isCompactView, showInCompactView, showInDetailsView) {
1334
- // Standalone biomarkerNote items duplicate the note already rendered inside
1335
- // renderBiomarkerV3 (biomarker.notes), so they are intentionally suppressed
1336
- // here to avoid every note appearing twice. Monospace styling for PHA is
1337
- // handled where biomarker.notes is rendered.
1338
- return '';
1339
- }
1340
-
1341
- function renderAllergyClasses(allergyClasses, isRtl = false) {
1342
- let count = allergyClasses.length;
1343
- let tableFloat = isRtl ? 'float: left;' : 'float: right;';
1344
- let unitAlign = isRtl ? 'text-align: left;right: 0;' : 'text-align: right;left: 0;';
1345
- let content = `<div class="allergy-classes-container">
1346
- <span class="allergy-classes-unit" style="${unitAlign}">kU/L</span>
1347
- <table class="allergy-classes-table colored" width="100%" cellspacing="0" cellpadding="0" style="${tableFloat}">
1348
- <tr><td colspan="${count}" class="allergy-classes-table-title">${localeService.t('Allergy Reaction Strength')}</td></tr>`;
1349
-
1350
- let tr1 = '<tr>';
1351
- let tr2 = '<tr>';
1352
-
1353
- allergyClasses.forEach(classItem => {
1354
- let formattedMinValue = classItem.formattedMinValue;
1355
- let formattedMaxValue = classItem.formattedMaxValue;
1356
- let range = '';
1357
-
1358
- if (!formattedMinValue && formattedMaxValue) {
1359
- range = `< ${formattedMaxValue}`;
1360
- }
1361
- if (formattedMinValue && formattedMaxValue) {
1362
- range = `${formattedMinValue} - ${formattedMaxValue}`;
1363
- }
1364
- if (formattedMinValue && !formattedMaxValue) {
1365
- range = `> ${formattedMinValue}`;
1366
- }
1367
-
1368
- tr1 += `<td style="color:${classItem.color}">${classItem.className}</td>`;
1369
- tr2 += `<td style="color:${classItem.color}">${classItem.lingualReading}<br/>${range}</td>`;
1370
- });
1371
-
1372
- tr1 += '</tr>';
1373
- tr2 += '</tr>';
1374
-
1375
- content += tr1 + tr2 + '</table></div>';
1376
- return content;
1377
- }
1378
-
1379
- function renderAllergyBiomarker(biomarker, nestingLevel, isChildBiomarker, isCompactView, isComponent = false, isRtl = false, showInDetailsView = true, isRectified = false, allergyClasses = [], forPatient = true) {
1380
- let newBiomarkerHtml = '';
1381
-
1382
- if (!isCompactView && showInDetailsView) {
1383
- newBiomarkerHtml += renderOneAllergyBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent, allergyClasses, forPatient);
1384
- }
1385
-
1386
- // Render its values inside components element
1387
- if (biomarker.components && biomarker.components.length > 0) {
1388
- biomarker.components.forEach(comp => {
1389
- comp.showInDetailsView = biomarker.showInDetailsView;
1390
- comp.showInCompactView = biomarker.showInCompactView;
1391
- comp.formattedNormalRangeMin = comp.min;
1392
- comp.formattedNormalRangeMax = comp.max;
1393
- newBiomarkerHtml += renderAllergyBiomarker(comp, nestingLevel, isChildBiomarker, isCompactView, true, isRtl, showInDetailsView, isRectified, allergyClasses, forPatient);
1394
- });
1395
- }
1396
-
1397
- return newBiomarkerHtml;
1398
- }
1399
-
1400
- function renderOneAllergyBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent, allergyClasses, forPatient) {
1401
- const INDENTATION = {
1402
- BIOMARKER_DETAILS: 20, // Example value, replace with actual value
1403
- PANEL_TITLE: 10 // Example value, replace with actual value
1404
- };
1405
-
1406
- let biomarkerDetailsHtml = '';
1407
- if (biomarker.showInDetailsView === true) {
1408
- const biomarkerCssId = "dtl-bio-db" + String(biomarker?.id ?? "").toLowerCase();
1409
- let customBiomarkerValueFontSize = '';
1410
- const formattedValue = biomarker.formattedValue.replace(/,/g, "");
1411
- if (formattedValue.length > 35) {
1412
- customBiomarkerValueFontSize = 'font-size:12px;';
1413
- }
1414
- const biomarkerName = biomarker.name || '';
1415
- const biomarkerStatus = biomarker.status || '';
1416
- const insightsCount = biomarker.insightsCount || '';
1417
- let CircleStyles = '';
1418
- if (!isComponent) {
1419
- CircleStyles = GetBiomarkerCircleStyle(biomarker.color, biomarkerName, biomarkerStatus, insightsCount, '', false);
1420
- }
1421
-
1422
- // indent biomarker details based on panel level
1423
- let firstGridColClass = 'size5';
1424
- let bioIndentStyle = '';
1425
- let bioIndentNoteStyle = '';
1426
- if (nestingLevel > 0) {
1427
- // replace classes
1428
- // increase biomarker part
1429
- firstGridColClass = 'size5';
1430
- const extraPadding = ((nestingLevel + 10 % 10) + 1);
1431
- const levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1432
- const levelNestingNote = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + 16;
1433
- bioIndentStyle = 'padding-left: ' + levelNesting + 'px;';
1434
- bioIndentNoteStyle = 'padding-left: ' + levelNestingNote + 'px';
1435
- }
1436
-
1437
- // @note: IMC special case (large biomarker value)
1438
- let biomarkerClass = '';
1439
- if (biomarker.formattedValue.length >= 125) {
1440
- biomarkerClass = 'large-value-class';
1441
- firstGridColClass = '';
1442
- //$firstGridColstyle = 'width:100%';
1443
- }
1444
-
1445
- const biomarkerValue = biomarker.formattedValue.replace(/\r\n/g, "<br/>");
1446
-
1447
- if (!biomarker.showScale) {
1448
- biomarkerClass += ' hide-scale ';
1449
- }
1450
-
1451
- const biomarkerColor = biomarker.color && !biomarker.isNormal ? "color:" + biomarker.color + ";" : "";
1452
- let rangesPlaceholder = "";
1453
- let currentRange = "";
1454
- let currentPercent = 25;
1455
- let biomarkerTitleClass = '';
1456
- if (biomarker.ranges.length > 1) {
1457
- biomarkerTitleClass = 'has-ranges';
1458
- } else {
1459
- biomarkerTitleClass = 'has-no-ranges';
1460
- }
1461
-
1462
- let childRangesPlaceholder = "";
1463
- let childCurrentRange = "";
1464
- let heightCount = 1;
1465
- let bioValue = "";
1466
- let bioUnit = "";
1467
- let afterBioInfoValue = '';
1468
- let History = '';
1469
- if (biomarker.childrenBiomarkers && biomarker.childrenBiomarkers.length > 0) {
1470
- biomarker.childrenBiomarkers.forEach(bio => {
1471
- if (bio.ranges.length > 1) {
1472
- heightCount++;
1473
- /* viewing the biomarker and unit of children after the parent ones */
1474
- bioValue = bio.formattedValue.replace(/\n/g, "<br />");
1475
- let bioValue_style = "";
1476
- if (bioValue.length > 33) {
1477
- bioValue_style = "font-size:12px;";
1478
- }
1479
- bioUnit = bio.unit;
1480
- childCurrentRange = '';
1481
- /* generating the children ranges and display them under the parent ones */
1482
- childRangesPlaceholder += '<div class="extra-range-placeholder">'
1483
- + convertToRangeLayout(bio.ranges, bio, isRtl)
1484
- + '</div>';
1485
- biomarkerTitleClass = 'has-ranges';
1486
- afterBioInfoValue += '<div class="colorable pair">'
1487
- + '<span class="biomarker-extra-value" style="' + bioValue_style + '">' + bioValue + '</span>'
1488
- + '<span class="biomarker-extra-unit">' + bioUnit + '</span>'
1489
- + '</div>';
1490
- } else {
1491
- biomarkerTitleClass = 'has-no-ranges';
1492
- }
1493
- /*
1494
- $History = renderBiomarkerHistoryChart($bio);
1495
- */
1496
- });
1497
- } else {
1498
- /*
1499
- $History = renderBiomarkerHistoryChart($biomarker);
1500
- */
1501
- }
1502
-
1503
- if (heightCount > 1) {
1504
- biomarkerClass += 'hide-flag';
1505
- }
1506
-
1507
- /* set biomarker history chart */
1508
-
1509
- const biomarkerFullName = biomarker.fullName || '';
1510
- let bioName = biomarkerFullName.replace(/\r\n/g, '');
1511
- bioName = bioName.replace(/\n/g, '');
1512
- const rangePlaceHolder = convertToRangeLayout(biomarker.ranges, biomarker, isRtl);
1513
- let biomarkerSampleHTML = '';
1514
- let biomInfoStyle = '';
1515
- let biomValueAlignStyle = '';
1516
- if (biomarker.sample) {
1517
- biomarkerSampleHTML += '<div class="biom-sample">'
1518
- + '<span class="biom-sample-value">'
1519
- + biomarker.sample
1520
- + '</span>'
1521
- + '</div>';
1522
- // $biomValueAlignStyle = "vertical-align: bottom;";
1523
- }
1524
- if (!biomarker.sample && (rangePlaceHolder || childRangesPlaceholder || History)) {
1525
- biomInfoStyle = "margin-top:15px;";
1526
- }
1527
-
1528
- // Configurations
1529
- let biomarkerScale = '';
1530
- if (isChildBiomarker) {
1531
- // $biomarkerScale = 'transform:scale(.9);';
1532
- }
1533
- let biomCircleCss = '';
1534
- if (isComponent) {
1535
- biomCircleCss = 'opacity: 0;';
1536
- biomarkerSampleHTML = '';
1537
- bioName = '';
1538
- }
1539
- let panelNesting = '';
1540
- let biomarkerTitleWidth = 'width:260px';
1541
- if (nestingLevel > 0) {
1542
- const extraPadding = ((nestingLevel + 10 % 10) + 1);
1543
- const levelNesting = extraPadding + nestingLevel * INDENTATION.PANEL_TITLE;
1544
- panelNesting = isRtl ? 'padding-right: ' + levelNesting + 'px;box-sizing:border-box;' : 'padding-left: ' + levelNesting + 'px;box-sizing:border-box;';
1545
- const width = 260 - levelNesting;
1546
- biomarkerTitleWidth = 'width:' + width + 'px';
1547
- }
1548
-
1549
- if (!rangePlaceHolder && !childRangesPlaceholder && !History) {
1550
- biomarkerScale += 'width:100%;';
1551
- }
1552
- const doctorNotesArr = biomarker.doctorNotes || [];
1553
- const doctorNotes = renderDoctorNotes(doctorNotesArr, bioIndentNoteStyle);
1554
-
1555
- biomarkerDetailsHtml += '<div data-id="' + biomarkerCssId + '" class="allergy-biomarker-container biomarker-container grid-col ' + biomarkerClass + '">'
1556
- + '<!--panel title was her in the first biomarker -->'
1557
- + '<div class="grid-col ' + firstGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + panelNesting + '">'
1558
- + '<div class="biomarker">'
1559
- + '<div class="biom-circle" style="' + removeWhiteSpace(CircleStyles) + ';' + biomCircleCss + '">' + biomarkerName + '</div>'
1560
- + '<div class="biom-info ' + biomarkerTitleClass + '" style="' + biomInfoStyle + '">'
1561
- + '<div class="title ' + biomarkerTitleClass + '" style="' + biomarkerTitleWidth + '">' + biomarkerSampleHTML + '<span class="bio-name-info a" style="color: #555558;">' + bioName + '</span></div>'
1562
- + '<div class="value colorable" style="' + biomValueAlignStyle + '">'
1563
- + biomarkerSampleHTML
1564
- + '<div class="pair parent-pair">'
1565
- + '<span class="value-text">'
1566
- + '<span style="' + customBiomarkerValueFontSize + biomarkerColor + '">' + biomarkerValue + '</span>'
1567
- + '</span>'
1568
- + '<span class="unit colorable">' + biomarker.unit + '</span>'
1569
- + '</div>'
1570
- + '</div>'
1571
- + afterBioInfoValue
1572
- + '</div>'
1573
- + '</div>';
1574
-
1575
- if (doctorNotes) {
1576
- biomarkerDetailsHtml += '<div class="grid-row biomarker-insights a">'
1577
- + doctorNotes
1578
- + '</div>';
1579
- }
1580
-
1581
- if (biomarker.notes && biomarker.notes.length > 0) {
1582
- if (nestingLevel > 0) {
1583
- // $extraPadding = (($nestingLevel + 10 % 10) + 1);
1584
- // $levelNesting = $extraPadding + $nestingLevel * $INDENTATION['BIOMARKER_DETAILS'] + 5;
1585
- // $bioIndentStyle = 'padding-left: ' . $levelNesting . 'px;';
1586
- }
1587
- bioIndentStyle = isRtl ? 'padding-right: 16px;' : 'padding-left: 16px;';
1588
-
1589
- if (useMonospace) {
1590
- const monoStyle = `font-style: normal; font-size: 13px; color: #333; font-family: 'Roboto Mono', monospace; white-space: pre-wrap; overflow-x: auto;`;
1591
- biomarkerDetailsHtml += '<div class="biomarker-notes 1" style="padding-top: 10px;' + bioIndentStyle + '">';
1592
- biomarkerDetailsHtml += `<span style="${monoStyle}">` + biomarker.notes.join('<br/>') + '</span>';
1593
- biomarkerDetailsHtml += '</div>';
1594
- } else {
1595
- biomarkerDetailsHtml += '<div class="biomarker-notes 1" style="color: #8F8F92;padding-top: 10px;' + bioIndentStyle + '">';
1596
- biomarkerDetailsHtml += biomarker.notes.join('<br/>');
1597
- biomarkerDetailsHtml += '</div>';
1598
- }
1599
- }
1600
- biomarkerDetailsHtml += '</div> <!-- end first col -->';
1601
-
1602
- if (allergyClasses) {
1603
- const rangeIndex = biomarker.rangeIndex;
1604
- biomarkerDetailsHtml += '<table class="allergy-classes-table" width="100%" cellspacing="0" cellpadding="0" range="' + rangeIndex + '">';
1605
-
1606
- let tr1 = '<tr>';
1607
-
1608
- allergyClasses.forEach((cls, index) => { // Renamed 'class' to 'cls' to avoid conflict
1609
- const formattedMinValue = cls.formattedMinValue;
1610
- const formattedMaxValue = cls.formattedMaxValue;
1611
- let range = '';
1612
-
1613
- if (!formattedMinValue && formattedMaxValue) {
1614
- range = `< ${formattedMaxValue}`;
1615
- } else if (formattedMinValue && formattedMaxValue) {
1616
- range = `${formattedMinValue} - ${formattedMaxValue}`;
1617
- } else if (formattedMinValue && !formattedMaxValue) {
1618
- range = `> ${formattedMinValue}`;
1619
- }
1620
-
1621
- let value = '';
1622
-
1623
- if (index === rangeIndex) {
1624
- let currentRange = biomarker.ranges[rangeIndex];
1625
- let currentPercent = 0;
1626
-
1627
- if (currentRange.minValue !== undefined && currentRange.maxValue !== undefined) {
1628
- currentPercent = ((parseFloat(biomarker.value) - currentRange.minValue) / (currentRange.maxValue - currentRange.minValue)) * 100;
1629
- }
1630
-
1631
- currentPercent = Math.max(8, Math.min(77, currentPercent)); // Ensures percent is between 8 and 77
1632
- const imagePlace = isRtl ? `right: ${currentPercent}%` : `left: ${currentPercent}%`;
1633
-
1634
- value = `<span index="${rangeIndex}" class="colored-value" patient="${forPatient}" style="background:${cls.color}">`;
1635
-
1636
- if (!forPatient) {
1637
- value += biomarker.formattedValue;
1638
- } else {
1639
- value += `<span style="${imagePlace}" class="white-circle"></span>`;
1640
- }
1641
-
1642
- value += `</span>`;
1643
- }
1644
-
1645
- tr1 += `<td>${value}</td>`;
1646
- });
1647
- allergyClasses.forEach((cls, index) => { // Renamed 'class' to 'cls' to avoid conflict
1648
- const formattedMinValue = cls.formattedMinValue;
1649
- const formattedMaxValue = cls.formattedMaxValue;
1650
- let range = '';
1651
-
1652
- if (!formattedMinValue && formattedMaxValue) {
1653
- range = `< ${formattedMaxValue}`;
1654
- } else if (formattedMinValue && formattedMaxValue) {
1655
- range = `${formattedMinValue} - ${formattedMaxValue}`;
1656
- } else if (formattedMinValue && !formattedMaxValue) {
1657
- range = `> ${formattedMinValue}`;
1658
- }
1659
-
1660
- let value = '';
1661
-
1662
- if (index === rangeIndex) {
1663
- let currentRange = biomarker.ranges[rangeIndex];
1664
- let currentPercent = 0;
1665
-
1666
- if (currentRange.minValue !== undefined && currentRange.maxValue !== undefined) {
1667
- currentPercent = ((parseFloat(biomarker.value) - currentRange.minValue) / (currentRange.maxValue - currentRange.minValue)) * 100;
1668
- }
1669
-
1670
- currentPercent = Math.max(8, Math.min(77, currentPercent)); // Ensures percent is between 8 and 77
1671
- const imagePlace = isRtl ? `right: ${currentPercent}%` : `left: ${currentPercent}%`;
1672
-
1673
- value = `<span index="${rangeIndex}" class="colored-value" patient="${forPatient}" style="background:${cls.color}">`;
1674
-
1675
- if (!forPatient) {
1676
- value += biomarker.formattedValue;
1677
- } else {
1678
- value += `<span style="${imagePlace}" class="white-circle"></span>`;
1679
- }
1680
-
1681
- value += `</span>`;
1682
- }
1683
-
1684
- tr1 += `<td>${value}</td>`;
1685
- });
1686
-
1687
- tr1 += '</tr>';
1688
- biomarkerDetailsHtml += tr1 + '</table>';
1689
- }
1690
-
1691
- biomarkerDetailsHtml += '</div> <!-- end first close biomarker-container-->';
1692
- }
1693
- return biomarkerDetailsHtml;
1694
- }
1695
-
1696
- // Helper functions (assuming they exist in your code)
1697
- function GetBiomarkerCircleStyle(BiomarkerColor, BiomarkerName, BiomarkerStatus, BiomarkerInsightsCount, PanelName, isSummary, item = []) {
1698
- let CircleStyles = '';
1699
- let fontSize = BiomarkerName && BiomarkerName.length > 6 ? '12px' : '14px';
1700
- let color = BiomarkerColor || "#555555";
1701
- let BioColor = BiomarkerColor || "#E05A5A";
1702
- let backgroundColor = BioColor ? 'transparent' : "#E05A5A30";
1703
-
1704
- if (isSummary) {
1705
- CircleStyles = `background-color: ${backgroundColor}; border: 2px solid ${BioColor}; color: ${color}; font-size: ${fontSize};`;
1706
- } else {
1707
- BioColor = BiomarkerColor || "#555555";
1708
- CircleStyles = `background-color: transparent; border: 2px solid ${BioColor}; color: ${color}; font-size: ${fontSize};`;
1709
- }
1710
-
1711
- if ((BiomarkerInsightsCount !== undefined && BiomarkerStatus !== undefined) || BiomarkerInsightsCount === null || BiomarkerStatus === null) {
1712
- backgroundColor = (BiomarkerInsightsCount || PanelName.toLowerCase() === 'health risk calculators') ? BiomarkerColor : "transparent";
1713
- let BiomarkerBorder = (BiomarkerInsightsCount || PanelName.toLowerCase() === 'health risk calculators') ? `2px solid ${BiomarkerColor}` : `2px solid ${BiomarkerColor}`;
1714
- color = BiomarkerInsightsCount ? "#ffffff" : BiomarkerColor;
1715
- fontSize = BiomarkerName && PanelName.toLowerCase().length > 6 ? "12px" : "14px";
1716
-
1717
- CircleStyles = `background-color: ${backgroundColor}; border: ${BiomarkerBorder}; color: ${color}; font-size: ${fontSize};`;
1718
-
1719
- if (BiomarkerStatus !== 1 && BiomarkerStatus !== "1") {
1720
- fontSize = BiomarkerName && BiomarkerName.length > 7 ? "12px" : "14px";
1721
- CircleStyles = `background-color: transparent; border: 2px solid rgba(0,0,0,.4); color: rgba(0,0,0,.4); font-size: ${fontSize};`;
1722
- }
1723
- }
1724
- return CircleStyles;
1725
- }
1726
-
1727
- function convertToRangeLayout(ranges, biomarker, isRtl) {
1728
-
1729
- let barHTML = '';
1730
- if (ranges.length > 1) {
1731
- barHTML += '<table>';
1732
- let tr1 = '<tr class="ranges">';
1733
- let tr2 = '<tr class="labels">';
1734
- let tr3 = '<tr class="values">';
1735
- let currentPercent = 50;
1736
- let currentRange = "";
1737
-
1738
- ranges.forEach(range => {
1739
- let isCurrentClass = range.isCurrent ? "current" : "";
1740
- let labelClass = range.isCurrent ? 'colorable' : "";
1741
- let rangeTitle = '';
1742
- let rangeArrow = '';
1743
-
1744
- if (range.isCurrent) {
1745
- currentRange = `${range.minValue} - ${range.maxValue}`;
1746
- if (range.minValue && range.maxValue) {
1747
- currentPercent = (((parseFloat(biomarker.value) - range.minValue) / (range.maxValue - range.minValue)) * 100);
1748
- }
1749
- currentPercent = Math.min(90, currentPercent);
1750
- let imagePlace = isRtl ? `padding-right:${currentPercent}%` : `padding-left:${currentPercent}%`;
1751
- rangeArrow = `<img class="range-arrow-layout" style="${imagePlace}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAAAXNSR0IArs4c6QAAATBJREFUWAnNjgmOAjEQA/M0P90/23UhsdICw1w5uqSgIem2q7XWnPNT/ODYlFNdFMcHzm9VWdz+UL6qiuL2D+dfNVmc3lBuqoni9BHntoosLpsoL1VEcfmK87paFoddlInVojgcwplaJUv3YZTJVaJ0n8KZni1L52mUjdmidF7C2ZolS9dllM1ZonTdwtkeLUvHbZSE0aJ0dMFJGSVLdjeUpFGiZHfFSestS2Z3lMTeomQOwUntJUvWMJTkXqJkDcVJvytLxnCUhruiZEzBabkqy+40lKarouxOxWk7K8vOdJTGs6LsLMFpPSrL7DKU5qOizC7Fad+TZWY5isGeKDMlcCy2ZHkrg2KyJcpbKRybV1nuyqEYvYpyVxLH6inLd1kUs6co36Vx7DjlUQw5XfkFNeBu5tFKZ1EAAAAASUVORK5CYII="/>`;
1752
- }
1753
-
1754
- rangeTitle = getRangeBar(biomarker, range, biomarker.normalRangeType);
1755
- let colorClass = color(range.color);
1756
- tr1 += `<td style="background-position-x:${currentPercent}%;" class="${colorClass} ${isCurrentClass}">${rangeArrow}<div class="range colorablebg" style="background:${colorClass}"></div></td>`;
1757
- tr2 += `<td class="${isCurrentClass} ${colorClass}"><span class="${labelClass}">${range.name}</span></td>`;
1758
- tr3 += `<td>${rangeTitle}</td>`;
1759
- });
1760
-
1761
- barHTML += tr1 + '</tr>' + tr3 + '</tr>' + tr2 + '</tr>';
1762
- barHTML += '</table>';
1763
- }
1764
- return barHTML;
1765
- }
1766
-
1767
-
1768
- function removeWhiteSpace(html) {
1769
- html = html.replace(/\r\n/g, "");
1770
- return html.replace(/>\s+</g, "><");
1771
- }
1772
-
1773
- function fetchFileContents(filePath) {
1774
- try {
1775
- return fs.readFileSync(filePath, 'utf8');
1776
- } catch (error) {
1777
- console.error('Error reading file:', error);
1778
- return null;
1779
- }
1780
- }
1781
-
1782
- function parseTable(string) {
1783
- let levels = string.split("\n");
1784
- let list = levels.map(level => {
1785
- return {
1786
- id: random(2, 6),
1787
- content: level,
1788
- sepCount: separtorsNumbers(level)
1789
- };
1790
- });
1791
-
1792
- let parent = list[0].sepCount;
1793
-
1794
- for (let i = 0; i < list.length; i++) {
1795
- if (i === 0) {
1796
- list[i].parent_id = "0";
1797
- continue;
1798
- }
1799
-
1800
- if (parent === list[i].sepCount) {
1801
- list[i].parent_id = "0";
1802
- continue;
1803
- }
1804
-
1805
- if (list[i - 1]) {
1806
- if (list[i].sepCount > list[i - 1].sepCount) {
1807
- list[i].parent_id = list[i - 1].id;
1808
- } else if (list[i].sepCount === list[i - 1].sepCount) {
1809
- list[i].parent_id = list[i - 1].parent_id;
1810
- } else {
1811
- list[i].parent_id = findParentId(i - 1, list[i].sepCount, list);
1812
- }
1813
- }
1814
- }
1815
-
1816
- return list_to_tree(list, "0");
1817
- }
1818
-
1819
- function renderDetails(list, isRtl) {
1820
- if (list.length === 1 && !list[0].children) {
1821
- return `<li class="1">${list[0].content.substring(list[0].sepCount)}</li>`;
1822
- }
1823
-
1824
- let columns = [];
1825
- let isParent = false;
1826
-
1827
- list.forEach(child => {
1828
- if (child.parent_id === "0") isParent = true;
1829
- let div = '';
1830
-
1831
- if (!child.children) {
1832
- let childContent = child.content.substring(child.sepCount);
1833
- childContent = childContent.replace('(', '(').replace(')', ')');
1834
-
1835
- if (isRtl) {
1836
- // Optional RTL handling here
1837
- }
1838
-
1839
- div = `<li class="2">${childContent}</li>`;
1840
- } else {
1841
- div = `<li class="arrow-icon">${child.content.substring(child.sepCount)}</li>
1842
- <ul>${renderDetails(child.children, isRtl)}</ul>`;
1843
- }
1844
-
1845
- if (div) columns.push(div);
1846
- });
1847
-
1848
- return columns.length > 0 ? columns.join('') : null;
1849
- }
1850
-
1851
- function GetRelatedBiomarker(biomarkerstyle, name, fullname) {
1852
- let fontSize = '';
1853
- if (name && fullname) {
1854
- if (name.length >= 7) {
1855
- fontSize = 'small-fontSize-circle';
1856
- }
1857
- return `<div class="related">
1858
- <div class="bicon ${fontSize}" style="${biomarkerstyle}">
1859
- <span class="bicon-inner">${name}</span>
1860
- </div>
1861
- </div>`;
1862
- }
1863
- return null;
1864
- }
1865
-
1866
- function color(hex) {
1867
- switch (hex) {
1868
- case "#E05A5A":
1869
- return "red";
1870
- case "#EE734E":
1871
- return "orange";
1872
- case "#41B9D6":
1873
- return "blue";
1874
- case "#F7AA2E":
1875
- return "yellow";
1876
- case "#BC2828":
1877
- return "dark-red";
1878
- case "#02D9D1":
1879
- case "#05B9AF":
1880
- return "green";
1881
- case "#0F0F0F":
1882
- return "black";
1883
- case "#87B7CE":
1884
- return "lightblue";
1885
- case "#FB7377":
1886
- return "light-red";
1887
- default:
1888
- return hex;
1889
- }
1890
- }
1891
-
1892
- function getRangeBar(item, level, normalRangeType = '') {
1893
-
1894
- let range = '';
1895
- let min = level.formattedMinValue;
1896
- let max = level.formattedMaxValue;
1897
-
1898
- if (item.ranges && item.ranges.length > 0) {
1899
- let customBioWithTwoRangesAndNormalExist = false;
1900
- if (item.isCustom && item.ranges.length === 2) {
1901
- let normalRange = item.ranges.find(range => range.isReference);
1902
- if (normalRange) customBioWithTwoRangesAndNormalExist = true;
1903
- }
1904
-
1905
- let rangeIndex = item.ranges.findIndex(range => range.isReference);
1906
-
1907
- let readingLevels = item.ranges;
1908
- let prevItem = rangeIndex - 1;
1909
-
1910
- if (max !== null || min !== null) {
1911
-
1912
- if (level.isReference === true) {
1913
- switch (normalRangeType) {
1914
- case 0 | "0":
1915
- range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `<= ${max}` : `>= ${min}`;
1916
-
1917
- if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1918
- range = `> ${min}`;
1919
- }
1920
- break;
1921
- case 1 | "1":
1922
- range = `< ${max}`;
1923
- break;
1924
- case 2 | "2":
1925
- range = `<= ${max}`;
1926
- break;
1927
- case 3 | "3":
1928
- range = `> ${min}`;
1929
- break;
1930
- case 4 | "4":
1931
- range = `>= ${min}`;
1932
- break;
1933
- }
1934
- } else {
1935
- range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `< ${max}` : `> ${min}`;
1936
- if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1937
- range = `>= ${min}`;
1938
- if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1939
- range = `> ${min}`;
1940
- }
1941
- }
1942
-
1943
- if (customBioWithTwoRangesAndNormalExist) {
1944
- switch (normalRangeType) {
1945
- case "0":
1946
- range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `< ${max}` : `> ${min}`;
1947
- break;
1948
- case "1":
1949
- range = min !== null ? `>= ${min}` : range;
1950
- break;
1951
- case "2":
1952
- range = min !== null ? `> ${min}` : range;
1953
- break;
1954
- case "3":
1955
- range = max !== null ? `<= ${max}` : range;
1956
- break;
1957
- case "4":
1958
- range = max !== null ? `< ${max}` : range;
1959
- break;
1960
- }
1961
- }
1962
- }
1963
- }
1964
- }
1965
- return range;
1966
- }
1967
-
1968
- function separtorsNumbers(string) {
1969
- let last = string.lastIndexOf('#');
1970
- let first = string.indexOf('#');
1971
-
1972
- if (first !== -1 && first !== 0) {
1973
- return 0;
1974
- }
1975
-
1976
- let subString = string.substring(first, last + 1);
1977
- let mySplits = subString.split('');
1978
- let isLevel = mySplits.every(char => char === "#");
1979
-
1980
- return isLevel ? mySplits.length : separtorsNumbers(string.substring(first, last));
1981
- }
1982
-
1983
- function shouldRenderReadingLevels(biomarker, isComponentItem = false) {
1984
- return biomarker && biomarker.ranges.length > 1 && (biomarker.showScale || isComponentItem);
1985
- }
1986
-
1987
- function convertToMiniRangeLayout(ranges, biomarker, isRtl) {
1988
- let barHTML = '';
1989
-
1990
- if (ranges.length > 0) {
1991
- barHTML += '<div class="bar">';
1992
- ranges.forEach(range => {
1993
- let colorClass = color(range.color);
1994
- let label = range.name;
1995
- let isCurrentClass = range.isCurrent ? "current" : "";
1996
- let rangeTitle = `${range.minValue} - ${range.maxValue}`;
1997
- let imgCurrent = '';
1998
-
1999
- if (range.isCurrent) {
2000
- let currentRange = `${range.minValue} - ${range.maxValue}`;
2001
- let currentPercent = null;
2002
- if (range.minValue && range.maxValue) {
2003
- currentPercent = (((parseFloat(biomarker.value) - range.minValue) / (range.maxValue - range.minValue)) * 100);
2004
- }
2005
- currentPercent = currentPercent / 2;
2006
- let imagePlace = currentPercent ? `left: ${currentPercent}%` : '';
2007
- if (isRtl) {
2008
- imagePlace = currentPercent ? `right: ${currentPercent}%` : '';
2009
- }
2010
- imgCurrent = `<img style="${imagePlace}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhBAMAAAClyt9cAAAAHlBMVEUAAAD///////////////////////////////////8kfJuVAAAACXRSTlMA8ObIrYNJLyOprz29AAAAaUlEQVQoz2OgPyg1mmRagCzQMhMIpjQgBNgkZ4KAGEIkcyYEJMAEOCShIhIwEZaZUDAFJlI5EwZg1kXCRQKgIpZwEQOoiCZcRAEqMhMOJmCqwTQH0y5M92C4GdNfmH7HCB/MMMQMZ7oDAAe/ZbNL9bq2AAAAAElFTkSuQmCC" />`;
2011
- }
2012
- barHTML += `<div class="segment ${colorClass} ${isCurrentClass}" style="background:${colorClass}">${imgCurrent}</div>`;
2013
- });
2014
- barHTML += '</div>';
2015
- }
2016
- return barHTML;
2017
- }
2018
-
2019
- function getRange(item, normalRangeKey = "isReference") {
2020
- let range = '';
2021
- if (item.ranges && item.ranges.length > 0) {
2022
- let itemValues = item.ranges.find(range => range[normalRangeKey] === true);
2023
- let rangeIndex = item.ranges.findIndex(range => range[normalRangeKey] === true);
2024
- let readingLevels = item.ranges;
2025
-
2026
- let max = itemValues?.formattedMaxValue ?? null;
2027
- let min = itemValues?.formattedMinValue ?? null;
2028
- let normalRangeType = item.normalRangeType;
2029
- let prevItem = rangeIndex - 1;
2030
-
2031
- if (max !== null || min !== null) {
2032
- if (max !== null && min !== null) {
2033
- range = `${min} - ${max}`;
2034
- } else if (max !== null) {
2035
- range = `< ${max}`;
2036
- } else if (min !== null) {
2037
- range = `> ${min}`;
2038
- }
2039
-
2040
- switch (String(normalRangeType)) {
2041
- case "0":
2042
- if (max !== null && min !== null) {
2043
- range = `${min} - ${max}`;
2044
- } else if (max !== null) {
2045
- range = `<= ${max}`;
2046
- } else if (min !== null) {
2047
- range = `>= ${min}`;
2048
- }
2049
- break;
2050
- case "1":
2051
- if (max !== null) range = `< ${max}`;
2052
- break;
2053
- case "2":
2054
- if (max !== null) range = `<= ${max}`;
2055
- break;
2056
- case "3":
2057
- if (min !== null) range = `> ${min}`;
2058
- break;
2059
- case "4":
2060
- if (min !== null) range = `>= ${min}`;
2061
- break;
2062
- }
2063
- }
2064
- }
2065
- return range;
2066
- }
2067
-
2068
- function find(array, key, value) {
2069
- for (let single of array) {
2070
- if (single[key] === value) {
2071
- return single;
2072
- }
2073
- }
2074
- return null;
2075
- }
2076
-
2077
- function renderBiomarkerHistoryChart(biomarker) {
2078
- let content = '';
2079
- const history = biomarker.history && biomarker.history.length > 0 ? biomarker.history : [];
2080
-
2081
- // Custom biomarker shouldn't have a chart
2082
- if (history.length > 1) {
2083
- const biomarkerCssChartId = "chart-bio-db" +
2084
- Math.random().toString(36).substring(2, 8) +
2085
- String(biomarker?.id ?? "").toLowerCase();
2086
- content += `<canvas id="${biomarkerCssChartId}" width="100%" height="30" class="chart-canvas"></canvas>`;
2087
- const dataset = [];
2088
- const dataColor = [];
2089
- const dataColorLabels = [];
2090
- const dataLabel = [];
2091
- const unit = [];
2092
- const dataFormattedValues = [];
2093
-
2094
- history.forEach((item, index) => {
2095
- dataset[index] = parseInt(biomarker.type) ? parseFloat(item.value) : item.rangeIndex;
2096
- dataFormattedValues[index] = item.formattedValue;
2097
- dataColor[index] = item.color;
2098
- dataColorLabels[index] = item.color === "#E05A5A" ? item.color : "rgba(51,51,51, 0.8)";
2099
- dataLabel[index] = item.formattedDate;
2100
- unit[index] = item.unit;
2101
- });
2102
-
2103
- const maxOfChart = Math.max(...dataset) + 0.5;
2104
- const differenceRange = Math.max(...dataset) - Math.min(...dataset);
2105
- let stepSizeNumber = 0.5;
2106
- if (differenceRange > 100) {
2107
- stepSizeNumber = 0;
2108
- }
2109
-
2110
- content += `
2111
- <script>
2112
- $(document).ready(function() {
2113
- function showChart() {
2114
- const ctx = document.getElementById("${biomarkerCssChartId}").getContext("2d");
2115
- new Chart(ctx, {
2116
- type: "line",
2117
- data: {
2118
- labels: ${JSON.stringify(dataLabel)},
2119
- datasets: [{
2120
- data: ${JSON.stringify(dataset)},
2121
- dataFormattedValues: ${JSON.stringify(dataFormattedValues)},
2122
- pointBorderColor: "transparent",
2123
- pointRadius: 6,
2124
- pointBorderWidth: 2,
2125
- lineTension: 0,
2126
- fill: false,
2127
- pointBackgroundColor: ${JSON.stringify(dataColor)},
2128
- fill: false
2129
- }]
2130
- },
2131
- options: {
2132
- layout: {
2133
- padding: {
2134
- top: 30,
2135
- bottom: 30,
2136
- left: 50,
2137
- right: 40
2138
- }
2139
- },
2140
- legend: {
2141
- display: false,
2142
- fontColor: "black"
2143
- },
2144
- tooltips: { enabled: false },
2145
- plugins: {
2146
- datalabels: {
2147
- display: "block",
2148
- color: ${JSON.stringify(dataColorLabels)},
2149
- align: "top",
2150
- anchor: "end",
2151
- clamp: true,
2152
- clip: false,
2153
- font: {
2154
- lineHeight: 1.2,
2155
- size: 12,
2156
- style: "normal"
2157
- },
2158
- formatter: function(value, context) {
2159
- return " " + context.dataset.dataFormattedValues[context.dataIndex];
2160
- }
2161
- }
2162
- },
2163
- document: true,
2164
- window: true,
2165
- hover: false,
2166
- beginAtZero: false,
2167
- responsive: true,
2168
- maintainAspectRatio: true,
2169
- scales: {
2170
- yAxes: [{
2171
- ticks: {
2172
- suggestedMax: ${maxOfChart},
2173
- suggestedMin: 0,
2174
- stepSize: ${stepSizeNumber}
2175
- },
2176
- display: false
2177
- }],
2178
- xAxes: [{
2179
- gridLines: {
2180
- display: false
2181
- }
2182
- }]
2183
- },
2184
- animation: {
2185
- onComplete: function() {
2186
- window.JSREPORT_READY_TO_START = true;
2187
- }
2188
- }
2189
- }
2190
- });
2191
- }
2192
- setTimeout(showChart, 200);
2193
- });
2194
- </script>
2195
- `;
2196
- }
2197
-
2198
- return content;
2199
- }
2200
-
2201
-
2202
- module.exports = {
2203
- renderReportItems,
2204
- find,
2205
- renderSummary,
2206
- renderReportSummary,
2207
- renderHTMLTemplate,
2208
- renderAllergyClasses,
2209
- setUseMonospace,
2210
- }
2211
-
2212
-
2213
-
2214
-
2215
-
2216
-
2217
-
2218
-
2219
-
2220
-
2221
-
2222
-
2223
-
2224
-
2225
-
2226
-
2227
-
1
+ const LocaleService = require('../app/services/localeService.js');
2
+ const i18n = require('../app/i18n_wellbeing.config.js');
3
+ fetchFileContents
4
+ const fs = require("fs");
5
+ const path = require('path');
6
+
7
+ let useMonospace = false;
8
+ function setUseMonospace(val) { useMonospace = val; }
9
+ const INDENTATION = {
10
+ PANEL_TITLE: 12,
11
+ BIOMARKER_DETAILS: 12,
12
+ INSIGHT: 12,
13
+ PAGE_WIDTH: 794
14
+ };
15
+ var localeService = new LocaleService(i18n);
16
+
17
+
18
+ function renderHTMLTemplate(html, isRtl, data, isIMCLab, lang = 'en') {
19
+ let dir = isRtl ? 'rtl' : 'ltr';
20
+ let language = isRtl ? 'ar' : 'en';
21
+ let IMCclass = isIMCLab ? 'imc-lab' : '';
22
+ let styles = fetchFileContents(path.resolve(__dirname, '../assets/medicus_pdf/styles.css'));
23
+ if (isRtl) {
24
+ styles += fetchFileContents(path.resolve(__dirname, '../assets/medicus_pdf/arabic.css'));
25
+ }
26
+ let bodyClass = "";
27
+ if (!data['approvalDate']) {
28
+ bodyClass = "draft-bg";
29
+ if (lang !== 'en' && ['ar', 'de', 'fr', 'nl-NL', 'pt', 'pt_BR', 'ar-SA'].includes(lang)) {
30
+
31
+ bodyClass = "draft-bg-" + lang;
32
+ }
33
+ }
34
+ if (data['labType'] === 18) {
35
+ bodyClass += ' smart-donor';
36
+ }
37
+ const fs = require('fs');
38
+ let content = `
39
+ <div>
40
+ <div id="page_template" class="all-pages ${IMCclass}">
41
+
42
+ ${html}
43
+ </div>
44
+ </div>
45
+ `;
46
+
47
+ return content;
48
+ }
49
+
50
+ function returnIntroTemplate(data, isIMCLab) {
51
+ let profileItemHTML = '';
52
+ let classProfileItemContainer = "size4";
53
+ let widthProfileItemContainer = '';
54
+ let introWidth = '';
55
+ if (isIMCLab || (!process.env.SHOW_QR_BOX || process.env.SHOW_QR_BOX === "false")) {
56
+ classProfileItemContainer = '';
57
+ widthProfileItemContainer = 'width:100%';
58
+ introWidth = 'width:100%';
59
+
60
+ let length = 100 / 3;
61
+ let items = data['profileItems'];
62
+ if (data['reportProperties'] && isIMCLab) {
63
+ for (const [key, value] of Object.entries(data['reportProperties'])) {
64
+ items.push({title: key, value: value});
65
+ }
66
+ }
67
+
68
+ for (const item of items) {
69
+ profileItemHTML += `<div style='width: ${length}%; padding: 10px 0' class='grid-col size2'>
70
+ <div class='info-label'>${item.title}</div>
71
+ <div class='info-value'>${item.value}</div>
72
+ </div>`;
73
+ }
74
+ } else {
75
+ let index = 0;
76
+ while (index < data['profileItems'].length - 1) {
77
+ let newDivId = 'sub' + index;
78
+ profileItemHTML += `<div id="${newDivId}" class='grid-row'>`;
79
+
80
+ for (let j = 0; j < 2; j++) {
81
+ let valueId = newDivId + '-' + j;
82
+ let profileItemValue = data['profileItems'][index]['value'];
83
+ let classAttr = /[A-Za-z]/.test(profileItemValue) && /[0-9]/.test(profileItemValue) ? "ltr_dir" : "";
84
+
85
+ profileItemHTML += `<div id='${valueId}' class='grid-col size2'>
86
+ <div class='info-label'>${data['profileItems'][index]['title']}</div>
87
+ <div class='info-value ${classAttr}'>${profileItemValue}</div>
88
+ </div>`;
89
+ index++;
90
+ }
91
+ profileItemHTML += `</div>`;
92
+ }
93
+
94
+ if (data['profileItems'].length % 2 !== 0) {
95
+ let newDivId = 'sub' + index;
96
+ let valueId = newDivId + '-0';
97
+ let profileItemValue = data['profileItems'][index]['value'];
98
+ let classAttr = /[A-Za-z]/.test(profileItemValue) && /[0-9]/.test(profileItemValue) ? "ltr_dir" : "";
99
+
100
+ profileItemHTML += `<div id="${newDivId}" class='grid-row'>
101
+ <div id='${valueId}' class='grid-col size2'>
102
+ <div class='info-label'>${data['profileItems'][index]['title']}</div>
103
+ <div class='info-value ${classAttr}'>${profileItemValue}</div>
104
+ </div>
105
+ </div>`;
106
+ }
107
+ }
108
+
109
+ let moreLink = localeService.t('Access your detailed report online') + " ";
110
+ moreLink = moreLink.replace("{link}", `<strong><a href='${data['moreLink']}' target='_blank'>${data['moreLink']}</a></strong>`);
111
+
112
+ if (data['patientPin'] && data['showPIN'] && data['qrCode']) {
113
+ moreLink += localeService.t("Scan this report’s QR code or enter this PIN code:") + "";
114
+ } else if (data['patientPin'] && data['showPIN']) {
115
+ moreLink += localeService.t("Enter this PIN code:") + "";
116
+ } else if (data['qrCode']) {
117
+ moreLink += localeService.t("Scan this report’s QR code:") + "";
118
+ }
119
+
120
+ let content = '';
121
+ let pin = data['patientPin'] || '';
122
+ let qrImage = data['qrCode'] || '';
123
+ let displayPin = pin ? 'inline-block' : 'none';
124
+ let displayQR = qrImage ? 'inline-block' : 'none';
125
+ let reportTitle = data['labType'] === 18 ? 'Smart Report' : localeService.t('Smart Report');
126
+
127
+ content += `<div class="intro" style="${introWidth}">
128
+ <div class="grid-row">
129
+ <div class="profile-items-container grid-col ${classProfileItemContainer}" style="${widthProfileItemContainer}">
130
+ <div id="report-title-header-ltr" class="report-title">
131
+ <div class="heading">${reportTitle}</div>
132
+ </div>
133
+ <div id="patient-container">${profileItemHTML}</div>
134
+ </div>`;
135
+
136
+ if (!isIMCLab && (process.env.SHOW_QR_BOX === "true" || process.env.SHOW_QR_BOX === true) && data['labType'] !== 18) {
137
+ content += `<div class="plus-info-container grid-col size4">
138
+ <div class="plus-info">
139
+ <div class="grid-row">
140
+ <div class="grid-col size3">
141
+ <div id="more-link" class="description">${moreLink}
142
+ <span id="pin" class="pin" style="display:${displayPin}">${pin}</span>
143
+ </div>
144
+ </div>
145
+ <div class="grid-col size1 qr-container">
146
+ <div id="qrcode" class="pin-qr" style="display:${displayQR}"><img src="${qrImage}"></div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>`;
151
+ }
152
+
153
+ content += '</div></div>';
154
+
155
+ return content;
156
+ }
157
+
158
+
159
+ function renderDoctorNotes(doctorNotes, bioIndentStyle = '') {
160
+ let doctorNotesHTML = '';
161
+ if (doctorNotes && doctorNotes.length > 0) {
162
+ doctorNotes.forEach(note => {
163
+ let noteId = 'doctor-note-' + Math.random().toString(36).substr(2, 9);
164
+ doctorNotesHTML += `<div class="card-wrapper card-wrapper-note" style="${bioIndentStyle}">
165
+ <div id="${noteId}" class="card">
166
+ <div class="icon">
167
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
168
+ <g fill="none" fill-rule="evenodd">
169
+ <rect width="32" height="32" fill="" rx="16"/>
170
+ <g fill="#295C42" fill-rule="nonzero">
171
+ <path d="M20.613 23.8a.31.31 0 0 0 .217-.088l1.845-1.8a.296.296 0 0 0 .09-.212v-12c0-.166-.137-.3-.307-.3h-1.845a.304.304 0 0 0-.308.3c0 .166.138.3.308.3h1.537v11.576l-1.755 1.712a.295.295 0 0 0 0 .424.31.31 0 0 0 .218.088z"/>
172
+ <path d="M11.387 10c.17 0 .308-.134.308-.3 0-.166-.138-.3-.308-.3H9.542a.304.304 0 0 0-.307.3v12.9c0 .662.552 1.2 1.23 1.2h8.918c.17 0 .307-.134.307-.3v-1.8c0-.136.137-.3.308-.3h1.23c.17 0 .307-.134.307-.3 0-.166-.137-.3-.307-.3h-1.23c-.492 0-.923.42-.923.9v1.5h-8.61a.608.608 0 0 1-.615-.6V10h1.537zM16 8.2c-.34 0-.615.27-.615.6 0 .33.276.6.615.6.34 0 .615-.27.615-.6 0-.33-.276-.6-.615-.6z"/>
173
+ <path d="M22.458 8.2h-1.845a.304.304 0 0 0-.308.3c0 .166.138.3.308.3h1.845c.448 0 .922.463.922.9v13.8c0 .437-.474.9-.922.9H9.542c-.448 0-.922-.463-.922-.9V9.7c0-.437.474-.9.922-.9h1.845c.17 0 .308-.134.308-.3 0-.166-.138-.3-.308-.3H9.542c-.79 0-1.537.729-1.537 1.5v13.8c0 .771.747 1.5 1.537 1.5h12.916c.79 0 1.537-.729 1.537-1.5V9.7c0-.771-.747-1.5-1.537-1.5z"/>
174
+ </g>
175
+ </g>
176
+ </svg>
177
+ </div>
178
+ <div class="text text-with-icon">
179
+ <div class="from-doctor">${localeService.t('noteFromDoctor')} ${note.createdByName}</div>
180
+ <div class="note-content">${note.content.replace(/\n/g, "<br/>")}</div>
181
+ </div>
182
+ </div>
183
+ </div>`;
184
+ });
185
+ }
186
+ return doctorNotesHTML;
187
+ }
188
+
189
+ function renderDoctorNotesCompactView(doctorNotes, bioIndentStyle = '') {
190
+ let doctorNotesHTML = '';
191
+ if (doctorNotes && doctorNotes.length > 0) {
192
+ doctorNotes.forEach(note => {
193
+ doctorNotesHTML += `<div class="note-content">${note.content}</div>`;
194
+ });
195
+ }
196
+ return doctorNotesHTML;
197
+ }
198
+
199
+ function renderOneBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent) {
200
+
201
+ let biomarkerDetailsHtml = '';
202
+
203
+ if (biomarker.showInDetailsView === true) {
204
+ const biomarkerCssId = "dtl-bio-" + "db" + biomarker.id;
205
+ let customBiomarkerValueFontSize = '';
206
+ const rawFormattedValue = biomarker.formattedValue || biomarker.lingualReading || '';
207
+ const formattedValue = rawFormattedValue.replace(/,/g, "");
208
+ if (formattedValue.length > 35) {
209
+ customBiomarkerValueFontSize = 'font-size:12px;';
210
+ }
211
+ const biomarkerName = biomarker.name || '';
212
+ const biomarkerStatus = biomarker.status || '';
213
+ const insightsCount = biomarker.insightsCount || '';
214
+ let CircleStyles = '';
215
+ if (!isComponent) {
216
+ CircleStyles = GetBiomarkerCircleStyle(biomarker.color, biomarkerName, biomarkerStatus, insightsCount, '', false);
217
+ }
218
+
219
+ // Indent biomarker details based on panel level
220
+ let firstGridColClass = 'size5';
221
+ let firstGridColstyle = '';
222
+ let lastGridColClass = 'size3';
223
+ let bioIndentStyle = '';
224
+ let bioIndentNoteStyle = '';
225
+ if (nestingLevel > 0) {
226
+ // Replace classes
227
+ firstGridColClass = 'size5';
228
+ lastGridColClass = 'size3';
229
+ const extraPadding = ((nestingLevel + 10 % 10) + 1);
230
+ const levelNesting = extraPadding + nestingLevel * (INDENTATION.BIOMARKER_DETAILS || 0);
231
+ const levelNestingNote = extraPadding + nestingLevel * (INDENTATION.BIOMARKER_DETAILS || 0) + 16;
232
+ bioIndentStyle = 'padding-left: ' + levelNesting + 'px;';
233
+ bioIndentNoteStyle = 'padding-left: ' + levelNestingNote + 'px';
234
+ }
235
+
236
+ // IMC special case (large biomarker value)
237
+ let biomarkerClass = '';
238
+ if (rawFormattedValue.length >= 125) {
239
+ biomarkerClass = 'large-value-class';
240
+ firstGridColClass = '';
241
+ // firstGridColstyle = 'width:100%';
242
+ }
243
+
244
+ const biomarkerValue = rawFormattedValue.replace(/\r\n/g, "<br/>");
245
+
246
+ if (!biomarker.showScale) {
247
+ biomarkerClass += ' hide-scale ';
248
+ }
249
+
250
+ const biomarkerColor = (biomarker.color && !biomarker.isNormal) ? "color:" + biomarker.color + ";" : "";
251
+ let rangesPlaceholder = "";
252
+ let currentRange = "";
253
+ let currentPercent = 50;
254
+ let biomarkerTitleClass = '';
255
+ if (biomarker.ranges.length > 1 && biomarker.showScale) {
256
+ biomarkerTitleClass = 'has-ranges';
257
+ } else {
258
+ biomarkerTitleClass = 'has-no-ranges';
259
+ }
260
+
261
+ let childRangesPlaceholder = "";
262
+ let childCurrentRange = "";
263
+ let heightCount = 1;
264
+ let bioValue = "";
265
+ let bioUnit = "";
266
+ let afterBioInfoValue = '';
267
+ let History = '';
268
+ if (biomarker.childrenBiomarkers && biomarker.childrenBiomarkers.length > 0) {
269
+ biomarker.childrenBiomarkers.forEach(bio => {
270
+ if (bio.ranges.length > 1) {
271
+ heightCount++;
272
+ // Viewing the biomarker and unit of children after the parent ones
273
+ bioValue = bio.formattedValue.replace(/\n/g, "<br />");
274
+ let bioValue_style = "";
275
+ if (bioValue.length > 33) {
276
+ bioValue_style = "font-size:12px;";
277
+ }
278
+ bioUnit = bio.unit;
279
+ childCurrentRange = '';
280
+ // Generating the children ranges and display them under the parent ones
281
+ childRangesPlaceholder += '<div class="extra-range-placeholder">'
282
+ + convertToRangeLayout(bio.ranges, bio, isRtl)
283
+ + '</div>';
284
+ biomarkerTitleClass = 'has-ranges';
285
+ afterBioInfoValue += '<div class="colorable pair">'
286
+ + '<span class="biomarker-extra-value" style="' + bioValue_style + '">' + bioValue + '</span>'
287
+ + '<span class="biomarker-extra-unit">' + bioUnit + '</span>'
288
+ + '</div>';
289
+ } else {
290
+ biomarkerTitleClass = 'has-no-ranges';
291
+ }
292
+ History = renderBiomarkerHistoryChart(bio);
293
+ });
294
+ } else {
295
+ History = renderBiomarkerHistoryChart(biomarker);
296
+ }
297
+
298
+ if (heightCount > 1) {
299
+ biomarkerClass += 'hide-flag';
300
+ }
301
+
302
+ // Set biomarker history chart
303
+ const biomarkerFullName = biomarker.fullName || '';
304
+ let bioName = biomarkerFullName.replace(/\r\n/g, '');
305
+ bioName = bioName.replace(/\n/g, '');
306
+ const rangePlaceHolder = convertToRangeLayout(biomarker.ranges, biomarker, isRtl);
307
+ let biomarkerSampleHTML = '';
308
+ let biomInfoStyle = '';
309
+ let biomValueAlignStyle = '';
310
+ if (biomarker.sample) {
311
+ biomarkerSampleHTML += '<div class="biom-sample">'
312
+ + '<span class="biom-sample-value">'
313
+ + biomarker.sample
314
+ + '</span>'
315
+ + '</div>';
316
+ // biomValueAlignStyle = "vertical-align: bottom;";
317
+ }
318
+ if (!biomarker.sample && (rangePlaceHolder || childRangesPlaceholder || History)) {
319
+ biomInfoStyle = "margin-top:15px;";
320
+ }
321
+
322
+ // Configurations
323
+ let biomarkerScale = '';
324
+ if (isChildBiomarker) {
325
+ // biomarkerScale = 'transform:scale(.9);';
326
+ }
327
+ let biomCircleCss = '';
328
+ if (isComponent) {
329
+ biomCircleCss = 'opacity: 0;';
330
+ biomarkerSampleHTML = '';
331
+ bioName = '';
332
+ }
333
+ let panelNesting = '';
334
+ let biomarkerTitleWidth = 'width:260px';
335
+ if (nestingLevel > 0) {
336
+ const extraPadding = ((nestingLevel + 10 % 10) + 1);
337
+ const levelNesting = extraPadding + nestingLevel * (INDENTATION.PANEL_TITLE || 0);
338
+ panelNesting = 'padding-left: ' + levelNesting + 'px;box-sizing:border-box;';
339
+ const width = 260 - levelNesting;
340
+ biomarkerTitleWidth = 'width:' + width + 'px';
341
+ }
342
+
343
+ if ((!rangePlaceHolder || !biomarker.showScale) && !childRangesPlaceholder && !History) {
344
+ biomarkerScale += 'width:100%;';
345
+ }
346
+ const doctorNotesArr = biomarker.doctorNotes || [];
347
+ const doctorNotes = renderDoctorNotes(doctorNotesArr, bioIndentNoteStyle);
348
+ let bioNameStyle = '';
349
+ let isValueNumber = !isNaN(Number(biomarkerValue));
350
+ let valueDataType = isValueNumber ? "number" : "string";
351
+ const safeBioName = bioName
352
+ .replace(/\(/g, '&#x28;')
353
+ .replace(/\)/g, '&#x29;');
354
+ biomarkerDetailsHtml += '<div data-id="' + biomarkerCssId + '" class="biomarker-container grid-col ' + biomarkerClass + '">'
355
+ + '<div class="grid-col ' + firstGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + panelNesting + '">'
356
+ + '<div class="biomarker">'
357
+ + '<div class="biom-circle" style="' + removeWhiteSpace(CircleStyles) + ';' + biomCircleCss + '">' + biomarkerName + '</div>'
358
+ + '<div class="biom-info ' + biomarkerTitleClass + '" style="' + biomInfoStyle + '">'
359
+ + '<div class="title ' + biomarkerTitleClass + '" style="' + biomarkerTitleWidth + '">' + biomarkerSampleHTML + '<span class="bio-name-info b" dir="ltr" style="unicode-bidi:isolate;">' + safeBioName + '</span></div>'
360
+ + '<div class="value colorable" style="' + biomValueAlignStyle + '">'
361
+ + biomarkerSampleHTML
362
+ + '<div class="pair parent-pair">'
363
+ + '<span class="value-text" data-type="' + valueDataType + '">'
364
+ + '<span style="' + customBiomarkerValueFontSize + biomarkerColor + '">' + biomarkerValue + '</span>'
365
+ + '</span>'
366
+ + '<span dir="ltr" class="unit colorable">' + biomarker.unit + '</span>'
367
+ + '</div>'
368
+ + '</div>'
369
+ + afterBioInfoValue
370
+ + '</div>'
371
+ + '</div>'
372
+ + '</div>';
373
+
374
+ if (rangePlaceHolder || childRangesPlaceholder || History) {
375
+ biomarkerDetailsHtml += '<div class="grid-col ' + lastGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + '">';
376
+ let biomBarClass = "";
377
+ if (biomarker.ranges.length > 6) {
378
+ biomBarClass = "big";
379
+ }
380
+
381
+ biomarkerDetailsHtml += '<div class="biom-bar ' + biomBarClass + '">';
382
+ }
383
+ if (rangePlaceHolder && biomarker.showScale) {
384
+ biomarkerDetailsHtml += '<div class="range-placeholder c">' + convertToRangeLayout(biomarker.ranges, biomarker, isRtl) + '</div>';
385
+ }
386
+
387
+ if (childRangesPlaceholder) {
388
+ biomarkerDetailsHtml += childRangesPlaceholder;
389
+ }
390
+ if (rangePlaceHolder || childRangesPlaceholder || History) {
391
+ biomarkerDetailsHtml += '</div><!-- close biom-bar -->';
392
+ // History chart start
393
+ biomarkerDetailsHtml += '<div class="grid-col history-chart-col">';
394
+
395
+ if (History) {
396
+ biomarkerDetailsHtml += '<div class="chart">'
397
+ + History
398
+ + '</div>';
399
+ }
400
+
401
+ biomarkerDetailsHtml += '</div> <!-- end chart grid col -->';
402
+ // History chart end
403
+ biomarkerDetailsHtml += '</div> <!-- close last grid-col -->';
404
+ }
405
+ if (doctorNotes) {
406
+ biomarkerDetailsHtml += '<div class="grid-row biomarker-insights b">'
407
+ + doctorNotes
408
+ + '</div>';
409
+ }
410
+
411
+ biomarkerDetailsHtml += '</div> <!-- end first close biomarker-container-->';
412
+ }
413
+ return biomarkerDetailsHtml;
414
+ }
415
+
416
+ function renderBiomarkerDetails(panel, isRtl, counter) {
417
+ let content = '';
418
+ panel.biomarkers.forEach((biomarker, biomarkrCounter) => {
419
+ content += renderOneBiomarker(biomarker, biomarkrCounter, panel, isRtl, counter);
420
+ });
421
+ return content;
422
+ }
423
+
424
+ function renderPanelTitle(panel, counter, biomarkrCounter) {
425
+ let panelTitle = '';
426
+ let panelSubTitle = '';
427
+ let content = '';
428
+ let checkShowInDetailsView = panel.biomarkers.find(biomarker => biomarker.showInDetailsView === true);
429
+
430
+ if (panel.biomarkers && checkShowInDetailsView && biomarkrCounter === 0) {
431
+ if (panel.panelDetails && panel.panelDetails.length > 1) {
432
+ panelTitle = panel.panelDetails[0].name;
433
+ let newPanelDetails = panel.panelDetails;
434
+
435
+ if (newPanelDetails.length > 0) {
436
+ newPanelDetails.forEach((panelDetail, index) => {
437
+ if (index > 0) {
438
+ let marginLeft = (index + 1) * 10;
439
+ let name = panelDetail.name ? panelDetail.name : '';
440
+ panelSubTitle += `<div class="panel-subtitle-item" style="padding-left:${marginLeft}px">${name}</div>`;
441
+ }
442
+ });
443
+ }
444
+ } else {
445
+ panelTitle = panel.panelDetails && panel.panelDetails[0] && panel.panelDetails[0].name ? panel.panelDetails[0].name : panel.name;
446
+ }
447
+
448
+ let detailsTitle = '';
449
+ if (counter === 0) {
450
+ detailsTitle = `<div id="details-section" class="section">
451
+ <div class="resume-title">${localeService.t('Details')}</div>
452
+ </div>`;
453
+ }
454
+
455
+ content += `${detailsTitle}
456
+ <div class="panel-title">${panelTitle}</div>
457
+ <div class="panel-subtitle">${panelSubTitle}</div>`;
458
+ }
459
+ return content;
460
+ }
461
+
462
+ function renderPanelDetails(panel, biomarkerDetailsHtml, counter, isRtl) {
463
+ const panelCssId = `dtl-pnl-dp${panel.panelId.toLowerCase()}`;
464
+ const checkShowInDetailsView = panel.biomarkers.find(biomarker => biomarker.showInDetailsView === true);
465
+ let detailsSectionHTML = '';
466
+ let panelSubTitle = '';
467
+
468
+ if (panel.biomarkers && checkShowInDetailsView) {
469
+ let panelTitle = panel.panelDetails && panel.panelDetails.length > 1 ? panel.panelDetails[0].name : panel.name;
470
+
471
+ if (panel.panelDetails && panel.panelDetails.length > 1) {
472
+ panel.panelDetails.forEach((panelDetail, index) => {
473
+ if (index > 0) {
474
+ let marginLeft = (index + 1) * 10;
475
+ let name = panelDetail.name ? panelDetail.name : '';
476
+ panelSubTitle += `<div class="panel-subtitle-item" style="padding-left:${marginLeft}px">${name}</div>`;
477
+ }
478
+ });
479
+ }
480
+
481
+ let detailsTitle = '';
482
+ if (counter === 0) {
483
+ detailsTitle = `<div id="details-section" class="section">
484
+ <div class="resume-title">${localeService.t('Details')}</div>
485
+ </div>`;
486
+ }
487
+
488
+ let panelInsights = renderRelatedInsights(panel.insights, panel, true, `dtl-pnl-insight-${panel.panelId}`, isRtl);
489
+ let doctorNotes = renderDoctorNotes(panel.doctorNotes);
490
+ let bioInsights = (doctorNotes || panelInsights) ? `<div class="grid-row biomarker-insights">${doctorNotes}${panelInsights}</div>` : '';
491
+
492
+ detailsSectionHTML += `<div id="${panelCssId}" class="section panel sectionPanel">
493
+ <div class="panel-wrapper">
494
+ <div class="section-content">
495
+ <div class="panel-container">${biomarkerDetailsHtml}</div>
496
+ ${bioInsights}
497
+ </div>
498
+ </div>
499
+ </div>`;
500
+ }
501
+ return detailsSectionHTML;
502
+ }
503
+
504
+ function renderOneInsight(insight, nestingLevel, isRtl) {
505
+ const INDENTATION = {INSIGHT: 10}; // Assuming this is defined globally
506
+ let insightHtml = '';
507
+ const insightCssId = "insight-" + "cb" + insight.id + Math.floor(Math.random() * 3);
508
+ let insightClass = "";
509
+ let content = '';
510
+ let tolerance = "";
511
+ let introductoryStatement = "";
512
+ let nodes = "";
513
+ let doctorContentClass = "hide-me";
514
+ let insightImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAMAAAAjXV6yAAAAdVBMVEUAAACAma+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama/V/8ZIAAAAJnRSTlMATE389dtK6SQE7uTUOc/HlysO3qeJbwnBf1kzaBwWuZ+zYEJ1UwEot4sAAATfSURBVHja7NprcqpAEIbhDwckclPu4iUajb3/JZ78MDWQ1CmhKtKN9LOEt2bGGWwopZRSSimllFJKKaWUUkoppZRSSimllFJKKfVCFs+2vMZxelxhohx6NuMGfh4lp80trTE9Do3G5MnZecfEODQqNyqW01pHDo0tWB+OmA6HxmeSz+nsNIc4mOQwlY3mEA/3tMckOMQlbzJMgENs3I83yGcDbZxn2VXNeRv59EuyhHg20AJPlcXVh2eoKzxAuhECWWm1dqnDb6Q/0kYJZNW7bTdRUEK2kQJZ9SGiNreUvYZGC2QdC5dagk9IxhAIdelTi7+DYByBgItHLZ7kSzVPINx8alkLft4zBcI+IssUcg9qrkBYhGQFco8htkCoArISsZuML9CqdMk6S91kfIGQbckKY8jEGAjXkKwPoUuIMxA2ZPlCP32wBkpzsk4ylxBrIGzkn0K8gWKfrDMk4g304ZEVyD2G2AKhCshKxG4yvkCr0iXrLHWT8QVCtiUrjCETYyBcQ7I+hC4hzkDYkOUL/fTBGijNyTrJXEKsgbCRfwrxBop9ss6QiDfQh0dWIPcYYguEKiArEbvJ+AKtSpess9RNxhcI2ZasMIZMjIFwDcn6ELqEOANhQ5Yv9NMHa6A0J+skcwmxBsJG/inEGyj2yTpDIt5AHx5ZgdxjiC0QqoCsROwm4wu0Kl2yzlI3GV8gZFuywhgyMQbCNSTrQ+gS4gyEDVm+0E8frIHSnKyTzCXEGggb+acQb6DYJ+sMiXgDfXhkBXKPIbZAqAKyErGbjC/QqnTJOkvdZHyBkG3JCmPIxBgI15CsD6FLiDMQNmT5Qj99sAZKc7JOMpcQayBs5J9CvIFin6wzJOIN9OGRFcg9htgCoQrISsRuMr5Aq9Il6yx1k/EFQrYlK4whE2MgXEOyPoQuIc5A2JDlC/30wRoozck6yVxCrIGwkX8K8QaKfbLOkIg30IdHViD3GGILhCogKxG7yfgCrUqXrLPUTcYXCNmWrDCGTIyBcA3J+hC6hDgDYUOWL/TTB2ugNCfrJHMJsQbCRv4pxBso9sk6QyLeQB8eWYHcY4gtEKqArETsJuMLtCpdss5SNxlfIGRbssIYMjEGwjUk60PoEuIMhA1ZvtBPH6yB0pysk8wlxBoIG/mnEG+g2CfrDIl4A314ZAVyjyG2QKgCshKxm4wv0Kp0yTpL3WR8gZBtyQpjyMQYCNeQrA+hS4gzEDZk+UI/fbAGSnOyTjKXEGsgbOSfQryBYp+sMyTiDfThkRXIPYbYAqEKyErEbjK+QKvSJessdZPxBUK2JSuMIRNjIFxDsj6ELiHOQNiQ5Qv99MEaKM3JOsn+3yX0DgAAAAAAAAAAAPi3Dx06cIqm05GlAAAAAElFTkSuQmCC";
515
+ let insightTextClass = "";
516
+ let relatedBiomarkers = "";
517
+
518
+ if (insight.title) {
519
+ content += `<div class='title'>${insight.title}</div>`;
520
+ }
521
+
522
+ if (insight.introductoryStatement) {
523
+ introductoryStatement = insight.introductoryStatement + " ";
524
+ } else {
525
+ introductoryStatement = insight.content;
526
+ }
527
+
528
+ let insightCssIdStyle = '';
529
+ if (nestingLevel > 0) {
530
+ const extraPadding = ((nestingLevel + 10 % 10) + 1)
531
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT;
532
+ insightCssIdStyle = `padding-left: ${levelNesting}px`;
533
+ }
534
+
535
+ function renderOneInsight(insight, nestingLevel, isRtl) {
536
+ const INDENTATION = {INSIGHT: 10}; // Assuming this is defined globally
537
+ let insightHtml = '';
538
+ const insightCssId = "insight-" + "cb" + insight.id.toLowerCase() + Math.floor(Math.random() * 3);
539
+ let insightClass = "";
540
+ let content = '';
541
+ let tolerance = "";
542
+ let introductoryStatement = "";
543
+ let nodes = "";
544
+ let doctorContentClass = "hide-me";
545
+ let insightImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAMAAAAjXV6yAAAAdVBMVEUAAACAma+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama+Ama/V/8ZIAAAAJnRSTlMATE389dtK6SQE7uTUOc/HlysO3qeJbwnBf1kzaBwWuZ+zYEJ1UwEot4sAAATfSURBVHja7NprcqpAEIbhDwckclPu4iUajb3/JZ78MDWQ1CmhKtKN9LOEt2bGGWwopZRSSimllFJKKaWUUkoppZRSSimllFJKKfVCFs+2vMZxelxhohx6NuMGfh4lp80trTE9Do3G5MnZecfEODQqNyqW01pHDo0tWB+OmA6HxmeSz+nsNIc4mOQwlY3mEA/3tMckOMQlbzJMgENs3I83yGcDbZxn2VXNeRv59EuyhHg20AJPlcXVh2eoKzxAuhECWWm1dqnDb6Q/0kYJZNW7bTdRUEK2kQJZ9SGiNreUvYZGC2QdC5dagk9IxhAIdelTi7+DYByBgItHLZ7kSzVPINx8alkLft4zBcI+IssUcg9qrkBYhGQFco8htkCoArISsZuML9CqdMk6S91kfIGQbckKY8jEGAjXkKwPoUuIMxA2ZPlCP32wBkpzsk4ylxBrIGzkn0K8gWKfrDMk4g304ZEVyD2G2AKhCshKxG4yvkCr0iXrLHWT8QVCtiUrjCETYyBcQ7I+hC4hzkDYkOUL/fTBGijNyTrJXEKsgbCRfwrxBop9ss6QiDfQh0dWIPcYYguEKiArEbvJ+AKtSpess9RNxhcI2ZasMIZMjIFwDcn6ELqEOANhQ5Yv9NMHa6A0J+skcwmxBsJG/inEGyj2yTpDIt5AHx5ZgdxjiC0QqoCsROwm4wu0Kl2yzlI3GV8gZFuywhgyMQbCNSTrQ+gS4gyEDVm+0E8frIHSnKyTzCXEGggb+acQb6DYJ+sMiXgDfXhkBXKPIbZAqAKyErGbjC/QqnTJOkvdZHyBkG3JCmPIxBgI15CsD6FLiDMQNmT5Qj99sAZKc7JOMpcQayBs5J9CvIFin6wzJOIN9OGRFcg9htgCoQrISsRuMr5Aq9Il6yx1k/EFQrYlK4whE2MgXEOyPoQuIc5A2JDlC/30wRoozck6yVxCrIGwkX8K8QaKfbLOkIg30IdHViD3GGILhCogKxG7yfgCrUqXrLPUTcYXCNmWrDCGTIyBcA3J+hC6hDgDYUOWL/TTB2ugNCfrJHMJsQbCRv4pxBso9sk6QyLeQB8eWYHcY4gtEKqArETsJuMLtCpdss5SNxlfIGRbssIYMjEGwjUk60PoEuIMhA1ZvtBPH6yB0pysk8wlxBoIG/mnEG+g2CfrDIl4A314ZAVyjyG2QKgCshKxm4wv0Kp0yTpL3WR8gZBtyQpjyMQYCNeQrA+hS4gzEDZk+UI/fbAGSnOyTjKXEGsgbOSfQryBYp+sMyTiDfThkRXIPYbYAqEKyErEbjK+QKvSJessdZPxBUK2JSuMIRNjIFxDsj6ELiHOQNiQ5Qv99MEaKM3JOsn+3yX0DgAAAAAAAAAAAPi3Dx06cIqm05GlAAAAAElFTkSuQmCC";
546
+ let insightTextClass = "";
547
+ let relatedBiomarkers = "";
548
+
549
+ if (insight.title) {
550
+ content += `<div class='title'>${insight.title}</div>`;
551
+ }
552
+
553
+ if (insight.introductoryStatement) {
554
+ introductoryStatement = insight.introductoryStatement + " ";
555
+ } else {
556
+ introductoryStatement = insight.content;
557
+ }
558
+
559
+ let insightCssIdStyle = '';
560
+ if (nestingLevel > 0) {
561
+ const extraPadding = ((nestingLevel + 10 % 10) + 1)
562
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT;
563
+ insightCssIdStyle = `padding-left: ${levelNesting}px`;
564
+ }
565
+
566
+ if (insight.introductoryStatement || insight.summary || insight.testExplained || insight.suggestion) {
567
+ const introductoryStatementContent = introductoryStatement ? introductoryStatement.replace(/(\r\n|\n|\r)/gm, '') : '';
568
+ const insightSummaryContent = insight.summary ? insight.summary : '';
569
+ const testExplainedContent = insight.testExplained ? insight.testExplained : '';
570
+ const suggestionContent = insight.suggestion ? insight.suggestion : '';
571
+ content += `<div class="html-content inline-text introductory-statement">${introductoryStatementContent}</div>`;
572
+ content += `<div class="html-content inline-text testExplained">${testExplainedContent}</div>`;
573
+ content += `<div class="html-content inline-text">${insightSummaryContent}</div>`;
574
+ content += `<div class="html-content">${suggestionContent}</div>`;
575
+
576
+ if (isRtl) {
577
+ content = content.replace(/&rlm;/g, '');
578
+ content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
579
+ content = content.replace(/\)/g, ') &rlm;</span>');
580
+ }
581
+ } else {
582
+ content += introductoryStatement;
583
+ if (isRtl) {
584
+ content = content.replace(/&rlm;/g, '');
585
+ content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
586
+ content = content.replace(/\)/g, ') &rlm;</span>');
587
+ }
588
+ }
589
+
590
+ if (insight.detailedList) {
591
+ const parsedTable = parseTable(insight.detailedList);
592
+ nodes = renderDetails(parsedTable, isRtl);
593
+ doctorContentClass = "";
594
+ }
595
+
596
+ if (introductoryStatement) {
597
+ const span = "<span>";
598
+ if (introductoryStatement.includes(span)) {
599
+ tolerance = stripTags(introductoryStatement, '<span>');
600
+ introductoryStatement = introductoryStatement.replace(/<(.|\n)*?>/g, '');
601
+ introductoryStatement = introductoryStatement.replace(tolerance, '');
602
+ }
603
+ }
604
+
605
+ if (insight.color === "") {
606
+ insightClass = "tip";
607
+ } else {
608
+ insightClass = color(insight.color);
609
+ }
610
+
611
+ // if (insight.icon !== "") {
612
+ // insightImage = getFileAsBase64(insight.icon, 'image/png');
613
+ // }
614
+
615
+ if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
616
+ insightTextClass = "insight-text";
617
+ // Loop through relatedBiomarkers and generate HTML (commented out in PHP)
618
+ }
619
+
620
+ let insightCssIdCss = '';
621
+ if (nestingLevel > 0) {
622
+ const extraPadding = ((nestingLevel + 10 % 10) + 1)
623
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT + 16;
624
+ insightCssIdCss = `padding-left: ${levelNesting}px`;
625
+ }
626
+
627
+ let svgColor = color(insight.color);
628
+ if (insight.isMedical) {
629
+ insightClass += ' medical-insight';
630
+ svgColor = ' medical-insight';
631
+ }
632
+
633
+ if (content || insight.disclaimer || insight.toleranceText) {
634
+ insightHtml += `<div id="${insightCssId}" class="card-wrapper" style="${insightCssIdCss}" insight-id="${insight.id}">
635
+ <div class="card insight-card a ${insightClass}">
636
+ <div class="insight-container">`;
637
+ if (insight.isMedical || insight.insightType === 'doctor-visit') {
638
+ insightHtml += `<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" class="card-svg ${svgColor}">
639
+ <path d="M17 0C15.8955 0 15 0.895447 15 2V15H2C0.895508 15 0 15.8954 0 17V35C0 36.1046 0.895508 37 2 37H15V50C15 51.1046 15.8955 52 17 52H35C36.1045 52 37 51.1046 37 50V37H50C51.1045 37 52 36.1046 52 35V17C52 15.8954 51.1045 15 50 15H37V2C37 0.895416 36.1045 0 35 0H17Z" fill="white"/>
640
+ </svg>`;
641
+ }
642
+ insightHtml += `<div class="text ${insightTextClass}">${content}</div>`;
643
+
644
+ if (insight.disclaimer) {
645
+ insightHtml += `<div class="insight-disclaimer" style="padding: 10px 0px 0">${insight.disclaimer}</div>`;
646
+ }
647
+ if (insight.toleranceText) {
648
+ insightHtml += `<div class="insight-tolerance" style="padding: 10px 0px 0">${insight.toleranceText}</div>`;
649
+ }
650
+ insightHtml += `</div>`;
651
+ if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
652
+ insightHtml += `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>`;
653
+ }
654
+ insightHtml += `</div></div>`;
655
+ }
656
+
657
+ if (nodes || tolerance) {
658
+ insightHtml += `<div class="card-wrapper detailed-list-card-wrapper" style="${insightCssIdCss}">
659
+ <div class="detailed-list-wrapper">
660
+ <div class="detailed-list-title-wrapper">
661
+ <div class="doctor-only-content-container ${doctorContentClass}">
662
+ <h4 class="doctor-only-content-header">${getLocale('doctorContent')}</h4>
663
+ <span class="doctor-only-content-subheader">${getLocale('mostCommonCausesOfTheObservedValue')}</span>
664
+ </div>`;
665
+ if (nodes) {
666
+ insightHtml += `<ul class="detailed-list">${nodes}</ul>`;
667
+ }
668
+ if (tolerance) {
669
+ insightHtml += `<div class="tolerance-text">${tolerance}</div>`;
670
+ }
671
+ insightHtml += `</div></div></div>`;
672
+ }
673
+
674
+ return insightHtml;
675
+ }
676
+
677
+ if (insight.introductoryStatement || insight.summary || insight.testExplained || insight.suggestion) {
678
+ const introductoryStatementContent = introductoryStatement ? introductoryStatement.replace(/(\r\n|\n|\r)/gm, '') : '';
679
+ const insightSummaryContent = insight.summary ? insight.summary : '';
680
+ const testExplainedContent = insight.testExplained ? insight.testExplained : '';
681
+ const suggestionContent = insight.suggestion ? insight.suggestion : '';
682
+ content += `<div class="html-content inline-text introductory-statement">${introductoryStatementContent}</div>`;
683
+ content += `<div class="html-content inline-text testExplained">${testExplainedContent}</div>`;
684
+ content += `<div class="html-content inline-text">${insightSummaryContent}</div>`;
685
+ content += `<div class="html-content">${suggestionContent}</div>`;
686
+
687
+ if (isRtl) {
688
+ content = content.replace(/&rlm;/g, '');
689
+ content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
690
+ content = content.replace(/\)/g, ') &rlm;</span>');
691
+ }
692
+ } else {
693
+ content += introductoryStatement;
694
+ if (isRtl) {
695
+ content = content.replace(/&rlm;/g, '');
696
+ content = content.replace(/\(/g, '&rlm; <span class="ltr-dir">(');
697
+ content = content.replace(/\)/g, ') &rlm;</span>');
698
+ }
699
+ }
700
+
701
+ if (insight.detailedList) {
702
+ const parsedTable = parseTable(insight.detailedList);
703
+ nodes = renderDetails(parsedTable, isRtl);
704
+ doctorContentClass = "";
705
+ }
706
+
707
+ if (introductoryStatement) {
708
+ const span = "<span>";
709
+ if (introductoryStatement.includes(span)) {
710
+ tolerance = stripTags(introductoryStatement, '<span>');
711
+ introductoryStatement = introductoryStatement.replace(/<(.|\n)*?>/g, '');
712
+ introductoryStatement = introductoryStatement.replace(tolerance, '');
713
+ }
714
+ }
715
+
716
+ if (insight.color === "") {
717
+ insightClass = "tip";
718
+ } else {
719
+ insightClass = color(insight.color);
720
+ }
721
+
722
+ // if (insight.icon !== "") {
723
+ // insightImage = getFileAsBase64(insight.icon, 'image/png');
724
+ // }
725
+
726
+ if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
727
+ insightTextClass = "insight-text";
728
+ // Loop through relatedBiomarkers and generate HTML (commented out in PHP)
729
+ }
730
+
731
+ let insightCssIdCss = '';
732
+ if (nestingLevel > 0) {
733
+ const extraPadding = ((nestingLevel + 10 % 10) + 1)
734
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.INSIGHT + 16;
735
+ insightCssIdCss = `padding-left: ${levelNesting}px`;
736
+ }
737
+
738
+ let svgColor = color(insight.color);
739
+ if (insight.isMedical) {
740
+ insightClass += ' medical-insight';
741
+ svgColor = ' medical-insight';
742
+ }
743
+
744
+ if (content || insight.disclaimer || insight.toleranceText) {
745
+ insightHtml += `<div id="${insightCssId}" class="card-wrapper" style="${insightCssIdCss}" insight-id="${insight.id}">
746
+ <div class="card insight-card a ${insightClass}">
747
+ <div class="insight-container">`;
748
+ if (insight.isMedical || insight.insightType === 'doctor-visit') {
749
+ insightHtml += `<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" class="card-svg ${svgColor}">
750
+ <path d="M17 0C15.8955 0 15 0.895447 15 2V15H2C0.895508 15 0 15.8954 0 17V35C0 36.1046 0.895508 37 2 37H15V50C15 51.1046 15.8955 52 17 52H35C36.1045 52 37 51.1046 37 50V37H50C51.1045 37 52 36.1046 52 35V17C52 15.8954 51.1045 15 50 15H37V2C37 0.895416 36.1045 0 35 0H17Z" fill="white"/>
751
+ </svg>`;
752
+ }
753
+ insightHtml += `<div class="text ${insightTextClass}">${content}</div>`;
754
+
755
+ if (insight.disclaimer) {
756
+ insightHtml += `<div class="insight-disclaimer" style="padding: 10px 0px 0">${insight.disclaimer}</div>`;
757
+ }
758
+ if (insight.toleranceText) {
759
+ insightHtml += `<div class="insight-tolerance" style="padding: 10px 0px 0">${insight.toleranceText}</div>`;
760
+ }
761
+ insightHtml += `</div>`;
762
+ if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
763
+ insightHtml += `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>`;
764
+ }
765
+ insightHtml += `</div></div>`;
766
+ }
767
+
768
+ if (nodes || tolerance) {
769
+ insightHtml += `<div class="card-wrapper detailed-list-card-wrapper" style="${insightCssIdCss}">
770
+ <div class="detailed-list-wrapper">
771
+ <div class="detailed-list-title-wrapper">
772
+ <div class="doctor-only-content-container ${doctorContentClass}">
773
+ <h4 class="doctor-only-content-header">${getLocale('doctorContent')}</h4>
774
+ <span class="doctor-only-content-subheader">${getLocale('mostCommonCausesOfTheObservedValue')}</span>
775
+ </div>`;
776
+ if (nodes) {
777
+ insightHtml += `<ul class="detailed-list">${nodes}</ul>`;
778
+ }
779
+ if (tolerance) {
780
+ insightHtml += `<div class="tolerance-text">${tolerance}</div>`;
781
+ }
782
+ insightHtml += `</div></div></div>`;
783
+ }
784
+
785
+ return insightHtml;
786
+ }
787
+
788
+ function renderRelatedInsights(insights, panel, showRelatedBiomarkers = true, idPrefix = '', isRtl = false) {
789
+ let content = '';
790
+ if (insights.length > 0) {
791
+ insights.forEach((insight, insightIndex) => {
792
+ content += renderOneInsight(insight, insightIndex, idPrefix, panel, showRelatedBiomarkers, isRtl);
793
+ });
794
+ }
795
+ return content;
796
+ }
797
+
798
+ function setSummaryStatInsightText(summary) {
799
+ let insightsHasStackedArray = [];
800
+ let temp2 = [...summary];
801
+
802
+ summary.forEach((summaryCheckedItem, index) => {
803
+ if (summaryCheckedItem.stackedInsight !== null) {
804
+ let test = {};
805
+
806
+ temp2.forEach((stackedElement, i) => {
807
+ if (stackedElement && stackedElement.id === summaryCheckedItem.stackedInsight) {
808
+ test.index = i;
809
+ summaryCheckedItem.content += `<br><br>${stackedElement.content}`;
810
+ test.elm = summaryCheckedItem;
811
+ insightsHasStackedArray[index] = stackedElement;
812
+ summary[test.index] = test.elm;
813
+ summary.splice(i, 1);
814
+ }
815
+ });
816
+ }
817
+ });
818
+ return summary;
819
+ }
820
+
821
+ function returnSummaryTemplate(summaryI, isRtl) {
822
+ let summary = setSummaryStatInsightText(summaryI);
823
+ let insightIndex = 0;
824
+ let insightHtml = '';
825
+
826
+ summary.forEach(insight => {
827
+ let insightCssId = `smry-insight${insightIndex}`;
828
+ let relatedBiomarkers = '';
829
+ let insightCssClass = insight.isClinicalReading ? "cr" : insight.color === "" ? "tip" : color(insight.color);
830
+
831
+ let insightText = insight.title ? `<div class="title">${insight.title}</div>` : '';
832
+ let insightContent = insight.content;
833
+
834
+ if (isRtl) {
835
+ insightContent = insightContent.replace(/&rlm;/g, '')
836
+ .replace(/\(/g, '&rlm; <span class="ltr-dir">(')
837
+ .replace(/\)/g, ') &rlm;</span>');
838
+ }
839
+
840
+ insightText += insightContent.replace(/(\r\n|\n|\r)/gm, '<br/>');
841
+
842
+ if (insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0) {
843
+ insight.relatedBiomarkers.forEach(item => {
844
+ relatedBiomarkers += GetRelatedBiomarker(
845
+ GetBiomarkerCircleStyle(item.color, item.name, item.status, item.insightsCount, 'nothing', true, item),
846
+ item.name,
847
+ item.fullName
848
+ );
849
+ });
850
+ }
851
+
852
+ let svgColor = color(insight.color);
853
+ if (insight.isMedical) {
854
+ insightCssClass += ' medical-insight';
855
+ svgColor = ' medical-insight';
856
+ }
857
+
858
+ insightHtml += `<div id="${insightCssId}" class="card insight-card ${insightCssClass}">
859
+ <div class="insight-container">
860
+ <div class="text">${insightText}</div>
861
+ ${insight.toleranceText ? `<div class="text" style="color: #FF3200; padding: 10px 0px 5px 0px;">${insight.toleranceText}</div>` : ''}
862
+ </div>
863
+ ${insight.relatedBiomarkers && insight.relatedBiomarkers.length > 0 ? `<div id="${insightCssId}" class="related-icons has-related ${color(insight.color)}">${relatedBiomarkers}</div>` : ''}
864
+ </div>`;
865
+
866
+ insightIndex++;
867
+ });
868
+ return insightHtml;
869
+ }
870
+
871
+ function renderSummary(data, isRtl) {
872
+ let content = '';
873
+ let reportNotes = '';
874
+ let summary = '';
875
+
876
+ if ((data.summary) || (data.doctorNote && data.doctorNote.length > 0)) {
877
+ if (data.doctorNote && data.doctorNote.length > 0) {
878
+ reportNotes = renderDoctorNotes(data.doctorNote);
879
+ }
880
+ if (data.summary) {
881
+ summary = returnSummaryTemplate(data.summary, isRtl);
882
+ }
883
+ if (reportNotes && summary)
884
+ content += `
885
+ <div id="summary" class="section">
886
+ <div class="resume-title">${localeService.t('Summary')}</div>
887
+ <div class="section-content report-notes">${reportNotes}${summary}</div>
888
+ </div>
889
+ `;
890
+ }
891
+ return content;
892
+ }
893
+
894
+ function renderReportSummary(data, isIMCLab) {
895
+ let content = '';
896
+ if (data.reportSummary || (data.reportProperties && !isIMCLab)) {
897
+ content += '<br/><br/>';
898
+ let reportProperties = '';
899
+
900
+ content += '<div id="report-summary" class="section">';
901
+ if (data.reportSummary) {
902
+ content += `
903
+ <div class="resume-title report-summary-title">${localeService.t('reportSummary')}</div>
904
+ <div class="section-content report-summary-content">${data.reportSummary}</div>
905
+ `;
906
+ }
907
+ /* add report properties */
908
+ if (data.reportProperties && !isIMCLab) {
909
+ for (const [key, value] of Object.entries(data.reportProperties)) {
910
+ reportProperties += `<p>${key}: ${value}</p>`;
911
+ }
912
+ content += `
913
+ <div class="resume-title report-properties-title">${localeService.t('reportProperties')}</div>
914
+ <div class="section-content report-properties-content">${reportProperties}</div>
915
+ `;
916
+ }
917
+ content += '</div>';
918
+ }
919
+
920
+ return content;
921
+ }
922
+
923
+ function renderSignature(data, isRtl, isIMCLab) {
924
+ let content = '';
925
+ let textAlign = isRtl ? 'right' : 'left';
926
+
927
+ // Lab address
928
+ let labAddress = '';
929
+ if (data.labAddress && data.labType !== 18) {
930
+ labAddress = data.labAddress.replace(/\n/g, "<br>");
931
+ }
932
+ content += `<td style="vertical-align: middle;"><div style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;word-break: break-all;">
933
+ ${labAddress}
934
+ </div></td>`;
935
+
936
+ // Lab tel, email, and site
937
+ let labPhone = data.labPhoneNumber ? `${localeService.t('Tel:')} ${data.labPhoneNumber}<br/>` : '';
938
+ let labEmail = data.labEmailAddress ? `${localeService.t('Email:')} ${data.labEmailAddress}<br/>` : '';
939
+ let labSite = data.moreLink ? `${data.moreLink}<br/>` : '';
940
+
941
+ if (data.labType === 18) {
942
+ labSite = "Web: http://www.smartdonor.it/<br/>";
943
+ }
944
+
945
+ if (labPhone || labEmail || labSite) {
946
+ content += `<td style="vertical-align: middle;"><div style="width: 2px; height: 100%;background:rgba(157,169,180, 0.5);min-height: 70px ">&nbsp;</div></td>`;
947
+ }
948
+ content += `<td style="vertical-align: middle;"><div style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;">
949
+ ${labPhone}
950
+ ${labEmail}
951
+ ${labSite}
952
+ </div></td>`;
953
+
954
+ if (data.labType !== 18 && !isIMCLab && (data.labStamp || (data.signatures && data.signatures.length > 0))) {
955
+ content += `<td style="vertical-align: middle;"><div style="width: 2px; height: 100%;background:rgba(157,169,180, 0.5);min-height: 70px">&nbsp;</div></td>`;
956
+ }
957
+
958
+ if (data.labType !== 18 && data.signatures && data.signatures.length > 0) {
959
+ data.signatures.forEach((signature, index) => {
960
+ let approvedBy = signature.name;
961
+ content += `<td style="vertical-align: middle;">
962
+ <div class="approved-by-doctor" style="padding: 0 25px;font-size: 14px;text-align: ${textAlign};line-height: 1.4;">
963
+ <div>${localeService.t('Report approved by')}</div>
964
+ <div>${approvedBy}</div>
965
+ <img class="signature-image" style="height: 50px;width: auto;" src="${signature.signatureURL}">
966
+ </div>
967
+ </td>`;
968
+ });
969
+ }
970
+
971
+ if (data.labStamp && data.labType !== 18) {
972
+ content += `<td><img class="stamp-image a" style="padding: 0 25px;align-self: center;justify-self: center;webkit-align-items:center" src="${data.labStamp}" width="100"></td>`;
973
+ }
974
+
975
+ if (data.labType === 18) {
976
+ content += `<td><img class="stamp-image" style="float: right;padding: 0 25px;align-self: center;justify-self: center;webkit-align-items:center" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAADVCAYAAADtl80QAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAbXUAAHOgAAD83QAAg2QAAHDoAADsaAAAMD4AABCQ5OyZ6gAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAALNxJREFUeNrtXQeYVEW2np4ZZphhhiGIBAUZkkQJIohhwYyKWdeEiCKICUwYFhMgGFcFAwKKomDCgBl1FdEVFHFVQAQFBQWRIEpwhkndr477t+9u29NdVTfVvXP+7zvfvrfL3K7410l1KiODwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwWAwGAwGg8FgMBgMBoPBYDAYDAaDwdDD/wGlgtpU8FGP8wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNS0xMS0yMFQxMjo1MzoyOSswMTowMF975WEAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTUtMTEtMjBUMTI6NTM6MjkrMDE6MDAuJl3dAAAAAElFTkSuQmCC" width="100"></td>`;
977
+ }
978
+
979
+ return content;
980
+ }
981
+
982
+ function showDetailsView(data, isRtl) {
983
+ let biomarkerDetailsHtml = '';
984
+ let detailsSectionHTML = '';
985
+ let shouldShowSectionTitle = false;
986
+
987
+ data.panels.forEach((panel, counter) => {
988
+ /* Biomarker details template */
989
+ biomarkerDetailsHtml = renderBiomarkerDetails(panel, isRtl, counter);
990
+
991
+ /* Panel details template */
992
+ let checkShowInDetailsView = panel.biomarkers?.some(biomarker => biomarker.showInDetailsView);
993
+
994
+ if (panel.biomarkers && checkShowInDetailsView) {
995
+ shouldShowSectionTitle = true;
996
+ detailsSectionHTML += renderPanelDetails(panel, biomarkerDetailsHtml, counter, isRtl);
997
+ }
998
+ });
999
+
1000
+ return detailsSectionHTML;
1001
+ }
1002
+
1003
+ function renderReportItems(items, isCompactView = false, isRtl = false, isRectified = false, isAllergyItem = false, allergyClasses = [], forPatient = false) {
1004
+ let content = '';
1005
+ let counter = 0;
1006
+ let itemCounter = 0;
1007
+
1008
+ items.forEach((dashboardItem, index) => {
1009
+ let nestingLevel = dashboardItem.nestingLevel;
1010
+
1011
+ if (isAllergyItem) {
1012
+ if (itemCounter === 0) {
1013
+ itemCounter++;
1014
+ return;
1015
+ }
1016
+ if (nestingLevel > 0) {
1017
+ nestingLevel -= 1;
1018
+ }
1019
+ }
1020
+
1021
+ let showInCompactView = dashboardItem.showInCompactView || false;
1022
+ let showInDetailsView = dashboardItem.showInDetailsView || false;
1023
+
1024
+ if (dashboardItem.biomarker || (dashboardItem.panel && dashboardItem.panel.panelBiomarker)) {
1025
+ let bio = dashboardItem.biomarker || dashboardItem.panel.panelBiomarker;
1026
+ counter++;
1027
+ let indentationStyle = nestingLevel > 0 ? `margin-left:${nestingLevel * INDENTATION.BIOMARKER_DETAILS}px` : '';
1028
+
1029
+ let biomarkerContent = isAllergyItem
1030
+ ? renderAllergyBiomarker(bio, nestingLevel, dashboardItem.isChildBiomarker, isCompactView, false, isRtl, showInDetailsView, isRectified, allergyClasses, forPatient)
1031
+ : renderBiomarkerV3(bio, nestingLevel, dashboardItem.isChildBiomarker, isCompactView, false, isRtl, showInDetailsView, isRectified);
1032
+
1033
+ if (counter > 1 && !isCompactView && biomarkerContent) {
1034
+ content += '<hr class="biomarker-container-line b">';
1035
+ }
1036
+ content += biomarkerContent;
1037
+ } else if (dashboardItem.panel && !dashboardItem.panel.panelBiomarker && showInCompactView && isCompactView) {
1038
+ content += renderPanelV3(dashboardItem.panel, nestingLevel, isCompactView, itemCounter, isRectified, isAllergyItem);
1039
+ } else if (dashboardItem.panel && !dashboardItem.panel.panelBiomarker && showInDetailsView && !isCompactView) {
1040
+ counter = 0;
1041
+ content += renderPanelV3(dashboardItem.panel, nestingLevel, isCompactView, itemCounter, isRectified, isAllergyItem);
1042
+ } else if (dashboardItem.insight && dashboardItem.showInDetailsView && !isCompactView) {
1043
+ content += renderOneInsight(dashboardItem.insight, nestingLevel, isRtl);
1044
+ } else if (dashboardItem.biomarkerAttachment) {
1045
+ content += renderAttachmentsV3(dashboardItem.biomarkerAttachment, nestingLevel);
1046
+ } else if (dashboardItem.biomarkerDetailsList) {
1047
+ content += renderDetailsListV3(dashboardItem.biomarkerDetailsList, nestingLevel, isCompactView);
1048
+ } else if (dashboardItem.biomarkerNote) {
1049
+ content += renderNoteV3(dashboardItem.biomarkerNote, nestingLevel, isCompactView, showInCompactView, showInDetailsView);
1050
+ }
1051
+ itemCounter++;
1052
+ });
1053
+
1054
+ return content;
1055
+ }
1056
+
1057
+ function renderBiomarkerV3(biomarker, nestingLevel, isChildBiomarker, isCompactView, isComponent = false, isRtl = false, showInDetailsView = true, isRectified = false) {
1058
+ let biomarkerCssId = `biomarker-${("db" + String(biomarker?.id ?? "")).toLowerCase()}${Math.floor(Math.random() * 3)}`;
1059
+ let newBiomarkerHtml = '';
1060
+ let biomarkerClass = '';
1061
+ let biomarkerParentValueStyle = '';
1062
+ let indentationStyle = '';
1063
+
1064
+ if (isCompactView && biomarker.showInCompactView) {
1065
+ let biomarkerName = biomarker.fullName || '';
1066
+ let biomarkerUnit = biomarker.unit;
1067
+ let biomarkerValue = biomarker.formattedValue || biomarker.lingualReading || '';
1068
+ let rangePlaceholder = '';
1069
+ let referenceRange = '';
1070
+ let biomarkerLast = '-';
1071
+ let biomarkerPast = '-';
1072
+
1073
+ if (shouldRenderReadingLevels(biomarker)) {
1074
+ rangePlaceholder = convertToMiniRangeLayout(biomarker.ranges, biomarker, isRtl);
1075
+ }
1076
+
1077
+ if (biomarker.showScale) {
1078
+ let normalRangeObject = biomarker.ranges.find(range => range.isReference) || {};
1079
+ let formattedNormalRangeMin = normalRangeObject.formattedMinValue || null;
1080
+ let formattedNormalRangeMax = normalRangeObject.formattedMaxValue || null;
1081
+ referenceRange = biomarker.ranges.length > 1 ? getRange(biomarker) : '';
1082
+ }
1083
+
1084
+ if (biomarker.color && !biomarker.isNormal) {
1085
+ biomarkerClass = 'risk';
1086
+ biomarkerParentValueStyle = `color:${biomarker.color};`;
1087
+ }
1088
+
1089
+ if (nestingLevel > 0) {
1090
+ let extraPadding = ((nestingLevel + 10 % 10) + 1);
1091
+ let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1092
+ indentationStyle = `padding-left: ${levelNesting}px`;
1093
+ }
1094
+
1095
+ if (biomarker.history && biomarker.history.length > 1) {
1096
+ let lastBiomarkerIndex = biomarker.history.length - 2;
1097
+ biomarkerLast = biomarker.history[lastBiomarkerIndex]?.formattedValue || 0;
1098
+ biomarkerPast = biomarker.history[lastBiomarkerIndex]?.formattedDate || '-';
1099
+ }
1100
+
1101
+ if (biomarkerValue.length > 35) {
1102
+ biomarkerParentValueStyle += "font-size:12px;line-height:1";
1103
+ }
1104
+
1105
+ let firstTdWidth = '';
1106
+ let tdWidth = '';
1107
+ if (isCompactView) {
1108
+ let fullPanelWidth = INDENTATION.PAGE_WIDTH;
1109
+ let tdCount = 7;
1110
+ let RectifiedTd = isRectified ? 32 : 0;
1111
+ tdWidth = (fullPanelWidth - RectifiedTd) / tdCount;
1112
+ firstTdWidth = `${tdWidth * 2}px`;
1113
+ }
1114
+
1115
+ let biomarkerNameAlign = isRtl ? 'text-align: right;' : '';
1116
+ const RTL_FLAG = '\u200E';
1117
+
1118
+ let isValueNumber = !isNaN(Number(biomarkerValue.replace(/\r\n/g, "<br/>")));
1119
+ let valueDataType = isValueNumber ? "number" : "string";
1120
+ let cleanBioMarkerUnit =
1121
+ biomarkerUnit
1122
+ .replace(/[\u200E\u200F\u202A-\u202E]/g, '')
1123
+ .replace(/\(/g, '&#x28;')
1124
+ .replace(/\)/g, '&#x29;');
1125
+
1126
+ newBiomarkerHtml += `<table id="${biomarkerCssId}" style="width:100%; margin: 0 auto;" class="biomarker-block biomarker-block-row ${biomarkerClass}">
1127
+ <tr>
1128
+ <td style="width:${firstTdWidth}">
1129
+ <div class="biom-title name" style="direction: ltr;${biomarkerNameAlign}${indentationStyle}">${biomarkerName}</div>
1130
+ <div class="biom-method method"></div>
1131
+ </td>
1132
+ <td class="biom-value value" style="width:max-content;">
1133
+ <span class="biom-parent-value value-text" data-type="${valueDataType}" style="${biomarkerParentValueStyle}">${biomarkerValue.replace(/\r\n/g, "<br/>")}</span>
1134
+ </td>
1135
+ ${biomarkerUnit ?
1136
+ `<td class="biom-unit unit">
1137
+ <div class="biom-parent-unit unit-text" style="padding: 0 6px; font-family: 'Open Sans', sans-serif !important; direction: ltr !important; unicode-bidi: isolate !important;" dir="ltr">
1138
+ ${cleanBioMarkerUnit}
1139
+ </div>
1140
+ </td>` : ''}
1141
+ ${rangePlaceholder ? `<td class="biom-bar listing" style="width:${tdWidth}px; margin: 0;"><div class="range-placeholder b">${rangePlaceholder}</div></td>` : ''}
1142
+ <td class="biom-range biom-range-data" style="width:${tdWidth}px; margin: 0;"><div class="parent-reference-range range">${referenceRange}</div></td>
1143
+ </tr>
1144
+ </table>`;
1145
+ }
1146
+ // ${isRtl ?RTL_FLAG+cleanBioMarkerUnit:cleanBioMarkerUnit}
1147
+ if (!isCompactView && showInDetailsView) {
1148
+ newBiomarkerHtml += renderOneBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent);
1149
+ }
1150
+
1151
+ if (biomarker.components && biomarker.components.length > 0) {
1152
+ biomarker.components.forEach(comp => {
1153
+ comp.showInDetailsView = biomarker.showInDetailsView;
1154
+ comp.showInCompactView = biomarker.showInCompactView;
1155
+ comp.formattedNormalRangeMin = comp.min;
1156
+ comp.formattedNormalRangeMax = comp.max;
1157
+ newBiomarkerHtml += renderBiomarkerV3(comp, nestingLevel, isChildBiomarker, isCompactView, true, isRtl, showInDetailsView, isRectified);
1158
+ });
1159
+ }
1160
+
1161
+ if ((isCompactView && biomarker.showInCompactView) || (!isCompactView && biomarker.showInDetailsView)) {
1162
+ if (biomarker.notes && biomarker.notes.length > 0) {
1163
+ let BioNoteStyle = '';
1164
+ if (nestingLevel > 0) {
1165
+ let extraPadding = ((nestingLevel + 10 % 10) + 1);
1166
+ let paddingLeft = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + 8;
1167
+ BioNoteStyle = isRtl ? `padding-right:${paddingLeft}px` : `padding-left:${paddingLeft}px`;
1168
+ }
1169
+ let notesPadding = isRtl ? 'padding: 0px 16px 10px 0px;' : 'padding: 0px 0px 10px 16px;';
1170
+ let classNote = !isCompactView ? 'note-margin-bottom' : '';
1171
+ if (useMonospace) {
1172
+ const monoStyle = `font-style: normal; font-size: 13px; color: #333; font-family: 'Roboto Mono', monospace; white-space: pre-wrap; overflow-x: auto;`;
1173
+ newBiomarkerHtml += `<div class="biomarker-notes 2 ${classNote}" style="${notesPadding} ${BioNoteStyle}"><span style="${monoStyle}">${biomarker.notes.join('<br/>')}</span></div>`;
1174
+ } else {
1175
+ newBiomarkerHtml += `<div class="biomarker-notes 2 ${classNote}" style="color: #8F8F92; ${notesPadding} ${BioNoteStyle}">${biomarker.notes.join('<br/>')}</div>`;
1176
+ }
1177
+ }
1178
+ }
1179
+
1180
+ return newBiomarkerHtml;
1181
+ }
1182
+
1183
+ function renderPanelV3(panel, nestingLevel, isCompactView, counter = null, isRectified = false, isAllergyItem = false) {
1184
+
1185
+
1186
+ const panelCssId = `panel-${panel.panelId}db${Math.floor(Math.random() * 3)}`;
1187
+ const notes = panel.notes ? panel.notes.join('') : '';
1188
+ let classPanel = '';
1189
+ if (panel.doctorNotes && isCompactView) {
1190
+ classPanel = 'panel-with-notes';
1191
+ }
1192
+ let panelWidth = isCompactView ? 'width:687px' : '';
1193
+ const colspan = isCompactView ? '1' : '4';
1194
+ const doctorNotesCompactView = panel.doctorNotes && isCompactView
1195
+ ? `<div class="section-content">${renderDoctorNotesCompactView(panel.doctorNotes, nestingLevel)}</div>`
1196
+ : '';
1197
+ const doctorNotesDetailsView = panel.doctorNotes && !isCompactView
1198
+ ? `<div class="section-content-details-view">${renderDoctorNotes(panel.doctorNotes, nestingLevel)}</div>`
1199
+ : '';
1200
+ let newBiomarkerHtml = '';
1201
+ let panelClass = '';
1202
+ if (counter === 0 && !isCompactView) {
1203
+ panelClass = "first-panel";
1204
+ }
1205
+
1206
+ newBiomarkerHtml += `<!--panel-->
1207
+ <div id="${panelCssId}" class="section panel dontend ${panelClass}">`;
1208
+
1209
+ let panelNesting = "margin-left: auto;";
1210
+ let panelTitleFontSize = '';
1211
+ if (nestingLevel > 0) {
1212
+ const extraPadding = ((nestingLevel + 10 % 10) + 1);
1213
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.PANEL_TITLE;
1214
+ panelWidth = INDENTATION.PAGE_WIDTH - levelNesting;
1215
+ panelNesting = `margin-left: ${levelNesting}px;width: ${panelWidth}px;`;
1216
+ panelTitleFontSize = 'font-size:18px';
1217
+ }
1218
+
1219
+ let firstTdWidth = '';
1220
+ const RectifiedTd = isRectified ? 32 : 0;
1221
+ let tdWidth = 0; // Declare tdWidth here to make it accessible outside the if block
1222
+ if (isCompactView) {
1223
+ const fullPanelWidth = INDENTATION.PAGE_WIDTH;
1224
+ const tdCount = 7;
1225
+
1226
+ tdWidth = (fullPanelWidth - RectifiedTd) / tdCount;
1227
+ firstTdWidth = `${tdWidth * 2}px`;
1228
+ }
1229
+
1230
+ newBiomarkerHtml += `<div class="section-content-header ${classPanel}">
1231
+ <div class="panel-title" style="box-sizing: border-box; ${panelNesting}">
1232
+ <table style="width:100%; margin: 0 auto;" class="biomarker-block panel-section-header-new">
1233
+ <tr>`;
1234
+
1235
+ let allergyItemPanelClass = '';
1236
+ if (isAllergyItem && isAllergyItem) {
1237
+ allergyItemPanelClass = 'allergy-panel';
1238
+ if (panel.icon) {
1239
+ newBiomarkerHtml += `<td class="panel-icon-container">
1240
+ <img src="${panel.icon}"/>
1241
+ </td>`;
1242
+ }
1243
+ }
1244
+
1245
+ newBiomarkerHtml += `<td colspan="${colspan}" style="width:${firstTdWidth}">
1246
+ <div class="panel-title-text ${allergyItemPanelClass}" style="${panelTitleFontSize}">${panel.name}</div>
1247
+ ${doctorNotesCompactView}`;
1248
+
1249
+ if (notes) {
1250
+ newBiomarkerHtml += `<div class="panel-notes">${notes}</div>`;
1251
+ }
1252
+
1253
+ newBiomarkerHtml += `</td>`;
1254
+
1255
+ if (isCompactView) {
1256
+ newBiomarkerHtml += `<td class="biom-range" style="width:${tdWidth}px; margin: 0;">
1257
+ ${localeService.t('Reference range')}
1258
+ </td>
1259
+
1260
+ `;
1261
+ if (isRectified) {
1262
+ newBiomarkerHtml += `<td class="biom-past" style="width:${RectifiedTd}px"></td>`;
1263
+ }
1264
+ }
1265
+
1266
+ newBiomarkerHtml += `</tr></table>
1267
+ </div>
1268
+ </div>
1269
+ ${doctorNotesDetailsView}
1270
+ <!--panel END-->`;
1271
+
1272
+ return newBiomarkerHtml;
1273
+ }
1274
+
1275
+ function renderAttachmentsV3(attachment, nestingLevel) {
1276
+ if (!attachment) return '';
1277
+
1278
+ let blockStyle = '';
1279
+ if (nestingLevel > 0) {
1280
+ let extraPadding = ((nestingLevel + 10 % 10) + 1);
1281
+ let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1282
+ blockStyle = `padding-left: ${levelNesting}px`;
1283
+ }
1284
+
1285
+ let ext = attachment.split(';');
1286
+ if (ext[0].toLowerCase() === "data:application/pdf") {
1287
+ return '';
1288
+ } else {
1289
+ let pos = attachment.indexOf(';');
1290
+ let type = attachment.substring(0, pos).split(';');
1291
+ if (type[0].includes('svg')) {
1292
+ return `<div style="${blockStyle}" class="biomarker-attachment-item">${attachment}</div>`;
1293
+ } else {
1294
+ return `<img ${nestingLevel} style="${blockStyle};max-width:320px" class="biomarker-attachment-item" src="${attachment}" />`;
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ function renderDetailsListV3(detailsList, nestingLevel, isCompactView) {
1300
+ let maxRowLength = -1;
1301
+ let content = '';
1302
+ let itemsCount = detailsList.map(item => item.length);
1303
+ let colspanColumn = Math.max(...itemsCount);
1304
+
1305
+ detailsList.forEach(row => {
1306
+ content += "<tr>";
1307
+ row.forEach(item => {
1308
+ let colspan = row.length === 1 ? colspanColumn : 1;
1309
+ content += `<td colspan="${colspan}">${item}</td>`;
1310
+ });
1311
+
1312
+ if (row.length > maxRowLength) {
1313
+ maxRowLength = row.length;
1314
+ }
1315
+
1316
+ if (row.length < maxRowLength) {
1317
+ content += (row.length < colspanColumn && row.length !== 1) ? fillEmptyCellsWith(maxRowLength - row.length) : '';
1318
+ }
1319
+ content += "</tr>";
1320
+ });
1321
+
1322
+ let blockStyle = '';
1323
+ let plusPadding = isCompactView ? 8 : 18;
1324
+ if (nestingLevel > 0) {
1325
+ let extraPadding = ((nestingLevel + 10 % 10) + 1);
1326
+ let levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + plusPadding;
1327
+ blockStyle = `padding-left: ${levelNesting}px`;
1328
+ }
1329
+
1330
+ return `<div style="${blockStyle}" class="biomarker-details-table" style="padding:0 ${plusPadding}px"><table>${content}</table></div>`;
1331
+ }
1332
+
1333
+ function renderNoteV3(note, nestingLevel, isCompactView, showInCompactView, showInDetailsView) {
1334
+ // Standalone biomarkerNote items duplicate the note already rendered inside
1335
+ // renderBiomarkerV3 (biomarker.notes), so they are intentionally suppressed
1336
+ // here to avoid every note appearing twice. Monospace styling for PHA is
1337
+ // handled where biomarker.notes is rendered.
1338
+ return '';
1339
+ }
1340
+
1341
+ function renderAllergyClasses(allergyClasses, isRtl = false) {
1342
+ let count = allergyClasses.length;
1343
+ let tableFloat = isRtl ? 'float: left;' : 'float: right;';
1344
+ let unitAlign = isRtl ? 'text-align: left;right: 0;' : 'text-align: right;left: 0;';
1345
+ let content = `<div class="allergy-classes-container">
1346
+ <span class="allergy-classes-unit" style="${unitAlign}">kU/L</span>
1347
+ <table class="allergy-classes-table colored" width="100%" cellspacing="0" cellpadding="0" style="${tableFloat}">
1348
+ <tr><td colspan="${count}" class="allergy-classes-table-title">${localeService.t('Allergy Reaction Strength')}</td></tr>`;
1349
+
1350
+ let tr1 = '<tr>';
1351
+ let tr2 = '<tr>';
1352
+
1353
+ allergyClasses.forEach(classItem => {
1354
+ let formattedMinValue = classItem.formattedMinValue;
1355
+ let formattedMaxValue = classItem.formattedMaxValue;
1356
+ let range = '';
1357
+
1358
+ if (!formattedMinValue && formattedMaxValue) {
1359
+ range = `< ${formattedMaxValue}`;
1360
+ }
1361
+ if (formattedMinValue && formattedMaxValue) {
1362
+ range = `${formattedMinValue} - ${formattedMaxValue}`;
1363
+ }
1364
+ if (formattedMinValue && !formattedMaxValue) {
1365
+ range = `> ${formattedMinValue}`;
1366
+ }
1367
+
1368
+ tr1 += `<td style="color:${classItem.color}">${classItem.className}</td>`;
1369
+ tr2 += `<td style="color:${classItem.color}">${classItem.lingualReading}<br/>${range}</td>`;
1370
+ });
1371
+
1372
+ tr1 += '</tr>';
1373
+ tr2 += '</tr>';
1374
+
1375
+ content += tr1 + tr2 + '</table></div>';
1376
+ return content;
1377
+ }
1378
+
1379
+ function renderAllergyBiomarker(biomarker, nestingLevel, isChildBiomarker, isCompactView, isComponent = false, isRtl = false, showInDetailsView = true, isRectified = false, allergyClasses = [], forPatient = true) {
1380
+ let newBiomarkerHtml = '';
1381
+
1382
+ if (!isCompactView && showInDetailsView) {
1383
+ newBiomarkerHtml += renderOneAllergyBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent, allergyClasses, forPatient);
1384
+ }
1385
+
1386
+ // Render its values inside components element
1387
+ if (biomarker.components && biomarker.components.length > 0) {
1388
+ biomarker.components.forEach(comp => {
1389
+ comp.showInDetailsView = biomarker.showInDetailsView;
1390
+ comp.showInCompactView = biomarker.showInCompactView;
1391
+ comp.formattedNormalRangeMin = comp.min;
1392
+ comp.formattedNormalRangeMax = comp.max;
1393
+ newBiomarkerHtml += renderAllergyBiomarker(comp, nestingLevel, isChildBiomarker, isCompactView, true, isRtl, showInDetailsView, isRectified, allergyClasses, forPatient);
1394
+ });
1395
+ }
1396
+
1397
+ return newBiomarkerHtml;
1398
+ }
1399
+
1400
+ function renderOneAllergyBiomarker(biomarker, isRtl, nestingLevel, isChildBiomarker, isComponent, allergyClasses, forPatient) {
1401
+ const INDENTATION = {
1402
+ BIOMARKER_DETAILS: 20, // Example value, replace with actual value
1403
+ PANEL_TITLE: 10 // Example value, replace with actual value
1404
+ };
1405
+
1406
+ let biomarkerDetailsHtml = '';
1407
+ if (biomarker.showInDetailsView === true) {
1408
+ const biomarkerCssId = "dtl-bio-db" + String(biomarker?.id ?? "").toLowerCase();
1409
+ let customBiomarkerValueFontSize = '';
1410
+ const formattedValue = biomarker.formattedValue.replace(/,/g, "");
1411
+ if (formattedValue.length > 35) {
1412
+ customBiomarkerValueFontSize = 'font-size:12px;';
1413
+ }
1414
+ const biomarkerName = biomarker.name || '';
1415
+ const biomarkerStatus = biomarker.status || '';
1416
+ const insightsCount = biomarker.insightsCount || '';
1417
+ let CircleStyles = '';
1418
+ if (!isComponent) {
1419
+ CircleStyles = GetBiomarkerCircleStyle(biomarker.color, biomarkerName, biomarkerStatus, insightsCount, '', false);
1420
+ }
1421
+
1422
+ // indent biomarker details based on panel level
1423
+ let firstGridColClass = 'size5';
1424
+ let bioIndentStyle = '';
1425
+ let bioIndentNoteStyle = '';
1426
+ if (nestingLevel > 0) {
1427
+ // replace classes
1428
+ // increase biomarker part
1429
+ firstGridColClass = 'size5';
1430
+ const extraPadding = ((nestingLevel + 10 % 10) + 1);
1431
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS;
1432
+ const levelNestingNote = extraPadding + nestingLevel * INDENTATION.BIOMARKER_DETAILS + 16;
1433
+ bioIndentStyle = 'padding-left: ' + levelNesting + 'px;';
1434
+ bioIndentNoteStyle = 'padding-left: ' + levelNestingNote + 'px';
1435
+ }
1436
+
1437
+ // @note: IMC special case (large biomarker value)
1438
+ let biomarkerClass = '';
1439
+ if (biomarker.formattedValue.length >= 125) {
1440
+ biomarkerClass = 'large-value-class';
1441
+ firstGridColClass = '';
1442
+ //$firstGridColstyle = 'width:100%';
1443
+ }
1444
+
1445
+ const biomarkerValue = biomarker.formattedValue.replace(/\r\n/g, "<br/>");
1446
+
1447
+ if (!biomarker.showScale) {
1448
+ biomarkerClass += ' hide-scale ';
1449
+ }
1450
+
1451
+ const biomarkerColor = biomarker.color && !biomarker.isNormal ? "color:" + biomarker.color + ";" : "";
1452
+ let rangesPlaceholder = "";
1453
+ let currentRange = "";
1454
+ let currentPercent = 25;
1455
+ let biomarkerTitleClass = '';
1456
+ if (biomarker.ranges.length > 1) {
1457
+ biomarkerTitleClass = 'has-ranges';
1458
+ } else {
1459
+ biomarkerTitleClass = 'has-no-ranges';
1460
+ }
1461
+
1462
+ let childRangesPlaceholder = "";
1463
+ let childCurrentRange = "";
1464
+ let heightCount = 1;
1465
+ let bioValue = "";
1466
+ let bioUnit = "";
1467
+ let afterBioInfoValue = '';
1468
+ let History = '';
1469
+ if (biomarker.childrenBiomarkers && biomarker.childrenBiomarkers.length > 0) {
1470
+ biomarker.childrenBiomarkers.forEach(bio => {
1471
+ if (bio.ranges.length > 1) {
1472
+ heightCount++;
1473
+ /* viewing the biomarker and unit of children after the parent ones */
1474
+ bioValue = bio.formattedValue.replace(/\n/g, "<br />");
1475
+ let bioValue_style = "";
1476
+ if (bioValue.length > 33) {
1477
+ bioValue_style = "font-size:12px;";
1478
+ }
1479
+ bioUnit = bio.unit;
1480
+ childCurrentRange = '';
1481
+ /* generating the children ranges and display them under the parent ones */
1482
+ childRangesPlaceholder += '<div class="extra-range-placeholder">'
1483
+ + convertToRangeLayout(bio.ranges, bio, isRtl)
1484
+ + '</div>';
1485
+ biomarkerTitleClass = 'has-ranges';
1486
+ afterBioInfoValue += '<div class="colorable pair">'
1487
+ + '<span class="biomarker-extra-value" style="' + bioValue_style + '">' + bioValue + '</span>'
1488
+ + '<span class="biomarker-extra-unit">' + bioUnit + '</span>'
1489
+ + '</div>';
1490
+ } else {
1491
+ biomarkerTitleClass = 'has-no-ranges';
1492
+ }
1493
+ /*
1494
+ $History = renderBiomarkerHistoryChart($bio);
1495
+ */
1496
+ });
1497
+ } else {
1498
+ /*
1499
+ $History = renderBiomarkerHistoryChart($biomarker);
1500
+ */
1501
+ }
1502
+
1503
+ if (heightCount > 1) {
1504
+ biomarkerClass += 'hide-flag';
1505
+ }
1506
+
1507
+ /* set biomarker history chart */
1508
+
1509
+ const biomarkerFullName = biomarker.fullName || '';
1510
+ let bioName = biomarkerFullName.replace(/\r\n/g, '');
1511
+ bioName = bioName.replace(/\n/g, '');
1512
+ const rangePlaceHolder = convertToRangeLayout(biomarker.ranges, biomarker, isRtl);
1513
+ let biomarkerSampleHTML = '';
1514
+ let biomInfoStyle = '';
1515
+ let biomValueAlignStyle = '';
1516
+ if (biomarker.sample) {
1517
+ biomarkerSampleHTML += '<div class="biom-sample">'
1518
+ + '<span class="biom-sample-value">'
1519
+ + biomarker.sample
1520
+ + '</span>'
1521
+ + '</div>';
1522
+ // $biomValueAlignStyle = "vertical-align: bottom;";
1523
+ }
1524
+ if (!biomarker.sample && (rangePlaceHolder || childRangesPlaceholder || History)) {
1525
+ biomInfoStyle = "margin-top:15px;";
1526
+ }
1527
+
1528
+ // Configurations
1529
+ let biomarkerScale = '';
1530
+ if (isChildBiomarker) {
1531
+ // $biomarkerScale = 'transform:scale(.9);';
1532
+ }
1533
+ let biomCircleCss = '';
1534
+ if (isComponent) {
1535
+ biomCircleCss = 'opacity: 0;';
1536
+ biomarkerSampleHTML = '';
1537
+ bioName = '';
1538
+ }
1539
+ let panelNesting = '';
1540
+ let biomarkerTitleWidth = 'width:260px';
1541
+ if (nestingLevel > 0) {
1542
+ const extraPadding = ((nestingLevel + 10 % 10) + 1);
1543
+ const levelNesting = extraPadding + nestingLevel * INDENTATION.PANEL_TITLE;
1544
+ panelNesting = isRtl ? 'padding-right: ' + levelNesting + 'px;box-sizing:border-box;' : 'padding-left: ' + levelNesting + 'px;box-sizing:border-box;';
1545
+ const width = 260 - levelNesting;
1546
+ biomarkerTitleWidth = 'width:' + width + 'px';
1547
+ }
1548
+
1549
+ if (!rangePlaceHolder && !childRangesPlaceholder && !History) {
1550
+ biomarkerScale += 'width:100%;';
1551
+ }
1552
+ const doctorNotesArr = biomarker.doctorNotes || [];
1553
+ const doctorNotes = renderDoctorNotes(doctorNotesArr, bioIndentNoteStyle);
1554
+
1555
+ biomarkerDetailsHtml += '<div data-id="' + biomarkerCssId + '" class="allergy-biomarker-container biomarker-container grid-col ' + biomarkerClass + '">'
1556
+ + '<!--panel title was her in the first biomarker -->'
1557
+ + '<div class="grid-col ' + firstGridColClass + ' ' + biomarkerClass + '" style="' + biomarkerScale + panelNesting + '">'
1558
+ + '<div class="biomarker">'
1559
+ + '<div class="biom-circle" style="' + removeWhiteSpace(CircleStyles) + ';' + biomCircleCss + '">' + biomarkerName + '</div>'
1560
+ + '<div class="biom-info ' + biomarkerTitleClass + '" style="' + biomInfoStyle + '">'
1561
+ + '<div class="title ' + biomarkerTitleClass + '" style="' + biomarkerTitleWidth + '">' + biomarkerSampleHTML + '<span class="bio-name-info a" style="color: #555558;">' + bioName + '</span></div>'
1562
+ + '<div class="value colorable" style="' + biomValueAlignStyle + '">'
1563
+ + biomarkerSampleHTML
1564
+ + '<div class="pair parent-pair">'
1565
+ + '<span class="value-text">'
1566
+ + '<span style="' + customBiomarkerValueFontSize + biomarkerColor + '">' + biomarkerValue + '</span>'
1567
+ + '</span>'
1568
+ + '<span class="unit colorable">' + biomarker.unit + '</span>'
1569
+ + '</div>'
1570
+ + '</div>'
1571
+ + afterBioInfoValue
1572
+ + '</div>'
1573
+ + '</div>';
1574
+
1575
+ if (doctorNotes) {
1576
+ biomarkerDetailsHtml += '<div class="grid-row biomarker-insights a">'
1577
+ + doctorNotes
1578
+ + '</div>';
1579
+ }
1580
+
1581
+ if (biomarker.notes && biomarker.notes.length > 0) {
1582
+ if (nestingLevel > 0) {
1583
+ // $extraPadding = (($nestingLevel + 10 % 10) + 1);
1584
+ // $levelNesting = $extraPadding + $nestingLevel * $INDENTATION['BIOMARKER_DETAILS'] + 5;
1585
+ // $bioIndentStyle = 'padding-left: ' . $levelNesting . 'px;';
1586
+ }
1587
+ bioIndentStyle = isRtl ? 'padding-right: 16px;' : 'padding-left: 16px;';
1588
+
1589
+ if (useMonospace) {
1590
+ const monoStyle = `font-style: normal; font-size: 13px; color: #333; font-family: 'Roboto Mono', monospace; white-space: pre-wrap; overflow-x: auto;`;
1591
+ biomarkerDetailsHtml += '<div class="biomarker-notes 1" style="padding-top: 10px;' + bioIndentStyle + '">';
1592
+ biomarkerDetailsHtml += `<span style="${monoStyle}">` + biomarker.notes.join('<br/>') + '</span>';
1593
+ biomarkerDetailsHtml += '</div>';
1594
+ } else {
1595
+ biomarkerDetailsHtml += '<div class="biomarker-notes 1" style="color: #8F8F92;padding-top: 10px;' + bioIndentStyle + '">';
1596
+ biomarkerDetailsHtml += biomarker.notes.join('<br/>');
1597
+ biomarkerDetailsHtml += '</div>';
1598
+ }
1599
+ }
1600
+ biomarkerDetailsHtml += '</div> <!-- end first col -->';
1601
+
1602
+ if (allergyClasses) {
1603
+ const rangeIndex = biomarker.rangeIndex;
1604
+ biomarkerDetailsHtml += '<table class="allergy-classes-table" width="100%" cellspacing="0" cellpadding="0" range="' + rangeIndex + '">';
1605
+
1606
+ let tr1 = '<tr>';
1607
+
1608
+ allergyClasses.forEach((cls, index) => { // Renamed 'class' to 'cls' to avoid conflict
1609
+ const formattedMinValue = cls.formattedMinValue;
1610
+ const formattedMaxValue = cls.formattedMaxValue;
1611
+ let range = '';
1612
+
1613
+ if (!formattedMinValue && formattedMaxValue) {
1614
+ range = `< ${formattedMaxValue}`;
1615
+ } else if (formattedMinValue && formattedMaxValue) {
1616
+ range = `${formattedMinValue} - ${formattedMaxValue}`;
1617
+ } else if (formattedMinValue && !formattedMaxValue) {
1618
+ range = `> ${formattedMinValue}`;
1619
+ }
1620
+
1621
+ let value = '';
1622
+
1623
+ if (index === rangeIndex) {
1624
+ let currentRange = biomarker.ranges[rangeIndex];
1625
+ let currentPercent = 0;
1626
+
1627
+ if (currentRange.minValue !== undefined && currentRange.maxValue !== undefined) {
1628
+ currentPercent = ((parseFloat(biomarker.value) - currentRange.minValue) / (currentRange.maxValue - currentRange.minValue)) * 100;
1629
+ }
1630
+
1631
+ currentPercent = Math.max(8, Math.min(77, currentPercent)); // Ensures percent is between 8 and 77
1632
+ const imagePlace = isRtl ? `right: ${currentPercent}%` : `left: ${currentPercent}%`;
1633
+
1634
+ value = `<span index="${rangeIndex}" class="colored-value" patient="${forPatient}" style="background:${cls.color}">`;
1635
+
1636
+ if (!forPatient) {
1637
+ value += biomarker.formattedValue;
1638
+ } else {
1639
+ value += `<span style="${imagePlace}" class="white-circle"></span>`;
1640
+ }
1641
+
1642
+ value += `</span>`;
1643
+ }
1644
+
1645
+ tr1 += `<td>${value}</td>`;
1646
+ });
1647
+ allergyClasses.forEach((cls, index) => { // Renamed 'class' to 'cls' to avoid conflict
1648
+ const formattedMinValue = cls.formattedMinValue;
1649
+ const formattedMaxValue = cls.formattedMaxValue;
1650
+ let range = '';
1651
+
1652
+ if (!formattedMinValue && formattedMaxValue) {
1653
+ range = `< ${formattedMaxValue}`;
1654
+ } else if (formattedMinValue && formattedMaxValue) {
1655
+ range = `${formattedMinValue} - ${formattedMaxValue}`;
1656
+ } else if (formattedMinValue && !formattedMaxValue) {
1657
+ range = `> ${formattedMinValue}`;
1658
+ }
1659
+
1660
+ let value = '';
1661
+
1662
+ if (index === rangeIndex) {
1663
+ let currentRange = biomarker.ranges[rangeIndex];
1664
+ let currentPercent = 0;
1665
+
1666
+ if (currentRange.minValue !== undefined && currentRange.maxValue !== undefined) {
1667
+ currentPercent = ((parseFloat(biomarker.value) - currentRange.minValue) / (currentRange.maxValue - currentRange.minValue)) * 100;
1668
+ }
1669
+
1670
+ currentPercent = Math.max(8, Math.min(77, currentPercent)); // Ensures percent is between 8 and 77
1671
+ const imagePlace = isRtl ? `right: ${currentPercent}%` : `left: ${currentPercent}%`;
1672
+
1673
+ value = `<span index="${rangeIndex}" class="colored-value" patient="${forPatient}" style="background:${cls.color}">`;
1674
+
1675
+ if (!forPatient) {
1676
+ value += biomarker.formattedValue;
1677
+ } else {
1678
+ value += `<span style="${imagePlace}" class="white-circle"></span>`;
1679
+ }
1680
+
1681
+ value += `</span>`;
1682
+ }
1683
+
1684
+ tr1 += `<td>${value}</td>`;
1685
+ });
1686
+
1687
+ tr1 += '</tr>';
1688
+ biomarkerDetailsHtml += tr1 + '</table>';
1689
+ }
1690
+
1691
+ biomarkerDetailsHtml += '</div> <!-- end first close biomarker-container-->';
1692
+ }
1693
+ return biomarkerDetailsHtml;
1694
+ }
1695
+
1696
+ // Helper functions (assuming they exist in your code)
1697
+ function GetBiomarkerCircleStyle(BiomarkerColor, BiomarkerName, BiomarkerStatus, BiomarkerInsightsCount, PanelName, isSummary, item = []) {
1698
+ let CircleStyles = '';
1699
+ let fontSize = BiomarkerName && BiomarkerName.length > 6 ? '12px' : '14px';
1700
+ let color = BiomarkerColor || "#555555";
1701
+ let BioColor = BiomarkerColor || "#E05A5A";
1702
+ let backgroundColor = BioColor ? 'transparent' : "#E05A5A30";
1703
+
1704
+ if (isSummary) {
1705
+ CircleStyles = `background-color: ${backgroundColor}; border: 2px solid ${BioColor}; color: ${color}; font-size: ${fontSize};`;
1706
+ } else {
1707
+ BioColor = BiomarkerColor || "#555555";
1708
+ CircleStyles = `background-color: transparent; border: 2px solid ${BioColor}; color: ${color}; font-size: ${fontSize};`;
1709
+ }
1710
+
1711
+ if ((BiomarkerInsightsCount !== undefined && BiomarkerStatus !== undefined) || BiomarkerInsightsCount === null || BiomarkerStatus === null) {
1712
+ backgroundColor = (BiomarkerInsightsCount || PanelName.toLowerCase() === 'health risk calculators') ? BiomarkerColor : "transparent";
1713
+ let BiomarkerBorder = (BiomarkerInsightsCount || PanelName.toLowerCase() === 'health risk calculators') ? `2px solid ${BiomarkerColor}` : `2px solid ${BiomarkerColor}`;
1714
+ color = BiomarkerInsightsCount ? "#ffffff" : BiomarkerColor;
1715
+ fontSize = BiomarkerName && PanelName.toLowerCase().length > 6 ? "12px" : "14px";
1716
+
1717
+ CircleStyles = `background-color: ${backgroundColor}; border: ${BiomarkerBorder}; color: ${color}; font-size: ${fontSize};`;
1718
+
1719
+ if (BiomarkerStatus !== 1 && BiomarkerStatus !== "1") {
1720
+ fontSize = BiomarkerName && BiomarkerName.length > 7 ? "12px" : "14px";
1721
+ CircleStyles = `background-color: transparent; border: 2px solid rgba(0,0,0,.4); color: rgba(0,0,0,.4); font-size: ${fontSize};`;
1722
+ }
1723
+ }
1724
+ return CircleStyles;
1725
+ }
1726
+
1727
+ function convertToRangeLayout(ranges, biomarker, isRtl) {
1728
+
1729
+ let barHTML = '';
1730
+ if (ranges.length > 1) {
1731
+ barHTML += '<table>';
1732
+ let tr1 = '<tr class="ranges">';
1733
+ let tr2 = '<tr class="labels">';
1734
+ let tr3 = '<tr class="values">';
1735
+ let currentPercent = 50;
1736
+ let currentRange = "";
1737
+
1738
+ ranges.forEach(range => {
1739
+ let isCurrentClass = range.isCurrent ? "current" : "";
1740
+ let labelClass = range.isCurrent ? 'colorable' : "";
1741
+ let rangeTitle = '';
1742
+ let rangeArrow = '';
1743
+
1744
+ if (range.isCurrent) {
1745
+ currentRange = `${range.minValue} - ${range.maxValue}`;
1746
+ if (range.minValue && range.maxValue) {
1747
+ currentPercent = (((parseFloat(biomarker.value) - range.minValue) / (range.maxValue - range.minValue)) * 100);
1748
+ }
1749
+ currentPercent = Math.min(90, currentPercent);
1750
+ let imagePlace = isRtl ? `padding-right:${currentPercent}%` : `padding-left:${currentPercent}%`;
1751
+ rangeArrow = `<img class="range-arrow-layout" style="${imagePlace}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAAAXNSR0IArs4c6QAAATBJREFUWAnNjgmOAjEQA/M0P90/23UhsdICw1w5uqSgIem2q7XWnPNT/ODYlFNdFMcHzm9VWdz+UL6qiuL2D+dfNVmc3lBuqoni9BHntoosLpsoL1VEcfmK87paFoddlInVojgcwplaJUv3YZTJVaJ0n8KZni1L52mUjdmidF7C2ZolS9dllM1ZonTdwtkeLUvHbZSE0aJ0dMFJGSVLdjeUpFGiZHfFSestS2Z3lMTeomQOwUntJUvWMJTkXqJkDcVJvytLxnCUhruiZEzBabkqy+40lKarouxOxWk7K8vOdJTGs6LsLMFpPSrL7DKU5qOizC7Fad+TZWY5isGeKDMlcCy2ZHkrg2KyJcpbKRybV1nuyqEYvYpyVxLH6inLd1kUs6co36Vx7DjlUQw5XfkFNeBu5tFKZ1EAAAAASUVORK5CYII="/>`;
1752
+ }
1753
+
1754
+ rangeTitle = getRangeBar(biomarker, range, biomarker.normalRangeType);
1755
+ let colorClass = color(range.color);
1756
+ tr1 += `<td style="background-position-x:${currentPercent}%;" class="${colorClass} ${isCurrentClass}">${rangeArrow}<div class="range colorablebg" style="background:${colorClass}"></div></td>`;
1757
+ tr2 += `<td class="${isCurrentClass} ${colorClass}"><span class="${labelClass}">${range.name}</span></td>`;
1758
+ tr3 += `<td>${rangeTitle}</td>`;
1759
+ });
1760
+
1761
+ barHTML += tr1 + '</tr>' + tr3 + '</tr>' + tr2 + '</tr>';
1762
+ barHTML += '</table>';
1763
+ }
1764
+ return barHTML;
1765
+ }
1766
+
1767
+
1768
+ function removeWhiteSpace(html) {
1769
+ html = html.replace(/\r\n/g, "");
1770
+ return html.replace(/>\s+</g, "><");
1771
+ }
1772
+
1773
+ function fetchFileContents(filePath) {
1774
+ try {
1775
+ return fs.readFileSync(filePath, 'utf8');
1776
+ } catch (error) {
1777
+ console.error('Error reading file:', error);
1778
+ return null;
1779
+ }
1780
+ }
1781
+
1782
+ function parseTable(string) {
1783
+ let levels = string.split("\n");
1784
+ let list = levels.map(level => {
1785
+ return {
1786
+ id: random(2, 6),
1787
+ content: level,
1788
+ sepCount: separtorsNumbers(level)
1789
+ };
1790
+ });
1791
+
1792
+ let parent = list[0].sepCount;
1793
+
1794
+ for (let i = 0; i < list.length; i++) {
1795
+ if (i === 0) {
1796
+ list[i].parent_id = "0";
1797
+ continue;
1798
+ }
1799
+
1800
+ if (parent === list[i].sepCount) {
1801
+ list[i].parent_id = "0";
1802
+ continue;
1803
+ }
1804
+
1805
+ if (list[i - 1]) {
1806
+ if (list[i].sepCount > list[i - 1].sepCount) {
1807
+ list[i].parent_id = list[i - 1].id;
1808
+ } else if (list[i].sepCount === list[i - 1].sepCount) {
1809
+ list[i].parent_id = list[i - 1].parent_id;
1810
+ } else {
1811
+ list[i].parent_id = findParentId(i - 1, list[i].sepCount, list);
1812
+ }
1813
+ }
1814
+ }
1815
+
1816
+ return list_to_tree(list, "0");
1817
+ }
1818
+
1819
+ function renderDetails(list, isRtl) {
1820
+ if (list.length === 1 && !list[0].children) {
1821
+ return `<li class="1">${list[0].content.substring(list[0].sepCount)}</li>`;
1822
+ }
1823
+
1824
+ let columns = [];
1825
+ let isParent = false;
1826
+
1827
+ list.forEach(child => {
1828
+ if (child.parent_id === "0") isParent = true;
1829
+ let div = '';
1830
+
1831
+ if (!child.children) {
1832
+ let childContent = child.content.substring(child.sepCount);
1833
+ childContent = childContent.replace('(', '(').replace(')', ')');
1834
+
1835
+ if (isRtl) {
1836
+ // Optional RTL handling here
1837
+ }
1838
+
1839
+ div = `<li class="2">${childContent}</li>`;
1840
+ } else {
1841
+ div = `<li class="arrow-icon">${child.content.substring(child.sepCount)}</li>
1842
+ <ul>${renderDetails(child.children, isRtl)}</ul>`;
1843
+ }
1844
+
1845
+ if (div) columns.push(div);
1846
+ });
1847
+
1848
+ return columns.length > 0 ? columns.join('') : null;
1849
+ }
1850
+
1851
+ function GetRelatedBiomarker(biomarkerstyle, name, fullname) {
1852
+ let fontSize = '';
1853
+ if (name && fullname) {
1854
+ if (name.length >= 7) {
1855
+ fontSize = 'small-fontSize-circle';
1856
+ }
1857
+ return `<div class="related">
1858
+ <div class="bicon ${fontSize}" style="${biomarkerstyle}">
1859
+ <span class="bicon-inner">${name}</span>
1860
+ </div>
1861
+ </div>`;
1862
+ }
1863
+ return null;
1864
+ }
1865
+
1866
+ function color(hex) {
1867
+ switch (hex) {
1868
+ case "#E05A5A":
1869
+ return "red";
1870
+ case "#EE734E":
1871
+ return "orange";
1872
+ case "#41B9D6":
1873
+ return "blue";
1874
+ case "#F7AA2E":
1875
+ return "yellow";
1876
+ case "#BC2828":
1877
+ return "dark-red";
1878
+ case "#02D9D1":
1879
+ case "#05B9AF":
1880
+ return "green";
1881
+ case "#0F0F0F":
1882
+ return "black";
1883
+ case "#87B7CE":
1884
+ return "lightblue";
1885
+ case "#FB7377":
1886
+ return "light-red";
1887
+ default:
1888
+ return hex;
1889
+ }
1890
+ }
1891
+
1892
+ function getRangeBar(item, level, normalRangeType = '') {
1893
+
1894
+ let range = '';
1895
+ let min = level.formattedMinValue;
1896
+ let max = level.formattedMaxValue;
1897
+
1898
+ if (item.ranges && item.ranges.length > 0) {
1899
+ let customBioWithTwoRangesAndNormalExist = false;
1900
+ if (item.isCustom && item.ranges.length === 2) {
1901
+ let normalRange = item.ranges.find(range => range.isReference);
1902
+ if (normalRange) customBioWithTwoRangesAndNormalExist = true;
1903
+ }
1904
+
1905
+ let rangeIndex = item.ranges.findIndex(range => range.isReference);
1906
+
1907
+ let readingLevels = item.ranges;
1908
+ let prevItem = rangeIndex - 1;
1909
+
1910
+ if (max !== null || min !== null) {
1911
+
1912
+ if (level.isReference === true) {
1913
+ switch (normalRangeType) {
1914
+ case 0 | "0":
1915
+ range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `<= ${max}` : `>= ${min}`;
1916
+
1917
+ if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1918
+ range = `> ${min}`;
1919
+ }
1920
+ break;
1921
+ case 1 | "1":
1922
+ range = `< ${max}`;
1923
+ break;
1924
+ case 2 | "2":
1925
+ range = `<= ${max}`;
1926
+ break;
1927
+ case 3 | "3":
1928
+ range = `> ${min}`;
1929
+ break;
1930
+ case 4 | "4":
1931
+ range = `>= ${min}`;
1932
+ break;
1933
+ }
1934
+ } else {
1935
+ range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `< ${max}` : `> ${min}`;
1936
+ if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1937
+ range = `>= ${min}`;
1938
+ if (prevItem >= 0 && readingLevels[prevItem]?.formattedMaxValue === min) {
1939
+ range = `> ${min}`;
1940
+ }
1941
+ }
1942
+
1943
+ if (customBioWithTwoRangesAndNormalExist) {
1944
+ switch (normalRangeType) {
1945
+ case "0":
1946
+ range = min !== null && max !== null ? `${min} - ${max}` : min === null ? `< ${max}` : `> ${min}`;
1947
+ break;
1948
+ case "1":
1949
+ range = min !== null ? `>= ${min}` : range;
1950
+ break;
1951
+ case "2":
1952
+ range = min !== null ? `> ${min}` : range;
1953
+ break;
1954
+ case "3":
1955
+ range = max !== null ? `<= ${max}` : range;
1956
+ break;
1957
+ case "4":
1958
+ range = max !== null ? `< ${max}` : range;
1959
+ break;
1960
+ }
1961
+ }
1962
+ }
1963
+ }
1964
+ }
1965
+ return range;
1966
+ }
1967
+
1968
+ function separtorsNumbers(string) {
1969
+ let last = string.lastIndexOf('#');
1970
+ let first = string.indexOf('#');
1971
+
1972
+ if (first !== -1 && first !== 0) {
1973
+ return 0;
1974
+ }
1975
+
1976
+ let subString = string.substring(first, last + 1);
1977
+ let mySplits = subString.split('');
1978
+ let isLevel = mySplits.every(char => char === "#");
1979
+
1980
+ return isLevel ? mySplits.length : separtorsNumbers(string.substring(first, last));
1981
+ }
1982
+
1983
+ function shouldRenderReadingLevels(biomarker, isComponentItem = false) {
1984
+ return biomarker && biomarker.ranges.length > 1 && (biomarker.showScale || isComponentItem);
1985
+ }
1986
+
1987
+ function convertToMiniRangeLayout(ranges, biomarker, isRtl) {
1988
+ let barHTML = '';
1989
+
1990
+ if (ranges.length > 0) {
1991
+ barHTML += '<div class="bar">';
1992
+ ranges.forEach(range => {
1993
+ let colorClass = color(range.color);
1994
+ let label = range.name;
1995
+ let isCurrentClass = range.isCurrent ? "current" : "";
1996
+ let rangeTitle = `${range.minValue} - ${range.maxValue}`;
1997
+ let imgCurrent = '';
1998
+
1999
+ if (range.isCurrent) {
2000
+ let currentRange = `${range.minValue} - ${range.maxValue}`;
2001
+ let currentPercent = null;
2002
+ if (range.minValue && range.maxValue) {
2003
+ currentPercent = (((parseFloat(biomarker.value) - range.minValue) / (range.maxValue - range.minValue)) * 100);
2004
+ }
2005
+ currentPercent = currentPercent / 2;
2006
+ let imagePlace = currentPercent ? `left: ${currentPercent}%` : '';
2007
+ if (isRtl) {
2008
+ imagePlace = currentPercent ? `right: ${currentPercent}%` : '';
2009
+ }
2010
+ imgCurrent = `<img style="${imagePlace}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAhBAMAAAClyt9cAAAAHlBMVEUAAAD///////////////////////////////////8kfJuVAAAACXRSTlMA8ObIrYNJLyOprz29AAAAaUlEQVQoz2OgPyg1mmRagCzQMhMIpjQgBNgkZ4KAGEIkcyYEJMAEOCShIhIwEZaZUDAFJlI5EwZg1kXCRQKgIpZwEQOoiCZcRAEqMhMOJmCqwTQH0y5M92C4GdNfmH7HCB/MMMQMZ7oDAAe/ZbNL9bq2AAAAAElFTkSuQmCC" />`;
2011
+ }
2012
+ barHTML += `<div class="segment ${colorClass} ${isCurrentClass}" style="background:${colorClass}">${imgCurrent}</div>`;
2013
+ });
2014
+ barHTML += '</div>';
2015
+ }
2016
+ return barHTML;
2017
+ }
2018
+
2019
+ function getRange(item, normalRangeKey = "isReference") {
2020
+ let range = '';
2021
+ if (item.ranges && item.ranges.length > 0) {
2022
+ let itemValues = item.ranges.find(range => range[normalRangeKey] === true);
2023
+ let rangeIndex = item.ranges.findIndex(range => range[normalRangeKey] === true);
2024
+ let readingLevels = item.ranges;
2025
+
2026
+ let max = itemValues?.formattedMaxValue ?? null;
2027
+ let min = itemValues?.formattedMinValue ?? null;
2028
+ let normalRangeType = item.normalRangeType;
2029
+ let prevItem = rangeIndex - 1;
2030
+
2031
+ if (max !== null || min !== null) {
2032
+ if (max !== null && min !== null) {
2033
+ range = `${min} - ${max}`;
2034
+ } else if (max !== null) {
2035
+ range = `< ${max}`;
2036
+ } else if (min !== null) {
2037
+ range = `> ${min}`;
2038
+ }
2039
+
2040
+ switch (String(normalRangeType)) {
2041
+ case "0":
2042
+ if (max !== null && min !== null) {
2043
+ range = `${min} - ${max}`;
2044
+ } else if (max !== null) {
2045
+ range = `<= ${max}`;
2046
+ } else if (min !== null) {
2047
+ range = `>= ${min}`;
2048
+ }
2049
+ break;
2050
+ case "1":
2051
+ if (max !== null) range = `< ${max}`;
2052
+ break;
2053
+ case "2":
2054
+ if (max !== null) range = `<= ${max}`;
2055
+ break;
2056
+ case "3":
2057
+ if (min !== null) range = `> ${min}`;
2058
+ break;
2059
+ case "4":
2060
+ if (min !== null) range = `>= ${min}`;
2061
+ break;
2062
+ }
2063
+ }
2064
+ }
2065
+ return range;
2066
+ }
2067
+
2068
+ function find(array, key, value) {
2069
+ for (let single of array) {
2070
+ if (single[key] === value) {
2071
+ return single;
2072
+ }
2073
+ }
2074
+ return null;
2075
+ }
2076
+
2077
+ function renderBiomarkerHistoryChart(biomarker) {
2078
+ let content = '';
2079
+ const history = biomarker.history && biomarker.history.length > 0 ? biomarker.history : [];
2080
+
2081
+ // Custom biomarker shouldn't have a chart
2082
+ if (history.length > 1) {
2083
+ const biomarkerCssChartId = "chart-bio-db" +
2084
+ Math.random().toString(36).substring(2, 8) +
2085
+ String(biomarker?.id ?? "").toLowerCase();
2086
+ content += `<canvas id="${biomarkerCssChartId}" width="100%" height="30" class="chart-canvas"></canvas>`;
2087
+ const dataset = [];
2088
+ const dataColor = [];
2089
+ const dataColorLabels = [];
2090
+ const dataLabel = [];
2091
+ const unit = [];
2092
+ const dataFormattedValues = [];
2093
+
2094
+ history.forEach((item, index) => {
2095
+ dataset[index] = parseInt(biomarker.type) ? parseFloat(item.value) : item.rangeIndex;
2096
+ dataFormattedValues[index] = item.formattedValue;
2097
+ dataColor[index] = item.color;
2098
+ dataColorLabels[index] = item.color === "#E05A5A" ? item.color : "rgba(51,51,51, 0.8)";
2099
+ dataLabel[index] = item.formattedDate;
2100
+ unit[index] = item.unit;
2101
+ });
2102
+
2103
+ const maxOfChart = Math.max(...dataset) + 0.5;
2104
+ const differenceRange = Math.max(...dataset) - Math.min(...dataset);
2105
+ let stepSizeNumber = 0.5;
2106
+ if (differenceRange > 100) {
2107
+ stepSizeNumber = 0;
2108
+ }
2109
+
2110
+ content += `
2111
+ <script>
2112
+ $(document).ready(function() {
2113
+ function showChart() {
2114
+ const ctx = document.getElementById("${biomarkerCssChartId}").getContext("2d");
2115
+ new Chart(ctx, {
2116
+ type: "line",
2117
+ data: {
2118
+ labels: ${JSON.stringify(dataLabel)},
2119
+ datasets: [{
2120
+ data: ${JSON.stringify(dataset)},
2121
+ dataFormattedValues: ${JSON.stringify(dataFormattedValues)},
2122
+ pointBorderColor: "transparent",
2123
+ pointRadius: 6,
2124
+ pointBorderWidth: 2,
2125
+ lineTension: 0,
2126
+ fill: false,
2127
+ pointBackgroundColor: ${JSON.stringify(dataColor)},
2128
+ fill: false
2129
+ }]
2130
+ },
2131
+ options: {
2132
+ layout: {
2133
+ padding: {
2134
+ top: 30,
2135
+ bottom: 30,
2136
+ left: 50,
2137
+ right: 40
2138
+ }
2139
+ },
2140
+ legend: {
2141
+ display: false,
2142
+ fontColor: "black"
2143
+ },
2144
+ tooltips: { enabled: false },
2145
+ plugins: {
2146
+ datalabels: {
2147
+ display: "block",
2148
+ color: ${JSON.stringify(dataColorLabels)},
2149
+ align: "top",
2150
+ anchor: "end",
2151
+ clamp: true,
2152
+ clip: false,
2153
+ font: {
2154
+ lineHeight: 1.2,
2155
+ size: 12,
2156
+ style: "normal"
2157
+ },
2158
+ formatter: function(value, context) {
2159
+ return " " + context.dataset.dataFormattedValues[context.dataIndex];
2160
+ }
2161
+ }
2162
+ },
2163
+ document: true,
2164
+ window: true,
2165
+ hover: false,
2166
+ beginAtZero: false,
2167
+ responsive: true,
2168
+ maintainAspectRatio: true,
2169
+ scales: {
2170
+ yAxes: [{
2171
+ ticks: {
2172
+ suggestedMax: ${maxOfChart},
2173
+ suggestedMin: 0,
2174
+ stepSize: ${stepSizeNumber}
2175
+ },
2176
+ display: false
2177
+ }],
2178
+ xAxes: [{
2179
+ gridLines: {
2180
+ display: false
2181
+ }
2182
+ }]
2183
+ },
2184
+ animation: {
2185
+ onComplete: function() {
2186
+ window.JSREPORT_READY_TO_START = true;
2187
+ }
2188
+ }
2189
+ }
2190
+ });
2191
+ }
2192
+ setTimeout(showChart, 200);
2193
+ });
2194
+ </script>
2195
+ `;
2196
+ }
2197
+
2198
+ return content;
2199
+ }
2200
+
2201
+
2202
+ module.exports = {
2203
+ renderReportItems,
2204
+ find,
2205
+ renderSummary,
2206
+ renderReportSummary,
2207
+ renderHTMLTemplate,
2208
+ renderAllergyClasses,
2209
+ setUseMonospace,
2210
+ }
2211
+
2212
+
2213
+
2214
+
2215
+
2216
+
2217
+
2218
+
2219
+
2220
+
2221
+
2222
+
2223
+
2224
+
2225
+
2226
+
2227
+