@jinntec/fore 2.7.2 → 2.9.0

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.
@@ -377,6 +377,24 @@ export class FxSubmission extends ForeElementMixin {
377
377
  const targetInstance = this._getTargetInstance();
378
378
 
379
379
  if (this.replace === 'instance') {
380
+ const targetInstance = this._getTargetInstance();
381
+
382
+ // ### contentType handling
383
+
384
+ if (contentType.includes('html')) {
385
+ let effectiveData;
386
+ if (data.nodeType) {
387
+ effectiveData = data;
388
+ }
389
+ // ## try parsing
390
+ try {
391
+ effectiveData = new DOMParser().parseFromString(data, 'text/html');
392
+ } catch {
393
+ Fore.dispatch(this, 'error', { message: 'could not parse data as HTML' });
394
+ }
395
+
396
+ targetInstance.instanceData = effectiveData;
397
+ }
380
398
  if (targetInstance) {
381
399
  if (this.targetref) {
382
400
  const [theTarget] = evaluateXPath(
package/src/modelitem.js CHANGED
@@ -71,13 +71,13 @@ export class ModelItem {
71
71
  if (!this.node) return;
72
72
  const oldVal = this.value;
73
73
 
74
- if (newVal?.nodeType === Node.DOCUMENT_NODE) {
74
+ if (newVal?.nodeType && newVal.nodeType === Node.DOCUMENT_NODE) {
75
75
  this.node.replaceWith(newVal.firstElementChild);
76
- this.node = newVal.firstElementChild;
77
- } else if (newVal?.nodeType === Node.ELEMENT_NODE) {
76
+ // this.node.appendChild(newVal.firstElementChild);
77
+ } else if (newVal?.nodeType && newVal.nodeType === Node.ELEMENT_NODE) {
78
78
  this.node.replaceWith(newVal);
79
- this.node = newVal;
80
- } else if (this.node.nodeType === Node.ATTRIBUTE_NODE) {
79
+ // this.node.appendChild(newVal);
80
+ } else if (newVal?.nodeType && this.node.nodeType === Node.ATTRIBUTE_NODE) {
81
81
  this.node.nodeValue = newVal;
82
82
  } else {
83
83
  this.node.textContent = newVal;
@@ -0,0 +1 @@
1
+ This package is deprecated and no longer maintained or included in index.js
@@ -1,4 +1,4 @@
1
- import { XPathUtil } from '../xpath-util';
1
+ import { XPathUtil } from '../xpath-util.js';
2
2
  import './fx-log-item.js';
3
3
  import { FxLogSettings } from './fx-log-settings.js';
4
4
  import { getDocPath, getPath } from '../xpath-path.js';
@@ -521,7 +521,7 @@ export class FxActionLog extends HTMLElement {
521
521
  */
522
522
  _logDetails(e) {
523
523
  const eventType = e.type;
524
- const path = XPathUtil.getPath(e.target, '');
524
+ const path = getPath(e.target, '');
525
525
  // console.log('>>>> _logDetails', path);
526
526
  const cut = path.substring(path.indexOf('/fx-fore'), path.length);
527
527
  const xpath = `/${cut}`;
@@ -286,6 +286,10 @@ export default class AbstractControl extends UIElement {
286
286
  }
287
287
 
288
288
  _toggleValid(valid) {
289
+ // Used by required handling (and potentially other callers).
290
+ // It must also fire validity events and sync aria-invalid.
291
+ const wasInvalid = this.hasAttribute('invalid');
292
+
289
293
  if (valid) {
290
294
  this.removeAttribute('invalid');
291
295
  this.setAttribute('valid', '');
@@ -293,6 +297,25 @@ export default class AbstractControl extends UIElement {
293
297
  this.removeAttribute('valid');
294
298
  this.setAttribute('invalid', '');
295
299
  }
300
+ this._syncAriaInvalid();
301
+
302
+ const isInvalid = this.hasAttribute('invalid');
303
+ // Only dispatch when the state actually changed
304
+ if (wasInvalid !== isInvalid) {
305
+ this._dispatchEvent(isInvalid ? 'invalid' : 'valid');
306
+ }
307
+ }
308
+
309
+ _syncAriaInvalid() {
310
+ // Keep widget aria-invalid in sync with the *control* state, regardless of
311
+ // whether invalidity comes from constraint, required emptiness, etc.
312
+ try {
313
+ const w = this.getWidget?.() || this.widget;
314
+ if (!w) return;
315
+ w.setAttribute('aria-invalid', this.hasAttribute('invalid') ? 'true' : 'false');
316
+ } catch (e) {
317
+ // ignore: widget might not exist yet
318
+ }
296
319
  }
297
320
 
298
321
  handleReadonly() {
@@ -321,8 +344,8 @@ export default class AbstractControl extends UIElement {
321
344
  // if (alert) alert.style.display = 'none';
322
345
  this._dispatchEvent('valid');
323
346
  this.setAttribute('valid', '');
324
- this.getWidget().setAttribute('aria-invalid', 'false');
325
347
  this.removeAttribute('invalid');
348
+ this.getWidget().setAttribute('aria-invalid', 'false');
326
349
  } else {
327
350
  this.setAttribute('invalid', '');
328
351
  this.getWidget().setAttribute('aria-invalid', 'true');
@@ -352,31 +375,41 @@ export default class AbstractControl extends UIElement {
352
375
  this._dispatchEvent('invalid');
353
376
  }
354
377
  }
378
+
379
+ // Ensure aria-invalid matches the current control state even if
380
+ // we didn't enter the state-change branch above.
381
+ this._syncAriaInvalid();
382
+
355
383
  }
356
384
 
357
385
  handleRelevant() {
358
- // console.log('mip valid', this.modelItem.enabled);
386
+ // IMPORTANT: don't clear relevant/nonrelevant BEFORE comparing states.
387
+ // Otherwise isEnabled() (based on attributes) always reads as "enabled"
388
+ // and we can never detect a transition back to relevant.
359
389
  const item = this.modelItem.node;
360
- this.removeAttribute('relevant');
361
- this.removeAttribute('nonrelevant');
390
+
391
+ const wasEnabled = this.isEnabled();
392
+
393
+ // Determine new enabled state
394
+ let newEnabled = !!this.modelItem.relevant;
395
+
396
+ // If a nodeset resolves to an empty array, treat the control as nonrelevant
362
397
  if (Array.isArray(item) && item.length === 0) {
363
- this._dispatchEvent('nonrelevant');
364
- this.setAttribute('nonrelevant', '');
365
- // this.style.display = 'none';
366
- return;
398
+ newEnabled = false;
367
399
  }
368
- if (this.isEnabled() !== this.modelItem.relevant) {
369
- if (this.modelItem.relevant) {
370
- this._dispatchEvent('relevant');
371
- // this._fadeIn(this, this.display);
400
+
401
+ // Apply attributes
402
+ if (newEnabled) {
372
403
  this.setAttribute('relevant', '');
373
- // this.style.display = this.display;
404
+ this.removeAttribute('nonrelevant');
374
405
  } else {
375
- this._dispatchEvent('nonrelevant');
376
- // this._fadeOut(this);
377
406
  this.setAttribute('nonrelevant', '');
378
- // this.style.display = 'none';
407
+ this.removeAttribute('relevant');
379
408
  }
409
+
410
+ // Dispatch only on actual change
411
+ if (wasEnabled !== newEnabled) {
412
+ this._dispatchEvent(newEnabled ? 'relevant' : 'nonrelevant');
380
413
  }
381
414
  }
382
415
 
package/src/xpath-util.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as fx from 'fontoxpath';
2
- import { createNamespaceResolver } from './xpath-evaluation';
3
2
 
4
3
  export class XPathUtil {
5
4
  /**
@@ -44,10 +43,11 @@ export class XPathUtil {
44
43
  * @param xpath
45
44
  * @param doc {XMLDocument}
46
45
  * @param fore
46
+ * @param namespaceResolver {function} optional namespace resolver function
47
47
  * @return {Node|Attr}
48
48
  */
49
- static createNodesFromXPath(xpath, doc, fore) {
50
- const resolveNamespace = createNamespaceResolver(xpath, fore);
49
+ static createNodesFromXPath(xpath, doc, fore, namespaceResolver = null) {
50
+ const resolveNamespace = namespaceResolver || (() => undefined);
51
51
 
52
52
  if (!doc) {
53
53
  doc = document.implementation.createDocument(null, null, null); // Create a new XML document if not provided
@@ -1,181 +0,0 @@
1
- /* Usage:
2
-
3
- // Create or load your XML document
4
- const xmlString = `
5
- <root>
6
- <item id="1">Initial Value</item>
7
- </root>
8
- `;
9
- const parser = new DOMParser();
10
- const xmlDoc = parser.parseFromString(xmlString, "application/xml");
11
- const rootNode = xmlDoc.querySelector("root");
12
-
13
- // Create an instance of the DataObserver
14
- const xmlObserver = new DataObserver(200);
15
-
16
- // Define a callback to process mutations
17
- const handleXMLMutations = (mutationsList) => {
18
- console.log("XML Mutations:", mutationsList);
19
- };
20
-
21
- // Attach the observer to the XML root node
22
- xmlObserver.observe(rootNode, handleXMLMutations);
23
-
24
- // Modify the XML document to trigger mutations
25
- setTimeout(() => {
26
- const newItem = xmlDoc.createElement("item");
27
- newItem.textContent = "Newly Added Item";
28
- rootNode.appendChild(newItem); // This triggers a mutation
29
- }, 1000);
30
-
31
- For JSON:
32
- // Create a JSON object
33
- const jsonObject = {
34
- name: "John Doe",
35
- age: 30,
36
- hobbies: ["reading", "coding"],
37
- };
38
-
39
- // Create an instance of the DataObserver
40
- const jsonObserver = new DataObserver(200);
41
-
42
- // Define a callback to process changes
43
- const handleJSONChanges = (changesList) => {
44
- console.log("JSON Changes:", changesList);
45
- };
46
-
47
- // Attach the observer to the JSON object
48
- jsonObserver.observe(jsonObject, handleJSONChanges);
49
-
50
- // Modify the JSON object to trigger changes
51
- setTimeout(() => {
52
- jsonObject.name = "Jane Doe"; // This triggers a mutation
53
- jsonObject.age = 31; // Another mutation
54
- delete jsonObject.hobbies; // This triggers a delete mutation
55
- }, 1000);
56
-
57
- */
58
- export class DataObserver {
59
- constructor(debounceTime = 0) {
60
- this.observer = null; // Placeholder for MutationObserver (for XML)
61
- this.debounceTime = debounceTime; // Time in milliseconds for optional debouncing
62
- this.mutationsQueue = []; // To batch process mutations
63
- this.debounceTimer = null; // Timer for debouncing
64
- this.jsonProxy = null; // Proxy for JSON observation
65
- }
66
-
67
- /**
68
- * Attaches an observer to the given rootNode (DOM Node or JSON object)
69
- * @param {Node|Object} rootNode - The XML DOM node or JSON object to observe
70
- * @param {Function} callback - Function to process batch mutations
71
- */
72
- observe(rootNode, callback) {
73
- if (rootNode instanceof Node) {
74
- // Handle XML Node
75
- this.observeXML(rootNode, callback);
76
- } else if (typeof rootNode === "object" && rootNode !== null) {
77
- // Handle JSON Object
78
- this.observeJSON(rootNode, callback);
79
- } else {
80
- throw new Error("Invalid rootNode. Must be a DOM Node or a JSON object.");
81
- }
82
- }
83
-
84
- /**
85
- * Observes changes in an XML DOM node
86
- * @param {Node} xmlNode - The XML DOM node to observe
87
- * @param {Function} callback - Function to process batch mutations
88
- */
89
- observeXML(xmlNode, callback) {
90
- // Initialize a new MutationObserver
91
- this.observer = new MutationObserver((mutationsList) => {
92
- this.mutationsQueue.push(...mutationsList);
93
-
94
- if (this.debounceTime > 0) {
95
- clearTimeout(this.debounceTimer); // Clear previous timer
96
- this.debounceTimer = setTimeout(() => {
97
- this.processMutations(callback);
98
- }, this.debounceTime);
99
- } else {
100
- this.processMutations(callback);
101
- }
102
- });
103
-
104
- // Start observing the XML node
105
- this.observer.observe(xmlNode, {
106
- characterData: true,
107
- childList: true,
108
- subtree: true,
109
- });
110
- }
111
-
112
- /**
113
- * Observes changes in a JSON object using a proxy
114
- * @param {Object} jsonObject - The JSON object to observe
115
- * @param {Function} callback - Function to process changes
116
- */
117
- observeJSON(jsonObject, callback) {
118
- const handler = {
119
- set: (target, key, value) => {
120
- this.mutationsQueue.push({ type: "update", target, key, value });
121
-
122
- if (this.debounceTime > 0) {
123
- clearTimeout(this.debounceTimer); // Clear previous timer
124
- this.debounceTimer = setTimeout(() => {
125
- this.processMutations(callback);
126
- }, this.debounceTime);
127
- } else {
128
- this.processMutations(callback);
129
- }
130
-
131
- // Perform the actual update
132
- target[key] = value;
133
- return true;
134
- },
135
-
136
- deleteProperty: (target, key) => {
137
- this.mutationsQueue.push({ type: "delete", target, key });
138
-
139
- if (this.debounceTime > 0) {
140
- clearTimeout(this.debounceTimer); // Clear previous timer
141
- this.debounceTimer = setTimeout(() => {
142
- this.processMutations(callback);
143
- }, this.debounceTime);
144
- } else {
145
- this.processMutations(callback);
146
- }
147
-
148
- // Perform the actual delete
149
- return delete target[key];
150
- },
151
- };
152
-
153
- this.jsonProxy = new Proxy(jsonObject, handler);
154
- }
155
-
156
- /**
157
- * Processes the mutations batch and clears the queue
158
- * @param {Function} callback - Function to handle the batch of mutations
159
- */
160
- processMutations(callback) {
161
- if (this.mutationsQueue.length > 0) {
162
- callback(this.mutationsQueue); // Pass the batch to the callback
163
- this.mutationsQueue = []; // Clear the queue
164
- }
165
- }
166
-
167
- /**
168
- * Disconnects the observer and clears any pending debounce timer
169
- */
170
- disconnect() {
171
- if (this.observer) {
172
- this.observer.disconnect(); // Disconnect the MutationObserver
173
- this.observer = null;
174
- }
175
- if (this.debounceTimer) {
176
- clearTimeout(this.debounceTimer); // Clear any pending debounce timer
177
- }
178
- this.mutationsQueue = []; // Clear the mutation queue
179
- this.jsonProxy = null; // Clear JSON proxy reference
180
- }
181
- }