@jinntec/fore 2.7.2 → 2.8.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.
- package/dist/fore-dev.js +2137 -1942
- package/dist/fore.js +10837 -14535
- package/package.json +1 -1
- package/resources/fore.css +3 -0
- package/src/fx-bind.js +1 -1
- package/src/fx-fore.js +298 -143
- package/src/fx-instance.js +2 -2
- package/src/fx-submission.js +18 -0
- package/src/modelitem.js +5 -5
- package/src/tools/deprecation.md +1 -0
- package/src/tools/fx-action-log.js +2 -2
- package/src/ui/abstract-control.js +49 -16
- package/src/xpath-util.js +3 -3
- package/src/DataObserver.js +0 -181
package/src/fx-instance.js
CHANGED
|
@@ -163,10 +163,10 @@ export class FxInstance extends HTMLElement {
|
|
|
163
163
|
// Note: use the getter here: it might provide us with stubbed data if anything async is racing,
|
|
164
164
|
// such as an @src attribute
|
|
165
165
|
const instanceData = this.getInstanceData();
|
|
166
|
-
if (this.type === 'xml') {
|
|
166
|
+
if (this.type === 'xml' || this.type === 'html') {
|
|
167
167
|
return instanceData.firstElementChild;
|
|
168
168
|
}
|
|
169
|
-
return instanceData;
|
|
169
|
+
return this.instanceData;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
/**
|
package/src/fx-submission.js
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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
|
-
//
|
|
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
|
-
|
|
361
|
-
this.
|
|
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
|
-
|
|
364
|
-
this.setAttribute('nonrelevant', '');
|
|
365
|
-
// this.style.display = 'none';
|
|
366
|
-
return;
|
|
398
|
+
newEnabled = false;
|
|
367
399
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
// this._fadeIn(this, this.display);
|
|
400
|
+
|
|
401
|
+
// Apply attributes
|
|
402
|
+
if (newEnabled) {
|
|
372
403
|
this.setAttribute('relevant', '');
|
|
373
|
-
|
|
404
|
+
this.removeAttribute('nonrelevant');
|
|
374
405
|
} else {
|
|
375
|
-
this._dispatchEvent('nonrelevant');
|
|
376
|
-
// this._fadeOut(this);
|
|
377
406
|
this.setAttribute('nonrelevant', '');
|
|
378
|
-
|
|
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 =
|
|
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
|
package/src/DataObserver.js
DELETED
|
@@ -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
|
-
}
|