|
|
@@ -1646,26 +1646,79 @@ function renderMandatoryComparisonTable(attributes, title, productType) {
|
|
|
|
|
|
// Populate options
|
|
|
// Add possible values
|
|
|
- attr?.possibleValues.forEach(val => {
|
|
|
- const option = document.createElement('option');
|
|
|
- option.value = val;
|
|
|
- option.textContent = val;
|
|
|
- if (selectedValues.includes(val)) option.selected = true;
|
|
|
- select.appendChild(option);
|
|
|
- });
|
|
|
+ // attr?.possibleValues.forEach(val => {
|
|
|
+ // const option = document.createElement('option');
|
|
|
+ // option.value = val;
|
|
|
+ // option.textContent = val;
|
|
|
+ // if (selectedValues.includes(val)) option.selected = true;
|
|
|
+ // select.appendChild(option);
|
|
|
+ // });
|
|
|
+
|
|
|
+ // attr?.possibleValues.forEach(val => {
|
|
|
+ // // Split by comma if there are multiple values, trim spaces
|
|
|
+ // const values = val.split(',').map(v => v.trim());
|
|
|
+
|
|
|
+ // values.forEach(v => {
|
|
|
+ // const option = document.createElement('option');
|
|
|
+ // option.value = v;
|
|
|
+ // option.textContent = v;
|
|
|
+ // if (selectedValues.includes(v)) option.selected = true;
|
|
|
+ // select.appendChild(option);
|
|
|
+ // });
|
|
|
+ // });
|
|
|
+
|
|
|
|
|
|
// Add AI values not found in possibleValues as new selected options
|
|
|
- selectedValues.forEach(aiVal => {
|
|
|
- if (!attr?.possibleValues.includes(aiVal)) {
|
|
|
- const newOpt = document.createElement('option');
|
|
|
- newOpt.value = aiVal;
|
|
|
- newOpt.textContent = aiVal;
|
|
|
- newOpt.selected = true;
|
|
|
- select.appendChild(newOpt);
|
|
|
- }
|
|
|
- });
|
|
|
+ // selectedValues.forEach(aiVal => {
|
|
|
+ // if (!attr?.possibleValues.includes(aiVal)) {
|
|
|
+ // const newOpt = document.createElement('option');
|
|
|
+ // newOpt.value = aiVal;
|
|
|
+ // newOpt.textContent = aiVal;
|
|
|
+ // newOpt.selected = true;
|
|
|
+ // select.appendChild(newOpt);
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // 1. Prepare and clean the selected values first, flattening the comma-separated data.
|
|
|
+ let allSelectedValues = new Set();
|
|
|
+ selectedValues.forEach(aiVal => {
|
|
|
+ aiVal.split(',')
|
|
|
+ .map(value => value.trim())
|
|
|
+ .filter(value => value.length > 0)
|
|
|
+ .forEach(singleVal => allSelectedValues.add(singleVal));
|
|
|
+ });
|
|
|
+
|
|
|
+ // Convert the Set back to an array for easy checking, or keep it as a Set for O(1) lookups.
|
|
|
+ // We'll keep it as a Set for efficient checking.
|
|
|
|
|
|
+ // ---
|
|
|
|
|
|
+ // 2. Add all default possible values.
|
|
|
+ attr?.possibleValues.forEach(val => {
|
|
|
+ const option = document.createElement('option');
|
|
|
+ option.value = val;
|
|
|
+ option.textContent = val;
|
|
|
+
|
|
|
+ // Check if the possible value is one of the cleaned selected values
|
|
|
+ if (allSelectedValues.has(val)) {
|
|
|
+ option.selected = true;
|
|
|
+ // IMPORTANT: Remove this value from the Set so we know which ones are left over (the custom ones)
|
|
|
+ allSelectedValues.delete(val);
|
|
|
+ }
|
|
|
+
|
|
|
+ select.appendChild(option);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ---
|
|
|
+
|
|
|
+ // 3. Add any "custom" selected values that weren't in possibleValues.
|
|
|
+ // The allSelectedValues Set now only contains values that are NOT in attr?.possibleValues.
|
|
|
+ allSelectedValues.forEach(singleVal => {
|
|
|
+ const newOpt = document.createElement('option');
|
|
|
+ newOpt.value = singleVal;
|
|
|
+ newOpt.textContent = singleVal;
|
|
|
+ newOpt.selected = true; // These are guaranteed to be selected values
|
|
|
+ select.appendChild(newOpt);
|
|
|
+ });
|
|
|
aiTd.appendChild(select);
|
|
|
row.appendChild(aiTd);
|
|
|
|
|
|
@@ -2400,14 +2453,53 @@ function renderAttributesAsTable(attributes, title, mandatoryData = null) {
|
|
|
// if (apiSummary) apiSummary.style.display = 'block';
|
|
|
// }
|
|
|
|
|
|
+// function highlightMatches(text, keywords) {
|
|
|
+// if (!text) return '—';
|
|
|
+// let highlighted = text;
|
|
|
+// keywords.forEach(keyword => {
|
|
|
+// const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape regex
|
|
|
+// const regex = new RegExp(`(${escapedKeyword})`, 'gi');
|
|
|
+// highlighted = highlighted.replace(regex, '<span class="highlight-text">$1</span>');
|
|
|
+// });
|
|
|
+// return highlighted;
|
|
|
+// }
|
|
|
+
|
|
|
function highlightMatches(text, keywords) {
|
|
|
if (!text) return '—';
|
|
|
+
|
|
|
+ // 1. Process Keywords: Flatten the array and handle comma-separated strings
|
|
|
+ const uniqueKeywords = new Set();
|
|
|
+
|
|
|
+ keywords.forEach(compositeKeyword => {
|
|
|
+ // Split by comma, trim whitespace, and add non-empty terms to the Set
|
|
|
+ compositeKeyword.split(',')
|
|
|
+ .map(kw => kw.trim())
|
|
|
+ .filter(kw => kw.length > 0)
|
|
|
+ .forEach(singleKeyword => {
|
|
|
+ // Only add the keyword if it's not just an empty string
|
|
|
+ if (singleKeyword) {
|
|
|
+ uniqueKeywords.add(singleKeyword);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
let highlighted = text;
|
|
|
- keywords.forEach(keyword => {
|
|
|
- const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape regex
|
|
|
- const regex = new RegExp(`(${escapedKeyword})`, 'gi');
|
|
|
- highlighted = highlighted.replace(regex, '<span class="highlight-text">$1</span>');
|
|
|
+
|
|
|
+ // 2. Apply Highlighting using the clean, unique keywords
|
|
|
+ uniqueKeywords.forEach(keyword => {
|
|
|
+ // We still need to check for length in case something slipped through,
|
|
|
+ // though the filter and Set should handle most cases.
|
|
|
+ if (keyword.length > 0) {
|
|
|
+ const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape regex special characters
|
|
|
+
|
|
|
+ // Use 'g' (global) and 'i' (case-insensitive) flags
|
|
|
+ const regex = new RegExp(`(${escapedKeyword})`, 'gi');
|
|
|
+
|
|
|
+ // Use a function as the replacer to ensure we highlight all matches
|
|
|
+ highlighted = highlighted.replace(regex, '<span class="highlight-text">$1</span>');
|
|
|
+ }
|
|
|
});
|
|
|
+
|
|
|
return highlighted;
|
|
|
}
|
|
|
|