1 2/** 3 * Extracts the value from a filter row based on type and operator. 4 * Handles: 5 * - between (from/to inputs) 6 * - multi-select (Choices.js or standard) 7 * - single values 8 * 9 * @param {HTMLElement} row - The .filter-row element 10 * @param {Object} context - { datasetKey: String, defsByField: Object } 11 * @returns {Object|null} - { dataset: String, field: String, op: String, value: Any } or null if invalid 12 */ 13export function serializeFilterRow(row, { datasetKey, defsByField } = {}) { 14 const fieldSelect = row.querySelector('.filter-field') 15 16 // Safety check 17 if (!fieldSelect || !fieldSelect.value) return null 18 19 const field = fieldSelect.value 20 21 // Strict requirement: Definitions must be present to ensure type safety (multiple, type, etc) 22 if (!defsByField) return null 23 24 const def = defsByField[field] 25 26 // Determine Type/Multiple from definition (or fallback to logic if missing, but defs expected) 27 const isMultiple = !!(def && def.multiple) 28 29 const opSelect = row.querySelector('.filter-op') 30 let op = opSelect ? opSelect.value : null 31 if (!op) { 32 op = isMultiple ? 'in' : 'eq' 33 } 34 35 let val = null 36 37 if (op === 'between') { 38 const fromInput = row.querySelector('.filter-value-from') 39 const toInput = row.querySelector('.filter-value-to') 40 const v1 = fromInput ? fromInput.value : '' 41 const v2 = toInput ? toInput.value : '' 42 43 // Save if at least one bound is present 44 if (v1 !== '' || v2 !== '') { 45 val = { from: v1, to: v2 } 46 } 47 } else { 48 const valInput = row.querySelector('.filter-value') 49 if (valInput) { 50 if (isMultiple && valInput.options) { 51 // Handle Multi-select (Choices.js or native) 52 // If Choices.js is active, usually it updates the underlying select options 53 54 val = Array.from(valInput.options) 55 .filter(opt => opt.selected) 56 .map(opt => opt.value) 57 .filter(v => v !== "") 58 59 if (val.length === 0) val = null 60 } else { 61 // Single value or fallback if multiple but text input 62 val = valInput.value 63 // Empty string check 64 if (val === '') val = null 65 66 // If multiple but text, maybe split by comma? 67 // For robustness, let's splits if multiple 68 if (isMultiple && val && typeof val === 'string') { 69 val = val.split(',').map(s => s.trim()).filter(s => s) 70 } 71 } 72 } 73 } 74 75 if (val === null) return null 76 77 const result = { 78 dataset: datasetKey, // Keep for UI Restoration context 79 op, 80 value: val 81 } 82 83 // V2.1: If field is namespaced, it's a dimension 84 if (field.includes('.')) { 85 result.dimension = field 86 } else { 87 result.field = field 88 } 89 90 return result 91};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.