@jinntec/fore 2.6.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/fore-dev.js +4472 -2645
  2. package/dist/fore.js +4417 -2622
  3. package/index.js +2 -0
  4. package/package.json +10 -4
  5. package/resources/fore.css +2 -6
  6. package/src/DependencyNotifyingDomFacade.js +27 -21
  7. package/src/ForeElementMixin.js +282 -277
  8. package/src/actions/abstract-action.js +7 -12
  9. package/src/actions/fx-append.js +23 -2
  10. package/src/actions/fx-call.js +2 -2
  11. package/src/actions/fx-delete.js +54 -29
  12. package/src/actions/fx-insert.js +23 -15
  13. package/src/actions/fx-load.js +2 -2
  14. package/src/actions/fx-refresh.js +15 -5
  15. package/src/actions/fx-replace.js +18 -3
  16. package/src/actions/fx-reset.js +6 -0
  17. package/src/actions/fx-send.js +10 -7
  18. package/src/actions/fx-setattribute.js +12 -0
  19. package/src/actions/fx-setfocus.js +11 -8
  20. package/src/actions/fx-setvalue.js +93 -93
  21. package/src/actions/fx-toggle.js +1 -1
  22. package/src/extract-predicate-deps.js +57 -0
  23. package/src/extractPredicateDependencies.js +36 -0
  24. package/src/fore.js +41 -16
  25. package/src/fx-bind.js +128 -23
  26. package/src/fx-connection.js +24 -7
  27. package/src/fx-fore.js +506 -259
  28. package/src/fx-instance.js +9 -11
  29. package/src/fx-model.js +154 -65
  30. package/src/fx-submission.js +19 -30
  31. package/src/fx-var.js +5 -0
  32. package/src/getInScopeContext.js +7 -8
  33. package/src/modelitem.js +247 -112
  34. package/src/tools/fx-action-log.js +21 -19
  35. package/src/tools/fx-log-settings.js +4 -2
  36. package/src/ui/TemplateExpression.js +12 -0
  37. package/src/ui/UIElement.js +125 -10
  38. package/src/ui/abstract-control.js +5 -0
  39. package/src/ui/fx-case.js +15 -3
  40. package/src/ui/fx-container.js +5 -0
  41. package/src/ui/fx-control-menu.js +23 -14
  42. package/src/ui/fx-control.js +55 -15
  43. package/src/ui/fx-items.js +10 -4
  44. package/src/ui/fx-repeat-attributes.js +111 -35
  45. package/src/ui/fx-repeat.js +332 -85
  46. package/src/ui/fx-repeatitem.js +23 -20
  47. package/src/ui/fx-switch.js +5 -3
  48. package/src/ui/fx-upload.js +36 -40
  49. package/src/ui/repeat-base.js +532 -0
  50. package/src/withDraggability.js +8 -4
  51. package/src/xpath-evaluation.js +26 -8
  52. package/src/xpath-path.js +79 -0
  53. package/src/xpath-util.js +357 -289
@@ -1,21 +1,143 @@
1
1
  import ForeElementMixin from '../ForeElementMixin.js';
2
2
  import { Fore } from '../fore.js';
3
+ import { evaluateXPath, evaluateXPathToBoolean, resolveId } from '../xpath-evaluation';
4
+ import { DependencyNotifyingDomFacade } from '../DependencyNotifyingDomFacade';
3
5
 
