@jinntec/fore 3.3.2 → 4.0.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/README.md +98 -70
- package/dist/fore-dev.js +5593 -6832
- package/dist/fore.js +5602 -4887
- package/index.js +5 -10
- package/package.json +7 -7
- package/resources/fore.css +33 -0
- package/src/DependencyNotifyingDomFacade.js +90 -21
- package/src/DependentXPathQueries.js +15 -2
- package/src/ForeElementMixin.js +110 -16
- package/src/UndoManager.js +267 -0
- package/src/actions/abstract-action.js +71 -30
- package/src/actions/fx-action.js +5 -0
- package/src/actions/fx-append.js +3 -3
- package/src/actions/fx-commit-history.js +26 -0
- package/src/actions/fx-hide.js +1 -1
- package/src/actions/fx-insert.js +25 -22
- package/src/actions/fx-load.js +5 -5
- package/src/actions/fx-redo.js +58 -0
- package/src/actions/fx-refresh.js +2 -2
- package/src/actions/fx-reload.js +1 -1
- package/src/actions/fx-replace.js +1 -1
- package/src/actions/fx-send.js +27 -5
- package/src/actions/fx-setattribute.js +11 -7
- package/src/actions/fx-undo.js +58 -0
- package/src/createNodes.js +314 -0
- package/src/fore.js +53 -18
- package/src/functions/fx-functionlib.js +10 -10
- package/src/fx-bind.js +30 -18
- package/src/fx-fore.js +222 -200
- package/src/fx-instance.js +18 -1
- package/src/fx-model.js +236 -69
- package/src/fx-submission.js +37 -29
- package/src/fx-var.js +49 -13
- package/src/getInScopeContext.js +1 -1
- package/src/json/JSONDomFacade.js +1 -1
- package/src/json/JSONLens.js +2 -2
- package/src/ui/UIElement.js +18 -8
- package/src/ui/abstract-control.js +45 -3
- package/src/ui/fx-alert.js +4 -0
- package/src/ui/fx-case.js +1 -1
- package/src/ui/fx-container.js +3 -0
- package/src/ui/fx-control-menu.js +79 -11
- package/src/ui/fx-control.js +130 -41
- package/src/ui/fx-dialog.js +5 -0
- package/src/ui/fx-items.js +6 -6
- package/src/ui/fx-output.js +37 -1
- package/src/ui/fx-repeat.js +1065 -103
- package/src/ui/fx-repeatitem.js +4 -1
- package/src/ui/fx-switch.js +116 -3
- package/src/ui/fx-trigger.js +9 -4
- package/src/ui/fx-upload.js +10 -4
- package/src/ui/repeat-base.js +20 -12
- package/src/withDraggability.js +10 -1
- package/src/xpath-evaluation.js +30 -18
- package/src/xpath-path.js +122 -0
- package/src/xpath-util.js +11 -126
- package/src/actions/StringTpl.js +0 -17
- package/src/extract-predicate-deps.js +0 -57
- package/src/extractPredicateDependencies.js +0 -36
- package/src/json/lensFromNode.js +0 -5
- package/src/json-util.js +0 -27
- package/src/tools/adi.js +0 -1111
- package/src/tools/deprecation.md +0 -1
- package/src/tools/fx-action-log.js +0 -745
- package/src/tools/fx-devtools.js +0 -444
- package/src/tools/fx-dom-inspector.js +0 -610
- package/src/tools/fx-json-instance.js +0 -444
- package/src/tools/fx-log-item.js +0 -128
- package/src/tools/fx-log-settings.js +0 -533
- package/src/tools/fx-minimap.js +0 -203
- package/src/tools/helpers.js +0 -132
- package/src/ui/TemplateExpression.js +0 -12
- package/src/ui/fx-dom-inspector.js +0 -1255
package/src/xpath-util.js
CHANGED
|
@@ -30,123 +30,6 @@ export class XPathUtil {
|
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* creates DOM Nodes from an XPath locationpath expression. Support namespaced and un-namespaced
|
|
35
|
-
* nodes.
|
|
36
|
-
* E.g. 'foo/bar' creates an element 'foo' with an child element 'bar'
|
|
37
|
-
* 'foo/@bar' creates a 'foo' element with an 'bar' attribute
|
|
38
|
-
*
|
|
39
|
-
* supports multiple steps
|
|
40
|
-
*
|
|
41
|
-
* @param xpath
|
|
42
|
-
* @param doc {XMLDocument}
|
|
43
|
-
* @param fore
|
|
44
|
-
* @param namespaceResolver {function} optional namespace resolver function
|
|
45
|
-
* @return {Node|Attr}
|
|
46
|
-
*/
|
|
47
|
-
static createNodesFromXPath(xpath, doc, fore, namespaceResolver = null) {
|
|
48
|
-
const resolveNamespace = namespaceResolver || (() => undefined);
|
|
49
|
-
|
|
50
|
-
if (!doc) {
|
|
51
|
-
doc = document.implementation.createDocument(null, null, null); // Create a new XML document if not provided
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const parts = [];
|
|
55
|
-
let scratch = '';
|
|
56
|
-
let isInPredicate = false;
|
|
57
|
-
for (const char of xpath.split('')) {
|
|
58
|
-
if (!isInPredicate) {
|
|
59
|
-
// We are not in a predicate, the slash will terminate our step.
|
|
60
|
-
if (char === '/') {
|
|
61
|
-
parts.push(scratch);
|
|
62
|
-
scratch = '';
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
scratch += char;
|
|
67
|
-
if (char === '[') {
|
|
68
|
-
isInPredicate = true;
|
|
69
|
-
}
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
// We are in a predicate! So the only interesting token is ']', which means we're out of one.
|
|
73
|
-
scratch += char;
|
|
74
|
-
|
|
75
|
-
if (char === ']') {
|
|
76
|
-
isInPredicate = false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// Flush the last step
|
|
80
|
-
parts.push(scratch);
|
|
81
|
-
|
|
82
|
-
let rootNode = null;
|
|
83
|
-
let currentNode = null;
|
|
84
|
-
|
|
85
|
-
for (const part of parts) {
|
|
86
|
-
if (!part) continue; // Skip empty parts (e.g., leading slashes)
|
|
87
|
-
if (part === '.') {
|
|
88
|
-
// A '.' does not introduce new elements
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Handle attributes
|
|
93
|
-
if (part.startsWith('@')) {
|
|
94
|
-
const attrName = part.slice(1); // Strip '@'
|
|
95
|
-
if (!currentNode) {
|
|
96
|
-
return doc.createAttribute(attrName, '');
|
|
97
|
-
}
|
|
98
|
-
currentNode.setAttribute(attrName, '');
|
|
99
|
-
} else {
|
|
100
|
-
// We are a predicate selector! Handle it
|
|
101
|
-
// This regex matches strings like:
|
|
102
|
-
// - listBibl
|
|
103
|
-
// - tei:listBibl
|
|
104
|
-
// - listBibl[@type="foo"]
|
|
105
|
-
// - listBibl[@type="foo"][@class="bar"]
|
|
106
|
-
// It will also match strings like
|
|
107
|
-
// - listBibl[ancestor-or-self::foo]
|
|
108
|
-
// which will be filtered out later.
|
|
109
|
-
|
|
110
|
-
const result = part.match(/^(?<name>[\w:-]+)(?<predicates>(\[[^]*\])*)$/);
|
|
111
|
-
if (!result) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`No element could be made from the XPath step ${part}. It must be of these forms: 'localName', 'prefix:name', 'name[@attr="value"]' et cetera.`,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
const { name, predicates } = result.groups;
|
|
117
|
-
// Handle namespaces if present
|
|
118
|
-
const [prefix, localName] = name.includes(':') ? name.split(':') : [null, name];
|
|
119
|
-
const namespace = resolveNamespace(prefix);
|
|
120
|
-
|
|
121
|
-
const newElement = namespace
|
|
122
|
-
? doc.createElementNS(namespace, localName)
|
|
123
|
-
: doc.createElement(localName);
|
|
124
|
-
|
|
125
|
-
if (predicates) {
|
|
126
|
-
const predicateExtractionRegex =
|
|
127
|
-
/(\[@(?<name>[\w:-]*)\s?=\s?["'](?<value>[^"']*)['"]\])+/g;
|
|
128
|
-
const parsedPredicates = predicates
|
|
129
|
-
.matchAll(predicateExtractionRegex)
|
|
130
|
-
.map(match => ({ attrName: match.groups.name, value: match.groups.value }));
|
|
131
|
-
for (const { attrName, value } of parsedPredicates) {
|
|
132
|
-
newElement.setAttribute(attrName, value);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!rootNode) {
|
|
136
|
-
rootNode = newElement; // Set as the root node
|
|
137
|
-
} else {
|
|
138
|
-
currentNode.appendChild(newElement);
|
|
139
|
-
}
|
|
140
|
-
currentNode = newElement;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (!rootNode) {
|
|
144
|
-
throw new Error('Invalid XPath; no root element could be created.');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return rootNode;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
33
|
/**
|
|
151
34
|
* looks up namespace on ownerForm. Though not strictly in the sense of resolving namespaces in XML, the
|
|
152
35
|
* fx-fore element is a convenient place to put namespace declarations for 2 reasons:
|
|
@@ -281,7 +164,11 @@ export class XPathUtil {
|
|
|
281
164
|
*/
|
|
282
165
|
static isAbsolutePath(path) {
|
|
283
166
|
return (
|
|
284
|
-
path != null &&
|
|
167
|
+
path != null &&
|
|
168
|
+
(path.startsWith('/') ||
|
|
169
|
+
path.startsWith('instance(') ||
|
|
170
|
+
path.startsWith('$') ||
|
|
171
|
+
path.startsWith('?'))
|
|
285
172
|
);
|
|
286
173
|
}
|
|
287
174
|
|
|
@@ -309,7 +196,7 @@ export class XPathUtil {
|
|
|
309
196
|
// for dependency tracking, otherwise repeats won't refresh when they change.
|
|
310
197
|
try {
|
|
311
198
|
const host =
|
|
312
|
-
|
|
199
|
+
boundElement?.nodeType === Node.ATTRIBUTE_NODE ? boundElement.ownerElement : boundElement;
|
|
313
200
|
const fore = host?.closest?.('fx-fore');
|
|
314
201
|
const bindings = fore?._instanceVarBindings;
|
|
315
202
|
|
|
@@ -320,9 +207,7 @@ export class XPathUtil {
|
|
|
320
207
|
// $<id> => instance <id> (only if it's a known binding key)
|
|
321
208
|
for (const k of Object.keys(bindings)) {
|
|
322
209
|
if (k === 'default') continue;
|
|
323
|
-
const re = new RegExp(
|
|
324
|
-
`\\$${k.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?![\\w.-])`,
|
|
325
|
-
);
|
|
210
|
+
const re = new RegExp(`\\$${k.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?![\\w.-])`);
|
|
326
211
|
if (re.test(refStr)) return k;
|
|
327
212
|
}
|
|
328
213
|
}
|
|
@@ -347,14 +232,14 @@ export class XPathUtil {
|
|
|
347
232
|
// Variable indirection (may ultimately point to instance(...))
|
|
348
233
|
if (refStr.startsWith('$')) {
|
|
349
234
|
const variableName = refStr.match(/^\$(?<variableName>[a-zA-Z0-9\-_]+)/)?.groups
|
|
350
|
-
|
|
235
|
+
?.variableName;
|
|
351
236
|
|
|
352
237
|
let closestActualFormElement = boundElement;
|
|
353
238
|
while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
|
|
354
239
|
closestActualFormElement =
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
240
|
+
closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
|
|
241
|
+
? closestActualFormElement.ownerElement
|
|
242
|
+
: closestActualFormElement.parentNode;
|
|
358
243
|
}
|
|
359
244
|
|
|
360
245
|
const correspondingVariable = closestActualFormElement?.inScopeVariables?.get(variableName);
|
package/src/actions/StringTpl.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* simple module which turns a string into a template literal and substitutes all occurrences of map keys in the string. Syntax
|
|
3
|
-
* conforms to JavaScript template literals e.g. `${key}` where 'key' is a key in the map.
|
|
4
|
-
*
|
|
5
|
-
* credits go to https://stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string (Daniel)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
function parseTpl(template, map, fallback) {
|
|
9
|
-
return template.replace(/\$\{[^}]+\}/g, match =>
|
|
10
|
-
match
|
|
11
|
-
.slice(2, -1)
|
|
12
|
-
.trim()
|
|
13
|
-
.split('.')
|
|
14
|
-
.reduce((searchObject, key) => searchObject[key] || fallback || match, map),
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
export { parseTpl };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// extract-predicate-deps.js
|
|
2
|
-
import { DependencyNotifyingDomFacade } from './DependencyNotifyingDomFacade.js';
|
|
3
|
-
import { evaluateXPathToBoolean } from './xpath-evaluation.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extracts an instance ID from an XPath expression like instance('myInstance')/something
|
|
7
|
-
* @param {string} xpathExpr
|
|
8
|
-
* @returns {string|null} The extracted instance ID or null if not found
|
|
9
|
-
*/
|
|
10
|
-
function extractInstanceIdFromXPath(xpathExpr) {
|
|
11
|
-
const instanceRegex = /instance\(['"]([^)'"]+)['"]\)/;
|
|
12
|
-
const match = xpathExpr.match(instanceRegex);
|
|
13
|
-
return match ? match[1] : null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Gets the context node for a predicate XPath expression.
|
|
18
|
-
* @param {string} predicateExpr
|
|
19
|
-
* @param {import('./fx-model.js').FxModel} model
|
|
20
|
-
* @returns {Node}
|
|
21
|
-
*/
|
|
22
|
-
function getContextNodeForPredicate(predicateExpr, model) {
|
|
23
|
-
const instanceId = extractInstanceIdFromXPath(predicateExpr) || 'default';
|
|
24
|
-
const instance = model.getInstance(instanceId);
|
|
25
|
-
return instance.getDefaultContext();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Extracts and registers dependencies from XPath predicates.
|
|
30
|
-
*
|
|
31
|
-
* @param {string} ref - The XPath expression possibly containing predicates.
|
|
32
|
-
* @param {import('./fx-model.js').FxModel} model - The model to resolve instances from.
|
|
33
|
-
* @param {(modelItem: import('./modelitem.js').ModelItem) => void} register - A callback to register observers.
|
|
34
|
-
* @param {(node: Node) => import('./modelitem.js').ModelItem} resolveModelItem - A callback to resolve modelItems.
|
|
35
|
-
*/
|
|
36
|
-
export function extractPredicateDependencies(ref, contextNode, register, resolveModelItem) {
|
|
37
|
-
const predicateRegex = /\[(.*?)\]/g;
|
|
38
|
-
let match;
|
|
39
|
-
|
|
40
|
-
while ((match = predicateRegex.exec(ref)) !== null) {
|
|
41
|
-
const predicate = match[1];
|
|
42
|
-
try {
|
|
43
|
-
const domFacade = new DependencyNotifyingDomFacade(node => {
|
|
44
|
-
const mi = resolveModelItem(node);
|
|
45
|
-
if (mi) {
|
|
46
|
-
register(mi);
|
|
47
|
-
console.log(`[PredicateDependency] Observing ${mi.path} from predicate: [${predicate}]`);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const predContext = getContextNodeForPredicate(predicate, contextNode);
|
|
52
|
-
evaluateXPathToBoolean(predicate, predContext, { dispatchEvent() {} }, domFacade);
|
|
53
|
-
} catch (e) {
|
|
54
|
-
console.warn('Failed to evaluate predicate expression:', predicate, e);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// extract-predicate-deps.js
|
|
2
|
-
import { DependencyNotifyingDomFacade } from './DependencyNotifyingDomFacade.js';
|
|
3
|
-
import { evaluateXPathToBoolean } from './xpath-evaluation.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extracts and registers dependencies from XPath predicates.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} ref - The XPath expression possibly containing predicates.
|
|
9
|
-
* @param {Node} contextNode - The context node for evaluation.
|
|
10
|
-
* @param {(modelItem: import('./modelitem.js').ModelItem) => void} register - A callback to register observers.
|
|
11
|
-
* @param {(node: Node) => import('./modelitem.js').ModelItem} resolveModelItem - A callback to resolve modelItems.
|
|
12
|
-
*/
|
|
13
|
-
export function extractPredicateDependencies(ref, contextNode, register, resolveModelItem) {
|
|
14
|
-
const predicateRegex = /\[(.*?)\]/g;
|
|
15
|
-
let match;
|
|
16
|
-
const touchedNodes = new Set();
|
|
17
|
-
|
|
18
|
-
while ((match = predicateRegex.exec(ref)) !== null) {
|
|
19
|
-
const predicate = match[1];
|
|
20
|
-
try {
|
|
21
|
-
const domFacade = new DependencyNotifyingDomFacade(n => touchedNodes.add(n));
|
|
22
|
-
const fakeContext = { dispatchEvent: () => {} }; // prevent null dispatchEvent crash
|
|
23
|
-
evaluateXPathToBoolean(predicate, contextNode, undefined, domFacade);
|
|
24
|
-
|
|
25
|
-
touchedNodes.forEach(node => {
|
|
26
|
-
const mi = resolveModelItem(node);
|
|
27
|
-
if (mi) {
|
|
28
|
-
register(mi);
|
|
29
|
-
console.log(`[PredicateDependency] Observing ${mi.path} from predicate: [${predicate}]`);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
} catch (e) {
|
|
33
|
-
console.warn('Failed to evaluate predicate expression:', predicate, e);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/json/lensFromNode.js
DELETED
package/src/json-util.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const jsonToXml = json => {
|
|
2
|
-
const convert = obj => {
|
|
3
|
-
let xml = '';
|
|
4
|
-
for (const key in obj) {
|
|
5
|
-
if (obj.hasOwnProperty(key)) {
|
|
6
|
-
xml += `<${key}`;
|
|
7
|
-
if (typeof obj[key] === 'object') {
|
|
8
|
-
if (Array.isArray(obj[key])) {
|
|
9
|
-
xml += ' type="array">';
|
|
10
|
-
obj[key].forEach(item => {
|
|
11
|
-
xml += `<_>${convert(item)}</_>`;
|
|
12
|
-
});
|
|
13
|
-
} else {
|
|
14
|
-
xml += ' type="object">';
|
|
15
|
-
xml += convert(obj[key]);
|
|
16
|
-
}
|
|
17
|
-
} else {
|
|
18
|
-
xml += ` type="${typeof obj[key]}">${obj[key]}</${key}>`;
|
|
19
|
-
}
|
|
20
|
-
xml += `</${key}>`;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return xml;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
return `<json>${convert(json)}</json>`;
|
|
27
|
-
};
|