diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js index 3f72688f95b3a6195f003bbd8d4133558f5330f8..b2c583454faff1099074de35232450ddaa90f45b 100644 --- a/src/renderers/MeasurementRenderer.js +++ b/src/renderers/MeasurementRenderer.js @@ -5,7 +5,7 @@ import JSONEditor from 'jsoneditor'; import 'jsoneditor/dist/jsoneditor.css'; import { UsedMethodsRenderer } from './UsedMethodsRenderer.js'; import { InfluenceConditionsRenderer } from './InfluenceConditionsRenderer.js'; - +import {MeasuringEquipmentsRenderer} from './MeasuringEquipmentRenderer.js'; const palette = [ '#1f77b4', '#ff7f0e', @@ -114,6 +114,16 @@ export function renderMeasurementResults(measurementResults, language) { influenceDetails.appendChild(influenceSummary); influenceDetails.appendChild(influenceRenderer.render()); tabPanel.appendChild(influenceDetails); + + // *** NEW: Render Measuring Equipments using the new renderer (collapsible by default) *** + const equipmentsRenderer = new MeasuringEquipmentsRenderer(result['dcc:measuringEquipments'], language); + const equipmentsDetails = document.createElement('details'); + equipmentsDetails.open = false; + const equipmentsSummary = document.createElement('summary'); + equipmentsSummary.textContent = 'Measuring Equipments'; + equipmentsDetails.appendChild(equipmentsSummary); + equipmentsDetails.appendChild(equipmentsRenderer.render()); + tabPanel.appendChild(equipmentsDetails); tabContent.appendChild(tabPanel); }); @@ -161,7 +171,8 @@ function renderInfluenceConditions(measurementResult, language) { return container; } -// Helper to render a single measurement result (charts, tables, etc.) +// In src/renderers/MeasurementRenderer.js, update the renderSingleMeasurementResult function: + export function renderSingleMeasurementResult(resultObj, language, tabPanel) { console.debug('renderSingleMeasurementResult called with:', resultObj); tabPanel.innerHTML = ''; @@ -227,11 +238,12 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { } }); + // Build header texts using dsiUnits conversion. const dataHeaders = dataQuantities.map((q, idx) => { let headerText = q.getName(language); let unit = q.getUnit({ oneLine: true, wrapper: 'span' }); if (!headerText.toLowerCase().includes(" in ")) { - headerText = headerText + " in " + unit; + headerText = `${headerText} in ${unit}`; } return headerText; }); @@ -258,7 +270,8 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { tabPanel.appendChild(scalingContainer); const xAxisContainer = document.createElement('div'); - xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> '; + // Change header text for X-axis selector column + xAxisContainer.innerHTML = '<strong>Frequency in Hz (selected X):</strong> '; indexQuantities.forEach((q, idx) => { let nameStr = q.getName(language) || ('Index ' + idx); const radio = document.createElement('input'); @@ -286,13 +299,12 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { tolContainer.appendChild(tolLabel); tabPanel.appendChild(tolContainer); - // Create containers for plots and table. + // Create containers for plots and table, add extra spacing between them. const subplotsContainer = document.createElement('div'); subplotsContainer.id = 'subplotsContainer'; tabPanel.appendChild(subplotsContainer); const tableContainer = document.createElement('div'); tableContainer.id = 'tableContainer'; - // Add extra spacing between plots and table tableContainer.style.marginTop = '20px'; tabPanel.appendChild(tableContainer); @@ -302,14 +314,15 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { const selectedIndex = selectedRadio.value; const xQuantity = indexQuantities[selectedIndex]; const xValues = xQuantity.getValues(); - // Get xUnit using dsiUnits conversion with oneLine true + // Get xUnit using dsiUnits conversion with oneLine true. const xUnit = xQuantity.getUnit({ oneLine: true, wrapper: 'span' }); const XQuantityName = xQuantity.getName(language); console.debug('Selected X-Axis values:', xValues); console.debug('X-Axis unit:', xUnit); const headers = []; - headers.push(XQuantityName+' in ' + xUnit + ' (selected X)'); + // Use the new header format for the X-axis column. + headers.push(`${XQuantityName} in ${xUnit} (selected X)`); const dataValues = []; const uncertaintiesArray = []; @@ -321,7 +334,8 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { uncertaintiesArray.push(info.uncertainty || []); conformityArray.push(info.conformity ? info.conformity.getConformityValues() : []); }); - headers.push('Comments'); + // Remove the Comments column entirely + // headers.push('Comments'); const tableData = [headers]; for (let i = 0; i < xValues.length; i++) { @@ -330,14 +344,13 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { extraInfo.forEach((info, idx) => { let cellValue = (dataValues[idx][i] !== undefined) ? dataValues[idx][i] : ''; if (uncertaintiesArray[idx] && uncertaintiesArray[idx][i] !== undefined) { - cellValue = cellValue + ' ± ' + uncertaintiesArray[idx][i]; + cellValue = `${cellValue} ± ${uncertaintiesArray[idx][i]}`; } row.push(cellValue); if (info.conformity) { row.push(conformityArray[idx][i] || ''); } }); - row.push(extraInfo.map(info => info.comment || '').filter(c => c).join(' ; ')); tableData.push(row); } renderTable(tableData, computeConformityMapping()); @@ -346,7 +359,7 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { dataQuantities.forEach((q, idx) => { const unit = q.getUnit({ oneLine: false }); if (!unitGroups[unit]) { unitGroups[unit] = []; } - const header = dataHeaders[idx]; + const headerText = dataHeaders[idx]; let values = q.getValues(); const uncertainty = uncertaintiesArray[idx]; const conformity = conformityArray[idx]; @@ -357,7 +370,15 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { } return defaultColor; }); - unitGroups[unit].push({ name: header, y: values, uncertainty, conformity, defaultColor, markerColor: markerColorArray, index: idx }); + unitGroups[unit].push({ + name: headerText, + y: values, + uncertainty, + conformity, + defaultColor, + markerColor: markerColorArray, + index: idx + }); }); subplotsContainer.innerHTML = ''; @@ -371,7 +392,7 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { subplotsContainer.appendChild(graphDiv); plotDivs.push(graphDiv); const groupTraces = group.map(trace => { - let tooltip = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y}'; + let tooltip = `X: %{x} ${xUnit} | ${trace.name}: %{y}`; if (trace.conformity && trace.conformity.length > 0) { tooltip += ' | Conformity: %{customdata}'; } @@ -436,7 +457,7 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { let xaxisTitle = ''; if (groupIdx === unitKeys.length - 1) { let xLabel = 'X: ' + xQuantity.getName(language); - xaxisTitle = '<b>' + xLabel + ' in ' + xUnit + '</b>'; + xaxisTitle = `<b>${xLabel} in ${xUnit}</b>`; } const logX = tabPanel.querySelector('#logXToggle').checked; const logY = tabPanel.querySelector('#logYToggle').checked; @@ -462,7 +483,6 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { margin: { t: 20, b: 40 } }; Plotly.newPlot(graphDiv, allTraces, layout).then(() => { - // Force a resize to ensure full width setTimeout(() => { if (graphDiv.offsetWidth > 0 && graphDiv.offsetHeight > 0) { Plotly.Plots.resize(graphDiv); @@ -471,7 +491,7 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { } }, 100); const caption = document.createElement('div'); - caption.innerHTML = '<b>' + group[0].name + '</b>'; + caption.innerHTML = `<b>${group[0].name}</b>`; caption.style.textAlign = 'center'; caption.style.marginBottom = '5px'; graphDiv.parentNode.insertBefore(caption, graphDiv); @@ -510,7 +530,11 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { } } else { cell.textContent = cellData; - if (tableData[0][cellIndex] && tableData[0][cellIndex].toLowerCase().includes('conformity') && conformityMapping[cellIndex] !== undefined) { + if ( + tableData[0][cellIndex] && + tableData[0][cellIndex].toLowerCase().includes('conformity') && + conformityMapping[cellIndex] !== undefined + ) { const confKey = cellData.trim().toLowerCase(); if (conformityColors[confKey]) { cell.style.backgroundColor = conformityColors[confKey]; @@ -521,8 +545,12 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { cell.style.border = '1px solid #ccc'; tr.appendChild(cell); }); - tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; }); - tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; }); + tr.addEventListener('mouseover', () => { + tr.style.backgroundColor = '#eef'; + }); + tr.addEventListener('mouseout', () => { + tr.style.backgroundColor = ''; + }); table.appendChild(tr); }); tableContainer.appendChild(table); @@ -544,7 +572,9 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { function highlightTableRow(rowIndex) { const rows = tabPanel.querySelector('#tableContainer').querySelectorAll('tr'); - if (rows[rowIndex]) { rows[rowIndex].style.backgroundColor = '#fee'; } + if (rows[rowIndex]) { + rows[rowIndex].style.backgroundColor = '#fee'; + } } function clearTableRowHighlights() { @@ -554,7 +584,9 @@ export function renderSingleMeasurementResult(resultObj, language, tabPanel) { updateVisualization(); const radios = tabPanel.querySelectorAll('input[name="xAxisSelect"]'); - radios.forEach(radio => { radio.addEventListener('change', updateVisualization); }); + radios.forEach(radio => { + radio.addEventListener('change', updateVisualization); + }); tabPanel.querySelector('#logXToggle').addEventListener('change', updateVisualization); tabPanel.querySelector('#logYToggle').addEventListener('change', updateVisualization); tabPanel.querySelector('#toleranceToggle').addEventListener('change', () => { diff --git a/src/renderers/MeasuringEquipmentRenderer.js b/src/renderers/MeasuringEquipmentRenderer.js index 2d23e010ac179faeb6ad1f7e5cd5af42c3608a27..858ddc8cc91010da6548d795cc455c5589cefeb2 100644 --- a/src/renderers/MeasuringEquipmentRenderer.js +++ b/src/renderers/MeasuringEquipmentRenderer.js @@ -1,189 +1,130 @@ -// src/renderers/MeasuringEquipmentRenderer.js -import { DCCRealListQuantity } from "../dccQuantity.js"; +// src/renderers/MeasuringEquipmentsRenderer.js +import { DCCRealListQuantity } from '../dccQuantity.js'; -export class MeasuringEquipmentRenderer { - constructor(equipmentData, language) { - // equipmentData may be a single object or an array of measuringEquipment elements - this.data = Array.isArray(equipmentData) - ? equipmentData - : [equipmentData]; +export class MeasuringEquipmentsRenderer { + constructor(measuringEquipmentsData, language) { + this.data = measuringEquipmentsData; this.language = language; } render() { - const container = document.createElement("div"); - container.innerHTML = `<h3>Measuring Equipment</h3>`; - this.data.forEach((equip) => { - // Create collapsible section for each equipment - const details = document.createElement("details"); - details.style.border = "1px solid #ccc"; - details.style.padding = "8px"; - details.style.marginBottom = "10px"; + // Create a container without an extra header (if you prefer to not duplicate the title) + const container = document.createElement('div'); + // Optionally, you could add a header here if desired: + // container.innerHTML = `<h3>Measuring Equipments</h3>`; - // Build the summary (collapsed view) - const summary = document.createElement("summary"); - summary.style.fontWeight = "bold"; - // Equipment name - const equipName = this._getText(equip["dcc:name"]); + let equipments = this.data && this.data['dcc:measuringEquipment']; + if (!equipments) { + container.textContent = 'No measuring equipments data available.'; + return container; + } + if (!Array.isArray(equipments)) { + equipments = [equipments]; + } + + // Reset default browser styles for details and summary + const detailsStyle = 'margin: 0; padding: 0; border: 1px solid #ccc; padding: 8px; margin-bottom: 10px;'; + const summaryStyle = 'margin: 0; padding: 0; list-style: none;'; + + equipments.forEach(equipment => { + // Each equipment is rendered in a collapsible details block. + const details = document.createElement('details'); + details.setAttribute('style', detailsStyle); + details.open = false; // collapsed by default + + // Create summary for collapsed view + const summary = document.createElement('summary'); + summary.setAttribute('style', summaryStyle); + // Equipment name (language‑specific) + const eqName = this._getText(equipment['dcc:name']); // Manufacturer name (if available) - let manufacturerName = ""; - if (equip["dcc:manufacturer"] && equip["dcc:manufacturer"]["dcc:name"]) { - manufacturerName = this._getText(equip["dcc:manufacturer"]["dcc:name"]); - } + const manufacturer = equipment['dcc:manufacturer'] + ? this._getText(equipment['dcc:manufacturer']['dcc:name']) + : ''; // Model - const model = equip["dcc:model"] ? equip["dcc:model"].trim() : ""; + const model = equipment['dcc:model'] ? equipment['dcc:model'] : ''; // First identification (if available) - let firstId = ""; - if ( - equip["dcc:identifications"] && - equip["dcc:identifications"]["dcc:identification"] - ) { - let ids = equip["dcc:identifications"]["dcc:identification"]; - if (!Array.isArray(ids)) { - ids = [ids]; - } - const id0 = ids[0]; - // Get identification name (small label) and value - const idName = this._getText(id0["dcc:name"]); - const idValue = id0["dcc:value"] ? id0["dcc:value"].trim() : ""; - firstId = `<span style="font-size:smaller;">${idName}: ${idValue}</span>`; + let firstId = ''; + if (equipment['dcc:identifications'] && equipment['dcc:identifications']['dcc:identification']) { + let ident = equipment['dcc:identifications']['dcc:identification']; + if (!Array.isArray(ident)) { ident = [ident]; } + const first = ident[0]; + firstId = `<small>${this._getText(first['dcc:name'])}: ${first['dcc:value']}</small>`; } - summary.innerHTML = `${equipName} – ${manufacturerName} – ${model} ${firstId}`; + summary.innerHTML = `<strong>${eqName}</strong> – ${manufacturer} – Model: ${model} – ${firstId}`; details.appendChild(summary); // Expanded content - const contentDiv = document.createElement("div"); - contentDiv.style.marginTop = "8px"; - contentDiv.style.fontFamily = "sans-serif"; + const contentDiv = document.createElement('div'); + contentDiv.style.marginTop = '8px'; + contentDiv.style.fontFamily = 'sans-serif'; - // Render full identifications table - if (equip["dcc:identifications"] && equip["dcc:identifications"]["dcc:identification"]) { - const ids = Array.isArray(equip["dcc:identifications"]["dcc:identification"]) - ? equip["dcc:identifications"]["dcc:identification"] - : [equip["dcc:identifications"]["dcc:identification"]]; - const idTable = document.createElement("table"); - idTable.style.width = "100%"; - idTable.style.borderCollapse = "collapse"; - // Header row - const idHeaderRow = document.createElement("tr"); - ["Identification", "Issuer", "Value"].forEach((text) => { - const th = document.createElement("th"); - th.textContent = text; - th.style.border = "1px solid #ccc"; - th.style.padding = "4px"; - idHeaderRow.appendChild(th); + // Render full identifications + if (equipment['dcc:identifications']) { + const identDiv = document.createElement('div'); + identDiv.innerHTML = `<h4>Identifications</h4>`; + let ident = equipment['dcc:identifications']['dcc:identification']; + if (!Array.isArray(ident)) { ident = [ident]; } + ident.forEach(id => { + const idName = this._getText(id['dcc:name']); + const idValue = id['dcc:value'] || ''; + const issuer = id['dcc:issuer'] || ''; + identDiv.innerHTML += `<p><small>${idName} (${issuer}): ${idValue}</small></p>`; }); - idTable.appendChild(idHeaderRow); - // Data rows - ids.forEach((id) => { - const tr = document.createElement("tr"); - // Identification name (small label) - const tdName = document.createElement("td"); - tdName.style.border = "1px solid #ccc"; - tdName.style.padding = "4px"; - tdName.style.fontSize = "smaller"; - tdName.textContent = this._getText(id["dcc:name"]); - tr.appendChild(tdName); - // Issuer - const tdIssuer = document.createElement("td"); - tdIssuer.style.border = "1px solid #ccc"; - tdIssuer.style.padding = "4px"; - tdIssuer.textContent = id["dcc:issuer"] ? id["dcc:issuer"].trim() : ""; - tr.appendChild(tdIssuer); - // Value - const tdValue = document.createElement("td"); - tdValue.style.border = "1px solid #ccc"; - tdValue.style.padding = "4px"; - tdValue.textContent = id["dcc:value"] ? id["dcc:value"].trim() : ""; - tr.appendChild(tdValue); - idTable.appendChild(tr); - }); - contentDiv.appendChild(idTable); + contentDiv.appendChild(identDiv); } - // Render measuring equipment quantities table (if available) + // Render measuring equipment quantities as a table if available if ( - equip["dcc:measuringEquipmentQuantities"] && - equip["dcc:measuringEquipmentQuantities"]["dcc:measuringEquipmentQuantity"] + equipment['dcc:measuringEquipmentQuantities'] && + equipment['dcc:measuringEquipmentQuantities']['dcc:measuringEquipmentQuantity'] ) { - const qtys = Array.isArray( - equip["dcc:measuringEquipmentQuantities"]["dcc:measuringEquipmentQuantity"] - ) - ? equip["dcc:measuringEquipmentQuantities"]["dcc:measuringEquipmentQuantity"] - : [equip["dcc:measuringEquipmentQuantities"]["dcc:measuringEquipmentQuantity"]]; - const qtyTable = document.createElement("table"); - qtyTable.style.width = "100%"; - qtyTable.style.borderCollapse = "collapse"; - // Header row - const qtyHeaderRow = document.createElement("tr"); - ["Quantity", "Value", "Unit", "Uncertainty"].forEach((text) => { - const th = document.createElement("th"); + const qtyData = equipment['dcc:measuringEquipmentQuantities']['dcc:measuringEquipmentQuantity']; + const quantities = Array.isArray(qtyData) ? qtyData : [qtyData]; + const table = document.createElement('table'); + table.style.width = '100%'; + table.style.borderCollapse = 'collapse'; + const headerRow = document.createElement('tr'); + ['Quantity', 'Value'].forEach(text => { + const th = document.createElement('th'); th.textContent = text; - th.style.border = "1px solid #ccc"; - th.style.padding = "4px"; - qtyHeaderRow.appendChild(th); + th.style.border = '1px solid #ccc'; + th.style.padding = '4px'; + headerRow.appendChild(th); }); - qtyTable.appendChild(qtyHeaderRow); - // Data rows: use DCCRealListQuantity for each measuringEquipmentQuantity - qtys.forEach((q) => { - const row = document.createElement("tr"); - // Quantity name - const tdName = document.createElement("td"); - tdName.style.border = "1px solid #ccc"; - tdName.style.padding = "4px"; - tdName.textContent = this._getText(q["dcc:name"]); - row.appendChild(tdName); - // Value - const tdValue = document.createElement("td"); - tdValue.style.border = "1px solid #ccc"; - tdValue.style.padding = "4px"; - // Create a DCCRealListQuantity instance to extract values, unit, uncertainty - const realQty = new DCCRealListQuantity(q); - const values = realQty.getValues(); - tdValue.textContent = values.join(" / "); - row.appendChild(tdValue); - // Unit - const tdUnit = document.createElement("td"); - tdUnit.style.border = "1px solid #ccc"; - tdUnit.style.padding = "4px"; - const rawUnit = q["si:realListXMLList"] && q["si:realListXMLList"]["si:unitXMLList"] - ? q["si:realListXMLList"]["si:unitXMLList"].trim() - : ""; - if (rawUnit) { - const unitObj = new DSIUnit(rawUnit); - // Here we return plain text instead of HTML markup for the table cell - tdUnit.textContent = unitObj.toString(); - } - row.appendChild(tdUnit); - // Uncertainty - const tdUnc = document.createElement("td"); - tdUnc.style.border = "1px solid #ccc"; - tdUnc.style.padding = "4px"; - const uncValues = realQty.getUncertainty(); - tdUnc.textContent = uncValues.length ? uncValues.join(" / ") : ""; - row.appendChild(tdUnc); - qtyTable.appendChild(row); + table.appendChild(headerRow); + quantities.forEach(q => { + const tr = document.createElement('tr'); + const nameCell = document.createElement('td'); + nameCell.style.border = '1px solid #ccc'; + nameCell.style.padding = '4px'; + nameCell.textContent = this._getText(q['dcc:name']); + tr.appendChild(nameCell); + const valueCell = document.createElement('td'); + valueCell.style.border = '1px solid #ccc'; + valueCell.style.padding = '4px'; + // Render value and unit using DCCRealListQuantity + let qtyObj = new DCCRealListQuantity(q); + valueCell.innerHTML = qtyObj.getValues().join(' ') + ' ' + qtyObj.getUnit({ oneLine: true }); + tr.appendChild(valueCell); + table.appendChild(tr); }); - contentDiv.appendChild(qtyTable); + contentDiv.appendChild(table); } - // Append expanded content to details details.appendChild(contentDiv); container.appendChild(details); }); return container; } - // Helper to extract language-specific text from a node _getText(node) { - if (!node) return ""; - const content = node["dcc:content"]; + if (!node) return ''; + const content = node['dcc:content']; if (Array.isArray(content)) { - const match = content.find((item) => item.$ && item.$.lang === this.language) || content[0]; + const match = content.find(item => item.$ && item.$.lang === this.language) || content[0]; return match._ || match; } return content._ || content; } - - // (Optional) You can add additional helper methods to render other parts of the data }