4
6
  export class UIElement extends ForeElementMixin {
5
7
  constructor() {
6
8
  super();
9
+
10
+ this._removeEventListeners = [];
7
11
  }
8
12
 
9
13
  connectedCallback() {
10
14
  super.connectedCallback();
11
- this.ondemand = this.hasAttribute('on-demand') ? true : false;
15
+ this.ondemand = this.hasAttribute('on-demand');
12
16
  this.wasOnDemandInitially = this.ondemand;
17
+
13
18
  if (this.ondemand) {
14
19
  this.addEventListener('show-control', () => {
15
20
  this.removeAttribute('on-demand');
16
21
  });
17
22
  this.addTrashIcon();
18
23
  }
24
+
25
+ const ref = this.getAttribute('ref');
26
+ // TODO: make this smarter, handling multiple index functions etc
27
+ if (ref && ref.includes('index(')) {
28
+ const repeatId = ref.match(/index\(['"](?<repeatId>[^'"]*)['"]\)/)?.groups?.repeatId;
29
+ if (repeatId) {
30
+ /**
31
+ * @type {import('./fx-repeat.js').FxRepeat}
32
+ */
33
+ const repeat = resolveId(repeatId, this, 'fx-repeat');
34
+ const onRepeatItemChanged = () => {
35
+ this.getOwnerForm().addToBatchedNotifications(this);
36
+ };
37
+ repeat.addEventListener('item-changed', onRepeatItemChanged);
38
+ this._removeEventListeners.push(() =>
39
+ repeat.removeEventListener('item-changed', onRepeatItemChanged),
40
+ );
41
+ }
42
+ }
43
+ }
44
+
45
+ disconnectedCallback() {
46
+ if (this.modelItem && typeof this.modelItem.removeObserver === 'function') {
47
+ console.log(`[UIElement] Removing observer for ref="${this.ref}"`);
48
+ this.modelItem.removeObserver(this);
49
+ }
50
+
51
+ for (const removeEventListener of this._removeEventListeners) {
52
+ removeEventListener();
53
+ }
54
+ }
55
+ /*
56
+ evalInContext() {
57
+ this.dependencies.resetDependencies();
58
+ const model = this.getModel();
59
+ if (!model) return;
60
+
61
+ const touchedNodes = new Set();
62
+ const domFacade = new DependencyNotifyingDomFacade(node => touchedNodes.add(node));
63
+
64
+ const context = this.getInScopeContext();
65
+ const result = evaluateXPath(this.ref, context, this, domFacade);
66
+ this.nodeset = Array.isArray(result) ? result : [result];
67
+
68
+ touchedNodes.forEach(node => {
69
+ const mi = model.getModelItem(node);
70
+ if (mi) {
71
+ mi.addObserver(this);
72
+ console.log(`[UIElement] Dynamically observing ${mi.path} due to XPath dependency`);
73
+ }
74
+ });
75
+
76
+ // Manually evaluate predicate parts to ensure detection
77
+ const predicateRegex = /\[(.*?)\]/g;
78
+ let match;
79
+ while ((match = predicateRegex.exec(this.ref)) !== null) {
80
+ const predicate = match[1];
81
+ try {
82
+ const predicateContext = model.getDefaultInstance().getDefaultContext();
83
+ const predDomFacade = new DependencyNotifyingDomFacade(n => touchedNodes.add(n));
84
+ evaluateXPathToBoolean(predicate, predicateContext, this, predDomFacade);
85
+
86
+ touchedNodes.forEach(node => {
87
+ const mi = model.getModelItem(node);
88
+ if (mi) {
89
+ mi.addObserver(this);
90
+ console.log(`[UIElement] Observing ${mi.path} (from predicate: ${predicate})`);
91
+ }
92
+ });
93
+ } catch (e) {
94
+ console.warn('Predicate evaluation failed for dependency tracking:', predicate, e);
95
+ }
96
+ }
97
+ }
98
+ */
99
+
100
+ attachObserver() {
101
+ const modelItem = this.getModelItem();
102
+ if (!modelItem || typeof modelItem.addObserver !== 'function') return;
103
+
104
+ if (!modelItem.observers) {
105
+ modelItem.observers = new Set();
106
+ }
107
+ if (modelItem.observers.has(this)) {
108
+ // console.log(`[UIElement] Observer already registered for ref="${this.ref}"`);
109
+ return;
110
+ }
111
+ modelItem.addObserver(this);
112
+ // console.log(`[UIElement] attaching observer for ref="${this.ref}"`, this);
113
+
114
+ // if (typeof this.update === 'function') {
115
+ // this.update(modelItem);
116
+ // }
117
+ }
118
+
119
+ /**
120
+ * Called by ModelItem when it changes
121
+ * @param {import('../modelitem.js').ModelItem} modelItem - The ModelItem that changed
122
+ */
123
+ update(modelItem) {
124
+ if (this.isBound()) {
125
+ // console.log('[UIElement] update()', modelItem);
126
+ // this.getOwnerForm().addToBatchedNotifications(modelItem);
127
+ this.refresh();
128
+ }
129
+ }
130
+
131
+ // init() {
132
+ // throw new Error('You have to implement the method init!');
133
+ // }
134
+
135
+ async refresh(force) {
136
+ console.log(`🔄 [UIElement] refresh() called for ref="${this.ref}"`);
137
+ }
138
+
139
+ async refreshChildren(force) {
140
+ await Fore.refreshChildren(this, force);
19
141
  }
20
142
 
21
143
  activate() {
@@ -45,16 +167,11 @@ export class UIElement extends ForeElementMixin {
45
167
  }
46
168
 
47
169
  addTrashIcon() {
48
- // Only show icon if explicitly marked by control-menu
49
-
50
170
  if (!this.closest('[show-icon]')) return;
51
-
52
- // const wrapper = this.shadowRoot.querySelector('.wrapper');
53
- // if (!wrapper || wrapper.querySelector('.trash')) return;
54
171
  const trash = this.querySelector('.trash');
55
172
  if (trash) return;
173
+
56
174
  const icon = document.createElement('span');
57
- // icon.innerHTML = '&#128465;'; // trash icon
58
175
  icon.innerHTML = `
59
176
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none"
60
177
  stroke="currentColor" stroke-width="2" stroke-linecap="round"
@@ -62,10 +179,8 @@ export class UIElement extends ForeElementMixin {
62
179
  <path d="M17.94 17.94C16.13 19.12 14.13 20 12 20C7 20 2.73 15.88 1 12C1.6 10.66 2.43 9.47 3.46 8.48M10.58 10.58C10.21 11.01 10 11.5 10 12C10 13.11 10.89 14 12 14C12.5 14 12.99 13.79 13.42 13.42M6.53 6.53C7.87 5.54 9.39 5 12 5C17 5 21.27 9.12 23 12C22.4 13.34 21.57 14.53 20.54 15.52M1 1L23 23"/>
63
180
  </svg>
64
181
  `;
65
-
66
182
  icon.classList.add('trash');
67
183
  icon.setAttribute('title', 'Hide');
68
- // icon.setAttribute('part', 'trash');
69
184
  icon.style.cursor = 'pointer';
70
185
  icon.style.marginLeft = '0.5em';
71
186
 
@@ -81,11 +196,11 @@ export class UIElement extends ForeElementMixin {
81
196
  }
82
197
 
83
198
  removeTrashIcon() {
84
- debugger;
85
199
  const icon = this.querySelector('.trash');
86
200
  if (icon) icon.remove();
87
201
  }
88
202
  }
203
+
89
204
  if (!customElements.get('ui-element')) {
90
205
  customElements.define('ui-element', UIElement);
91
206
  }
@@ -125,6 +125,7 @@ export default class AbstractControl extends UIElement {
125
125
  if (this.modelItem instanceof ModelItem) {
126
126
  // console.log('### XfAbstractControl.refresh modelItem : ', this.modelItem);
127
127
 
128
+ this.attachObserver();
128
129
  if (this.hasAttribute('as') && this.getAttribute('as') === 'node') {
129
130
  // console.log('as', this.nodeset);
130
131
  // this.modelItem.value = this.nodeset;
@@ -156,9 +157,11 @@ export default class AbstractControl extends UIElement {
156
157
  /*
157
158
  this is another case that highlights the fact that an init() function might make sense in general.
158
159
  */
160
+ /*
159
161
  if (!this.modelItem.boundControls.includes(this)) {
160
162
  this.modelItem.boundControls.push(this);
161
163
  }
164
+ */
162
165
 
163
166
  // console.log('>>>>>>>> abstract refresh ', this.control);
164
167
  // this.control[this.valueProp] = this.value;
@@ -318,9 +321,11 @@ export default class AbstractControl extends UIElement {
318
321
  // if (alert) alert.style.display = 'none';
319
322
  this._dispatchEvent('valid');
320
323
  this.setAttribute('valid', '');
324
+ this.getWidget().setAttribute('aria-invalid', 'false');
321
325
  this.removeAttribute('invalid');
322
326
  } else {
323
327
  this.setAttribute('invalid', '');
328
+ this.getWidget().setAttribute('aria-invalid', 'true');
324
329
  this.removeAttribute('valid');
325
330
  // ### constraint is invalid - handle alerts
326
331
  /*
package/src/ui/fx-case.js CHANGED
@@ -70,7 +70,7 @@ export class FxCase extends FxContainer {
70
70
  // calls. Save all important state first.
71
71
  const { parentNode } = this;
72
72
  const replacement = await this._loadFromSrc();
73
- if(!replacement){
73
+ if (!replacement) {
74
74
  Fore.dispatch(this, 'error', {
75
75
  detail: {
76
76
  message: `HTML page couldn't be loaded`,
@@ -81,9 +81,21 @@ export class FxCase extends FxContainer {
81
81
  await parentNode.replaceCase(this, replacement);
82
82
  }
83
83
  const model = ownerForm.getModel();
84
- model.updateModel();
85
- ownerForm.refresh(true);
84
+ ownerForm.addToBatchedNotifications(this);
85
+ ownerForm.refresh(false);
86
86
  });
87
+ this.addEventListener('deselect', (event) => {
88
+ const ownerForm = this.getOwnerForm();
89
+ ownerForm.addToBatchedNotifications(event.target);
90
+ });
91
+ }
92
+
93
+ async refresh(force) {
94
+ console.log(`🔄 fx-case ${this.id} refresh`, force);
95
+ await super.refresh(force);
96
+ if (!this.isBound()) {
97
+ Fore.refreshChildren(this, force);
98
+ }
87
99
  }
88
100
 
89
101
  /**
@@ -78,9 +78,14 @@ export class FxContainer extends UIElement {
78
78
  if (this.isBound()) {
79
79
  this.evalInContext();
80
80
  this.modelItem = this.getModelItem();
81
+ /*
81
82
  if (this.modelItem && !this.modelItem.boundControls.includes(this)) {
82
83
  this.modelItem.boundControls.push(this);
83
84
  }
85
+ */
86
+ if (this.modelItem) {
87
+ this.attachObserver();
88
+ }
84
89
  this.handleModelItemProperties();
85
90
  }
86
91
 
@@ -121,28 +121,37 @@ export class FxControlMenu extends XfAbstractControl {
121
121
  this.updateMenu();
122
122
  }
123
123
 
124
+ _getScopedContainer() {
125
+ const repeatItem = this.closest('fx-repeatitem');
126
+ if (repeatItem) return repeatItem;
127
+
128
+ if (this.selectExpr) {
129
+ return document.querySelector(this.selectExpr);
130
+ }
131
+
132
+ return null;
133
+ }
134
+
124
135
  updateMenu() {
125
- const container = document.querySelector(this.selectExpr);
136
+ const container = this._getScopedContainer();
126
137
  if (!container) return;
127
138
 
128
139
  let targets = [];
129
-
140
+ // ✅ Include container itself if it has on-demand
130
141
  if (container.hasAttribute('on-demand')) {
131
- if (container.nodeName === 'FX-REPEAT') {
132
- // If it's an <fx-repeat> with on-demand, use only the container
133
- targets = [container];
134
- } else {
135
- // If it's not <fx-repeat>, include container and inner [on-demand] targets
136
- targets = [container, ...container.querySelectorAll('[on-demand]')];
137
- }
138
- } else {
139
- // If container is not on-demand, only look for inner [on-demand]
140
- targets = Array.from(container.querySelectorAll('[on-demand]'));
142
+ targets.push(container);
141
143
  }
144
+
145
+ // ✅ Also include any descendant [on-demand] controls if not within repeat
146
+ if (container.nodeName !== 'FX-REPEAT') {
147
+ const innerTargets = Array.from(container.querySelectorAll('[on-demand]'));
148
+ targets.push(...innerTargets);
149
+ }
150
+
142
151
  this._currentTargets = targets;
143
- this.menuEl.innerHTML = ''; // Clear menu
152
+ this.menuEl.innerHTML = '';
144
153
 
145
- // Find the slotted button
154
+ // Find the button to disable if needed
146
155
  const slot = this.shadowRoot.querySelector('slot');
147
156
  const assignedNodes = slot.assignedNodes({ flatten: true });
148
157
  const button = assignedNodes.find(
@@ -3,12 +3,15 @@ import {
3
3
  evaluateXPath,
4
4
  evaluateXPathToString,
5
5
  evaluateXPathToFirstNode,
6
+ evaluateXPathToBoolean,
6
7
  } from '../xpath-evaluation.js';
7
8
  import getInScopeContext from '../getInScopeContext.js';
8
9
  import { Fore } from '../fore.js';
9
10
  import { ModelItem } from '../modelitem.js';
10
11
  import { debounce } from '../events.js';
11
12
  import { FxModel } from '../fx-model.js';
13
+ import { DependencyNotifyingDomFacade } from '../DependencyNotifyingDomFacade';
14
+ import { extractPredicateDependencies } from '../extract-predicate-deps.js';
12
15
 
13
16
  const WIDGETCLASS = 'widget';
14
17
 
@@ -238,6 +241,7 @@ export default class FxControl extends XfAbstractControl {
238
241
  * @param val the new value to be set
239
242
  */
240
243
  setValue(val) {
244
+ console.log('Control.setValue', val, 'on', this);
241
245
  const modelitem = this.getModelItem();
242
246
 
243
247
  if (this.getAttribute('class')) {
@@ -265,9 +269,11 @@ export default class FxControl extends XfAbstractControl {
265
269
  const setval = this.shadowRoot.getElementById('setvalue');
266
270
  setval.setValue(modelitem, val);
267
271
 
272
+ /*
268
273
  if (this.modelItem instanceof ModelItem && !this.modelItem?.boundControls.includes(this)) {
269
274
  this.modelItem.boundControls.push(this);
270
275
  }
276
+ */
271
277
 
272
278
  setval.actionPerformed(false);
273
279
  // this.visited = true;
@@ -287,6 +293,9 @@ export default class FxControl extends XfAbstractControl {
287
293
  });
288
294
  }
289
295
  // this.getOwnerForm().refresh();
296
+ const body = this.closest('body');
297
+ const outerfore = body.querySelector('fx-fore');
298
+ outerfore.refresh(true);
290
299
  }
291
300
 
292
301
  renderHTML(ref) {
@@ -496,6 +505,8 @@ export default class FxControl extends XfAbstractControl {
496
505
  imported.model = imported.querySelector('fx-model');
497
506
  imported.model.updateModel();
498
507
 
508
+ // Ensure initialRun is true for the first refresh
509
+ imported.initialRun = true;
499
510
  imported.refresh(true);
500
511
  },
501
512
  { once: true },
@@ -535,15 +546,15 @@ export default class FxControl extends XfAbstractControl {
535
546
  return this.querySelector('template');
536
547
  }
537
548
 
538
- async refresh(force) {
539
- // console.log('fx-control refresh', this);
549
+ async refresh(force = false) {
550
+ console.log('🔄 fx-control refresh', this);
540
551
  super.refresh(force);
541
552
  // console.log('refresh template', this.template);
542
553
  // const {widget} = this;
543
554
 
544
555
  // ### if we find a ref on control we have a 'select' control of some kind
545
556
  const widget = this.getWidget();
546
- this._handleBoundWidget(widget);
557
+ this._handleBoundWidget(widget, force);
547
558
  this._handleDataAttributeBinding();
548
559
  Fore.refreshChildren(this, force);
549
560
  }
@@ -558,7 +569,7 @@ export default class FxControl extends XfAbstractControl {
558
569
  if (dataRefd && dataRefd.closest('fx-control') === this) {
559
570
  this.boundList = dataRefd;
560
571
  const ref = dataRefd.getAttribute('data-ref');
561
- this._handleBoundWidget(dataRefd);
572
+ this._handleBoundWidget(dataRefd, true); //todo: revisit !!! observer
562
573
  }
563
574
  }
564
575
 
@@ -570,7 +581,8 @@ export default class FxControl extends XfAbstractControl {
570
581
  * @param widget the widget to handle
571
582
  * @private
572
583
  */
573
- _handleBoundWidget(widget) {
584
+ _handleBoundWidget(widget, force = false) {
585
+ // console.log('_handleBoundWidget', widget);
574
586
  if (this.boundInitialized && this.static) return;
575
587
 
576
588
  const ref = widget.hasAttribute('ref')
@@ -581,15 +593,24 @@ export default class FxControl extends XfAbstractControl {
581
593
  // ### eval nodeset for list control
582
594
  const inscope = getInScopeContext(this, ref);
583
595
  // const nodeset = evaluateXPathToNodes(ref, inscope, this);
584
- const nodeset = evaluateXPath(ref, inscope, this);
585
596
 
586
- // ### clear items
587
- const { children } = widget;
588
- Array.from(children).forEach(child => {
589
- if (child.nodeName.toLowerCase() !== 'template') {
590
- child.parentNode.removeChild(child);
591
- }
592
- });
597
+ const touchedNodes = new Set();
598
+ const domFacade = new DependencyNotifyingDomFacade(node => touchedNodes.add(node));
599
+
600
+ const nodeset = evaluateXPath(ref, inscope, this, domFacade);
601
+
602
+ const contextNode = Array.isArray(inscope) ? inscope[0] : inscope;
603
+ // console.log('Extracting model', this.getModel());
604
+ // console.log('Extracting model inited', this.getModel().inited);
605
+ const model = this.getModel();
606
+ if (!contextNode) return;
607
+ // console.log('Extracting predicate deps from ref:', ref);
608
+ extractPredicateDependencies(
609
+ ref,
610
+ model,
611
+ mi => mi.addObserver(this),
612
+ node => model.getModelItem(node),
613
+ );
593
614
 
594
615
  // ### bail out when nodeset is array and empty
595
616
  if (Array.isArray(nodeset) && nodeset.length === 0) return;
@@ -597,6 +618,19 @@ export default class FxControl extends XfAbstractControl {
597
618
  // ### build the items
598
619
  const { template } = this;
599
620
  if (template) {
621
+ // ### clear items
622
+ // if (force === true || !this.boundInitialized) {
623
+ if (force === true) {
624
+ const { children } = widget;
625
+ Array.from(children).forEach(child => {
626
+ if (child.nodeName.toLowerCase() !== 'template') {
627
+ child.parentNode.removeChild(child);
628
+ }
629
+ });
630
+ } else {
631
+ return;
632
+ }
633
+
600
634
  // ### handle 'selection' open and insert an empty option in that case
601
635
  if (
602
636
  this.widget.nodeName === 'SELECT' &&
@@ -656,7 +690,7 @@ export default class FxControl extends XfAbstractControl {
656
690
 
657
691
  const valueExpr = valueAttribute;
658
692
  const cutted = valueExpr.substring(1, valueExpr.length - 1);
659
- const evaluated = evaluateXPathToString(cutted, node, newEntry);
693
+ const evaluated = evaluateXPathToString(cutted, node, this);
660
694
  newEntry.setAttribute('value', evaluated);
661
695
 
662
696
  if (this.value === evaluated) {
@@ -675,8 +709,14 @@ export default class FxControl extends XfAbstractControl {
675
709
  // ### <<< needs rework
676
710
  }
677
711
 
712
+ /**
713
+ *
714
+ * @param {string} optionLabel
715
+ * @param {Node} node
716
+ * @param {HTMLElement} newEntry
717
+ */
678
718
  evalLabel(optionLabel, node, newEntry) {
679
- const labelExpr = optionLabel.substring(1, optionLabel.length - 1);
719
+ const labelExpr = optionLabel.trim().substring(1, optionLabel.length - 1);
680
720
  if (!labelExpr) return;
681
721
 
682
722
  const label = evaluateXPathToString(labelExpr, node, this);
@@ -60,11 +60,13 @@ export class FxItems extends FxControl {
60
60
  e.preventDefault;
61
61
  e.stopPropagation();
62
62
  const items = this.querySelectorAll('[value]');
63
+ /*
63
64
  let target;
64
65
  if (e.target.nodeName === 'LABEL') {
65
66
  target = resolveId(e.target.getAttribute('for'), this);
66
67
  target.checked = !target.checked;
67
68
  }
69
+ */
68
70
 
69
71
  let val = '';
70
72
  Array.from(items).forEach(item => {
@@ -145,12 +147,16 @@ export class FxItems extends FxControl {
145
147
  evaluated = node[cutted];
146
148
  }
147
149
 
148
- // adding space around value to allow matching of 'words'
149
- const spaced = ` ${evaluated} `;
150
- const valAttr = ` ${this.getAttribute('value')} `;
151
150
  input.value = evaluated;
152
151
  input.setAttribute('id', id);
153
- if (valAttr.indexOf(spaced) !== -1) {
152
+ // Normalize the current value (remove newlines, tabs, excessive spaces)
153
+ const currentValue = (this.getAttribute('value') || '').replace(/\s+/g, ' ').trim();
154
+ const valueList = currentValue.split(/\s+/); // Split on whitespace
155
+
156
+ // Check if the value is in the space-separated list of values
157
+ // const currentValue = this.getAttribute('value') || '';
158
+ // const valueList = currentValue.split(/\s+/);
159
+ if (valueList.includes(evaluated)) {
154
160
  input.checked = true;
155
161
  }
156
162
  }