From abba068808afb2e85038558a98a04186d3bf221a Mon Sep 17 00:00:00 2001
From: Benedikt Seeger <benedikt.seeger@ptb.de>
Date: Tue, 4 Mar 2025 13:10:17 +0100
Subject: [PATCH] improved influence condition renderer

---
 src/renderers/InfluenceConditionsRenderer.js | 180 ++++++++++++-------
 1 file changed, 116 insertions(+), 64 deletions(-)

diff --git a/src/renderers/InfluenceConditionsRenderer.js b/src/renderers/InfluenceConditionsRenderer.js
index b26f7e5..b187aec 100644
--- a/src/renderers/InfluenceConditionsRenderer.js
+++ b/src/renderers/InfluenceConditionsRenderer.js
@@ -1,4 +1,6 @@
 // src/renderers/InfluenceConditionsRenderer.js
+import { DSIUnit } from "dsiunits-js";
+
 export class InfluenceConditionsRenderer {
     constructor(influenceConditionsData, language) {
         this.data = influenceConditionsData;
@@ -17,40 +19,44 @@ export class InfluenceConditionsRenderer {
             conditions = [conditions];
         }
         conditions.forEach(condition => {
+            // Create collapsible section for each influence condition
             const details = document.createElement('details');
             details.style.border = '1px solid #ccc';
             details.style.padding = '8px';
             details.style.marginBottom = '10px';
 
-            // Summary: influence condition name (language-specific)
+            // Summary always shows condition name and (if only one quantity) its value/unit
             const summary = document.createElement('summary');
-            summary.textContent = this._getText(condition['dcc:name'], this.language);
+            const condName = this._getText(condition['dcc:name']);
+            let summaryText = condName;
+            const quantities = this._getQuantities(condition);
+            if (quantities.length === 1) {
+                // If only one quantity, append its value and unit
+                const qty = quantities[0];
+                const valueStr = this._formatQuantity(qty);
+                summaryText += `:  ${valueStr}`;
+            }
+            summary.textContent = summaryText;
             details.appendChild(summary);
 
             // Expanded content: description and quantities table
             const contentDiv = document.createElement('div');
             contentDiv.style.marginTop = '8px';
             contentDiv.style.fontFamily = 'sans-serif';
-            const descText = this._getText(condition['dcc:description'], this.language);
+            const descText = this._getText(condition['dcc:description']);
             if (descText) {
                 const p = document.createElement('p');
                 p.textContent = descText;
                 contentDiv.appendChild(p);
             }
-
-            // If data exists, render quantities table
+            // Render quantities table (if any)
             if (condition['dcc:data'] && condition['dcc:data']['dcc:quantity']) {
-                let quantities = condition['dcc:data']['dcc:quantity'];
-                if (!Array.isArray(quantities)) {
-                    quantities = [quantities];
-                }
                 const table = document.createElement('table');
                 table.style.width = '100%';
                 table.style.borderCollapse = 'collapse';
-
-                // Create header row with columns: Quantity, Value, Description, Additional Info
+                // Header row
                 const headerRow = document.createElement('tr');
-                ['Quantity', 'Value', 'Description', 'Additional Info'].forEach(text => {
+                ['Quantity', 'Value', 'Description'].forEach(text => {
                     const th = document.createElement('th');
                     th.textContent = text;
                     th.style.border = '1px solid #ccc';
@@ -58,46 +64,39 @@ export class InfluenceConditionsRenderer {
                     headerRow.appendChild(th);
                 });
                 table.appendChild(headerRow);
-
-                // Render each quantity in its own row.
-                quantities.forEach(q => {
+                // Data rows for each quantity
+                this._getQuantities(condition).forEach(q => {
                     const row = document.createElement('tr');
-
-                    // Column 1: Quantity name
+                    // Quantity name cell
                     const nameCell = document.createElement('td');
                     nameCell.style.border = '1px solid #ccc';
                     nameCell.style.padding = '4px';
-                    nameCell.textContent = this._getText(q['dcc:name'], this.language);
+                    nameCell.textContent = this._getText(q['dcc:name']);
                     row.appendChild(nameCell);
-
-                    // Column 2: Value merged with uncertainty and unit
+                    // Value cell with merged unit and uncertainty, plus tooltip for additional info
                     const valueCell = document.createElement('td');
                     valueCell.style.border = '1px solid #ccc';
                     valueCell.style.padding = '4px';
-                    valueCell.innerHTML = this._formatQuantity(q);
+                    const formattedValue = this._formatQuantity(q);
+                    valueCell.innerHTML = formattedValue;
+                    // If there is additional (non-standard) data, add a tooltip
+                    const additional = this._getAdditionalData(q);
+                    if (Object.keys(additional).length > 0) {
+                        const infoSpan = document.createElement('span');
+                        infoSpan.textContent = ' ⓘ';
+                        infoSpan.style.cursor = 'pointer';
+                        infoSpan.style.color = '#888';
+                        // Use the title attribute for a basic tooltip (or later replace with a dynamic tree)
+                        infoSpan.title = JSON.stringify(additional, null, 2);
+                        valueCell.appendChild(infoSpan);
+                    }
                     row.appendChild(valueCell);
-
-                    // Column 3: Description (if available)
+                    // Description cell
                     const descCell = document.createElement('td');
                     descCell.style.border = '1px solid #ccc';
                     descCell.style.padding = '4px';
-                    descCell.textContent = this._getText(q['dcc:description'], this.language);
+                    descCell.textContent = this._getText(q['dcc:description']);
                     row.appendChild(descCell);
-
-                    // Column 4: Additional info (render any other keys as JSON string)
-                    const additionalCell = document.createElement('td');
-                    additionalCell.style.border = '1px solid #ccc';
-                    additionalCell.style.padding = '4px';
-                    // Remove known keys from q and show the rest
-                    const additional = {};
-                    Object.keys(q).forEach(key => {
-                        if (!['dcc:name', 'dcc:description', 'si:real', 'si:realListXMLList'].includes(key)) {
-                            additional[key] = q[key];
-                        }
-                    });
-                    additionalCell.textContent = Object.keys(additional).length > 0 ? JSON.stringify(additional) : '';
-                    row.appendChild(additionalCell);
-
                     table.appendChild(row);
                 });
                 contentDiv.appendChild(table);
@@ -108,65 +107,118 @@ export class InfluenceConditionsRenderer {
         return container;
     }
 
-    _getText(node, language) {
+    // Helper: return language-specific text from a node
+    _getText(node) {
         if (!node) return '';
         const content = node['dcc:content'];
         if (Array.isArray(content)) {
-            const match = content.find(item => item.$ && item.$.lang === language) || content[0];
+            const match = content.find(item => item.$ && item.$.lang === this.language) || content[0];
             return match._ || match;
         }
         return content._ || content;
     }
 
+    // Helper: extract quantities array from an influence condition
+    _getQuantities(condition) {
+        let quantities = [];
+        if (condition['dcc:data'] && condition['dcc:data']['dcc:quantity']) {
+            quantities = Array.isArray(condition['dcc:data']['dcc:quantity'])
+                ? condition['dcc:data']['dcc:quantity']
+                : [condition['dcc:data']['dcc:quantity']];
+        }
+        return quantities;
+    }
+
+    // Helper: format a quantity’s value (supporting si:hybrid, si:realListXMLList, si:real, and dcc:noQuantity)
     _formatQuantity(quantity) {
-        let result = '';
+        // Handle noQuantity (categorical data)
+        if (quantity['dcc:noQuantity']) {
+            return this._getText(quantity['dcc:noQuantity']);
+        }
+        // Handle si:hybrid: if there are two si:real elements, render first normally and second in light gray.
         if (quantity['si:hybrid']) {
             const hybrid = quantity['si:hybrid'];
             let reals = hybrid['si:real'];
             if (!Array.isArray(reals)) {
                 reals = [reals];
             }
-            const formatted = reals.map(real => {
+            const formatted = reals.map((real, index) => {
                 const value = real['si:value'] ? real['si:value'].trim() : '';
-                const unit = real['si:unit'] ? real['si:unit'].trim() : '';
+                let unit = '';
+                if (real['si:unit']) {
+                    const rawUnit = real['si:unit'].trim();
+                    const unitObj = new DSIUnit(rawUnit);
+                    unit = unitObj.toHTML({ oneLine: true });
+                }
                 let uncertainty = '';
                 if (real['si:measurementUncertaintyUnivariate'] &&
-                    real['si:measurementUncertaintyUnivariate']['si:expandedMU']) {
-                    uncertainty = real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU']
-                        ? real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU'].trim()
-                        : '';
+                    real['si:measurementUncertaintyUnivariate']['si:expandedMU'] &&
+                    real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU']) {
+                    uncertainty = real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU'].trim();
                 }
-                return uncertainty ? `${value} ± ${uncertainty} ${unit}` : `${value} ${unit}`;
+                let result = uncertainty
+                    ? `${value} ± ${uncertainty} ${unit}`
+                    : `${value} ${unit}`;
+                if (index > 0) {
+                    // For subsequent representations, wrap in a light gray span.
+                    result = `<span style="color:lightgray;">${result}</span>`;
+                }
+                return result;
             });
-            result = formatted.join(' / ');
-        } else if (quantity['si:realListXMLList']) {
-            const realList = quantity['si:realListXMLList'];
-            const values = realList['si:valueXMLList'] ? realList['si:valueXMLList'].trim().split(/\s+/) : [];
-            const unit = realList['si:unitXMLList'] ? realList['si:unitXMLList'].trim() : '';
+            return formatted.join(' / ');
+        }
+        // Handle si:realListXMLList
+        if (quantity['si:realListXMLList']) {
+            const rl = quantity['si:realListXMLList'];
+            const values = rl['si:valueXMLList'] ? rl['si:valueXMLList'].trim().split(/\s+/) : [];
+            let unit = '';
+            if (rl['si:unitXMLList']) {
+                const rawUnit = rl['si:unitXMLList'].trim();
+                const unitObj = new DSIUnit(rawUnit);
+                unit = unitObj.toHTML({ oneLine: true });
+            }
             let uncertainty = [];
-            if (realList['si:measurementUncertaintyUnivariateXMLList'] &&
-                realList['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']) {
-                uncertainty = realList['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList']
-                    ? realList['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList'].trim().split(/\s+/)
-                    : [];
+            if (rl['si:measurementUncertaintyUnivariateXMLList'] &&
+                rl['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList'] &&
+                rl['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList']) {
+                uncertainty = rl['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList'].trim().split(/\s+/);
             }
             const formatted = values.map((val, i) => {
                 const unc = (uncertainty[i] !== undefined) ? uncertainty[i] : '';
                 return unc ? `${val} ± ${unc} ${unit}` : `${val} ${unit}`;
             });
-            result = formatted.join(' / ');
-        } else if (quantity['si:real']) {
+            return formatted.join(' / ');
+        }
+        // Handle si:real
+        if (quantity['si:real']) {
             const real = quantity['si:real'];
             const value = real['si:value'] ? real['si:value'].trim() : '';
-            const unit = real['si:unit'] ? real['si:unit'].trim() : '';
+            let unit = '';
+            if (real['si:unit']) {
+                const rawUnit = real['si:unit'].trim();
+                const unitObj = new DSIUnit(rawUnit);
+                unit = unitObj.toHTML({ oneLine: true });
+            }
             let uncertainty = '';
             if (real['si:measurementUncertaintyUnivariate'] &&
                 real['si:measurementUncertaintyUnivariate']['si:expandedMU'] &&
                 real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU']) {
                 uncertainty = real['si:measurementUncertaintyUnivariate']['si:expandedMU']['si:valueExpandedMU'].trim();
             }
-            result = uncertainty ? `${value} ± ${uncertainty} ${unit}` : `${value} ${unit}`;
+            return uncertainty ? `${value} ± ${uncertainty} ${unit}` : `${value} ${unit}`;
         }
-        return result;
+        return '';
+    }
+
+    // Helper: extract additional data (all keys except the common ones)
+    _getAdditionalData(quantity) {
+        const exclude = ['dcc:name', 'dcc:description', 'si:real', 'si:realListXMLList', 'dcc:noQuantity'];
+        const additional = {};
+        Object.keys(quantity).forEach(key => {
+            if (!exclude.includes(key)) {
+                additional[key] = quantity[key];
+            }
+        });
+        return additional;
     }
 }
-- 
GitLab