@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.
Files changed (73) hide show
  1. package/README.md +98 -70
  2. package/dist/fore-dev.js +5593 -6832
  3. package/dist/fore.js +5602 -4887
  4. package/index.js +5 -10
  5. package/package.json +7 -7
  6. package/resources/fore.css +33 -0
  7. package/src/DependencyNotifyingDomFacade.js +90 -21
  8. package/src/DependentXPathQueries.js +15 -2
  9. package/src/ForeElementMixin.js +110 -16
  10. package/src/UndoManager.js +267 -0
  11. package/src/actions/abstract-action.js +71 -30
  12. package/src/actions/fx-action.js +5 -0
  13. package/src/actions/fx-append.js +3 -3
  14. package/src/actions/fx-commit-history.js +26 -0
  15. package/src/actions/fx-hide.js +1 -1
  16. package/src/actions/fx-insert.js +25 -22
  17. package/src/actions/fx-load.js +5 -5
  18. package/src/actions/fx-redo.js +58 -0
  19. package/src/actions/fx-refresh.js +2 -2
  20. package/src/actions/fx-reload.js +1 -1
  21. package/src/actions/fx-replace.js +1 -1
  22. package/src/actions/fx-send.js +27 -5
  23. package/src/actions/fx-setattribute.js +11 -7
  24. package/src/actions/fx-undo.js +58 -0
  25. package/src/createNodes.js +314 -0
  26. package/src/fore.js +53 -18
  27. package/src/functions/fx-functionlib.js +10 -10
  28. package/src/fx-bind.js +30 -18
  29. package/src/fx-fore.js +222 -200
  30. package/src/fx-instance.js +18 -1
  31. package/src/fx-model.js +236 -69
  32. package/src/fx-submission.js +37 -29
  33. package/src/fx-var.js +49 -13
  34. package/src/getInScopeContext.js +1 -1
  35. package/src/json/JSONDomFacade.js +1 -1
  36. package/src/json/JSONLens.js +2 -2
  37. package/src/ui/UIElement.js +18 -8
  38. package/src/ui/abstract-control.js +45 -3
  39. package/src/ui/fx-alert.js +4 -0
  40. package/src/ui/fx-case.js +1 -1
  41. package/src/ui/fx-container.js +3 -0
  42. package/src/ui/fx-control-menu.js +79 -11
  43. package/src/ui/fx-control.js +130 -41
  44. package/src/ui/fx-dialog.js +5 -0
  45. package/src/ui/fx-items.js +6 -6
  46. package/src/ui/fx-output.js +37 -1
  47. package/src/ui/fx-repeat.js +1065 -103
  48. package/src/ui/fx-repeatitem.js +4 -1
  49. package/src/ui/fx-switch.js +116 -3
  50. package/src/ui/fx-trigger.js +9 -4
  51. package/src/ui/fx-upload.js +10 -4
  52. package/src/ui/repeat-base.js +20 -12
  53. package/src/withDraggability.js +10 -1
  54. package/src/xpath-evaluation.js +30 -18
  55. package/src/xpath-path.js +122 -0
  56. package/src/xpath-util.js +11 -126
  57. package/src/actions/StringTpl.js +0 -17
  58. package/src/extract-predicate-deps.js +0 -57
  59. package/src/extractPredicateDependencies.js +0 -36
  60. package/src/json/lensFromNode.js +0 -5
  61. package/src/json-util.js +0 -27
  62. package/src/tools/adi.js +0 -1111
  63. package/src/tools/deprecation.md +0 -1
  64. package/src/tools/fx-action-log.js +0 -745
  65. package/src/tools/fx-devtools.js +0 -444
  66. package/src/tools/fx-dom-inspector.js +0 -610
  67. package/src/tools/fx-json-instance.js +0 -444
  68. package/src/tools/fx-log-item.js +0 -128
  69. package/src/tools/fx-log-settings.js +0 -533
  70. package/src/tools/fx-minimap.js +0 -203
  71. package/src/tools/helpers.js +0 -132
  72. package/src/ui/TemplateExpression.js +0 -12
  73. 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 && (path.startsWith('/') || path.startsWith('instance(') || path.startsWith('$') || path.startsWith('?'))
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
- boundElement?.nodeType === Node.ATTRIBUTE_NODE ? boundElement.ownerElement : boundElement;
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
- ?.variableName;
235
+ ?.variableName;
351
236
 
352
237
  let closestActualFormElement = boundElement;
353
238
  while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
354
239
  closestActualFormElement =
355
- closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
356
- ? closestActualFormElement.ownerElement
357
- : closestActualFormElement.parentNode;
240
+ closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
241
+ ? closestActualFormElement.ownerElement
242
+ : closestActualFormElement.parentNode;
358
243
  }
359
244
 
360
245
  const correspondingVariable = closestActualFormElement?.inScopeVariables?.get(variableName);
@@ -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
- }
@@ -1,5 +0,0 @@
1
- import { JSONLens } from './JSONLens.js';
2
-
3
- export function lensFromNode(jsonRoot, jsonNode) {
4
- return new JSONLens(jsonRoot, jsonNode.getPath());
5
- }
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
- };