diff --git a/src/renderers/InfluenceConditionsRenderer.js b/src/renderers/InfluenceConditionsRenderer.js index b26f7e55b505a3065a19684da4be40ff09ddbfca..b187aec83d5a939b8d1c720cdc311172dce09db7 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; } }