@jinntec/fore 2.1.1 → 2.3.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 (88) hide show
  1. package/README.md +2 -2
  2. package/dist/fore-dev.js +2 -2
  3. package/dist/fore-dev.js.map +1 -1
  4. package/dist/fore.js +2 -2
  5. package/dist/fore.js.map +1 -1
  6. package/index.js +4 -2
  7. package/package.json +10 -38
  8. package/resources/fore.css +263 -262
  9. package/resources/vars.css +8 -0
  10. package/src/DependencyNotifyingDomFacade.js +1 -0
  11. package/src/ForeElementMixin.js +243 -228
  12. package/src/actions/abstract-action.js +407 -398
  13. package/src/actions/fx-action.js +6 -6
  14. package/src/actions/fx-append.js +1 -1
  15. package/src/actions/fx-call.js +63 -62
  16. package/src/actions/fx-confirm.js +11 -11
  17. package/src/actions/fx-construct-done.js +4 -4
  18. package/src/actions/fx-copy.js +36 -36
  19. package/src/actions/fx-delete.js +77 -79
  20. package/src/actions/fx-dispatch.js +14 -14
  21. package/src/actions/fx-hide.js +15 -15
  22. package/src/actions/fx-insert.js +9 -12
  23. package/src/actions/fx-insertchild.js +88 -0
  24. package/src/actions/fx-load.js +188 -185
  25. package/src/actions/fx-message.js +18 -19
  26. package/src/actions/fx-refresh.js +10 -10
  27. package/src/actions/fx-reload.js +16 -14
  28. package/src/actions/fx-replace.js +0 -1
  29. package/src/actions/fx-reset.js +31 -31
  30. package/src/actions/fx-send.js +59 -52
  31. package/src/actions/fx-setattribute.js +48 -49
  32. package/src/actions/fx-setfocus.js +54 -58
  33. package/src/actions/fx-setvalue.js +85 -81
  34. package/src/actions/fx-show.js +11 -11
  35. package/src/actions/fx-toggle.js +2 -2
  36. package/src/actions/fx-toggleboolean.js +38 -39
  37. package/src/actions/fx-unmodified.js +27 -0
  38. package/src/actions/fx-update.js +6 -6
  39. package/src/dep_graph.js +1 -1
  40. package/src/drawdown.js +23 -30
  41. package/src/events.js +19 -19
  42. package/src/fore.js +143 -125
  43. package/src/functions/common-function.js +24 -25
  44. package/src/functions/fx-function.js +6 -99
  45. package/src/functions/fx-functionlib.js +56 -0
  46. package/src/functions/registerFunction.js +112 -0
  47. package/src/fx-bind.js +19 -24
  48. package/src/fx-connection.js +226 -0
  49. package/src/fx-fore.js +844 -815
  50. package/src/fx-header.js +4 -4
  51. package/src/fx-instance.js +25 -29
  52. package/src/fx-model.js +405 -380
  53. package/src/fx-submission.js +399 -397
  54. package/src/fx-var.js +13 -12
  55. package/src/getInScopeContext.js +102 -89
  56. package/src/json-util.js +24 -24
  57. package/src/lab/fore-component.js +48 -52
  58. package/src/lab/instance-inspector.js +13 -16
  59. package/src/modelitem.js +48 -10
  60. package/src/relevance.js +12 -16
  61. package/src/tools/adi.js +858 -812
  62. package/src/tools/fx-action-log.js +377 -295
  63. package/src/tools/fx-devtools.js +210 -210
  64. package/src/tools/fx-dom-inspector.js +123 -122
  65. package/src/tools/fx-json-instance.js +262 -253
  66. package/src/tools/fx-log-item.js +6 -11
  67. package/src/tools/fx-log-settings.js +358 -301
  68. package/src/tools/fx-minimap.js +186 -177
  69. package/src/tools/helpers.js +1 -1
  70. package/src/ui/abstract-control.js +73 -53
  71. package/src/ui/fx-case.js +59 -7
  72. package/src/ui/fx-container.js +13 -14
  73. package/src/ui/fx-control.js +572 -538
  74. package/src/ui/fx-dialog.js +3 -3
  75. package/src/ui/fx-droptarget.js +3 -4
  76. package/src/ui/fx-group.js +4 -2
  77. package/src/ui/fx-hint.js +3 -0
  78. package/src/ui/fx-inspector.js +5 -5
  79. package/src/ui/fx-items.js +11 -11
  80. package/src/ui/fx-output.js +10 -12
  81. package/src/ui/fx-repeat-attributes.js +23 -27
  82. package/src/ui/fx-repeat.js +347 -344
  83. package/src/ui/fx-repeatitem.js +75 -68
  84. package/src/ui/fx-switch.js +39 -14
  85. package/src/ui/fx-trigger.js +4 -4
  86. package/src/withDraggability.js +193 -191
  87. package/src/xpath-evaluation.js +969 -958
  88. package/src/xpath-util.js +161 -134
@@ -6,6 +6,7 @@ import { getBucketsForNode } from 'fontoxpath';
6
6
  * will be touched in the XPath.
7
7
  *
8
8
  * Maybe some more granularity is better. Maybe only notify a node's attributes are touched?
9
+ *
9
10
  */
10
11
  export class DependencyNotifyingDomFacade {
11
12
  /**
@@ -6,272 +6,287 @@ import {
6
6
  evaluateXPathToString,
7
7
  } from './xpath-evaluation.js';
8
8
  import getInScopeContext from './getInScopeContext.js';
9
- import {Fore} from './fore.js';
9
+ import { Fore } from './fore.js';
10
10
 
11
11
  /**
12
12
  * Mixin containing all general functions that are shared by all Fore element classes.
13
- * @param superclass
14
- * @returns {{readonly properties: {ref: {type: StringConstructor}, context: {type: ObjectConstructor}, nodeset: {type: ObjectConstructor}, model: {type: ObjectConstructor}, inScopeVariables: {type: MapConstructor}, modelItem: {type: ObjectConstructor}}, new(): ForeElementMixin, context: null, model: null, modelItem: {}, ref: *|string, inScopeVariables: null, nodeset: *, prototype: ForeElementMixin}}
13
+ * @extends {HTMLElement}
15
14
  */
16
- export const foreElementMixin = superclass =>
17
- class ForeElementMixin extends superclass {
18
- static get properties() {
19
- return {
20
- /**
21
- * context object for evaluation
22
- */
23
- context: {
24
- type: Object,
25
- },
26
- /**
27
- * the model of this element
28
- */
29
- model: {
30
- type: Object,
31
- },
32
- /**
33
- * The modelitem object associated to the bound node holding the evaluated state.
34
- */
35
- modelItem: {
36
- type: Object,
37
- },
38
- /**
39
- * the node(s) bound by this element
40
- */
41
- nodeset: {
42
- type: Object,
43
- },
44
- /**
45
- * XPath binding expression pointing to bound node
46
- */
47
- ref: {
48
- type: String,
49
- },
50
- inScopeVariables: {
51
- type: Map,
52
- },
53
- };
54
- }
15
+ export default class ForeElementMixin extends HTMLElement {
16
+ static get properties() {
17
+ return {
18
+ /**
19
+ * context object for evaluation
20
+ */
21
+ context: {
22
+ type: Object,
23
+ },
24
+ /**
25
+ * the model of this element
26
+ */
27
+ model: {
28
+ type: Object,
29
+ },
30
+ /**
31
+ * The modelitem object associated to the bound node holding the evaluated state.
32
+ */
33
+ modelItem: {
34
+ type: Object,
35
+ },
36
+ /**
37
+ * the node(s) bound by this element
38
+ */
39
+ nodeset: {
40
+ type: Object,
41
+ },
42
+ /**
43
+ * XPath binding expression pointing to bound node
44
+ */
45
+ ref: {
46
+ type: String,
47
+ },
48
+ inScopeVariables: {
49
+ type: Map,
50
+ },
51
+ };
52
+ }
55
53
 
56
- constructor() {
57
- super();
58
- this.context = null;
59
- this.model = null;
60
- this.modelItem = {};
61
- this.ref = this.hasAttribute('ref') ? this.getAttribute('ref') : '';
62
- this.inScopeVariables = new Map();
63
- }
54
+ constructor() {
55
+ super();
56
+ this.context = null;
57
+ this.model = null;
58
+ this.modelItem = {};
59
+ this.ref = this.hasAttribute('ref') ? this.getAttribute('ref') : '';
60
+ /**
61
+ * @type {Map<string, import('./fx-var.js').FxVariable>}
62
+ */
63
+ this.inScopeVariables = new Map();
64
+ }
64
65
 
65
- getModel() {
66
- // console.log('getModel this ', this);
67
- if (this.model) {
68
- return this.model;
69
- }
70
- // const ownerForm = this.closest('fx-fore');
71
- // const ownerForm = this.getOwnerForm(this);
72
- const ownerForm = this.getOwnerForm();
73
- return ownerForm.querySelector('fx-model');
66
+ /**
67
+ * @returns {import('./fx-model.js').FxModel}
68
+ */
69
+ getModel() {
70
+ // console.log('getModel this ', this);
71
+ if (this.model) {
72
+ return this.model;
74
73
  }
74
+ // const ownerForm = this.closest('fx-fore');
75
+ // const ownerForm = this.getOwnerForm(this);
76
+ const ownerForm = this.getOwnerForm();
77
+ return ownerForm.querySelector('fx-model');
78
+ }
75
79
 
76
- /**
77
- *
78
- * @returns {{parentNode}|ForeElementMixin}
79
- */
80
- getOwnerForm() {
81
- let currentElement = this;
82
- while (currentElement && currentElement.parentNode) {
83
- // console.log('current ', currentElement);
80
+ /**
81
+ *
82
+ * @returns {import('./fx-fore.js').FxFore} The fx-fore element associated with this form node
83
+ */
84
+ getOwnerForm() {
85
+ let currentElement = this;
86
+ while (currentElement && currentElement.parentNode) {
87
+ // console.log('current ', currentElement);
84
88
 
85
- if (currentElement.nodeName.toUpperCase() === 'FX-FORE') {
86
- return currentElement;
87
- }
89
+ if (currentElement.nodeName.toUpperCase() === 'FX-FORE') {
90
+ return currentElement;
91
+ }
88
92
 
89
- if (currentElement.parentNode instanceof DocumentFragment) {
90
- currentElement = currentElement.parentNode.host;
91
- } else {
92
- currentElement = currentElement.parentNode;
93
- }
93
+ if (currentElement.parentNode instanceof DocumentFragment) {
94
+ currentElement = currentElement.parentNode.host;
95
+ } else {
96
+ currentElement = currentElement.parentNode;
94
97
  }
95
- return currentElement;
96
98
  }
99
+ return currentElement;
100
+ }
97
101
 
98
- /**
99
- * evaluation of fx-bind and UiElements differ in details so that each class needs it's own implementation.
100
- */
101
- evalInContext() {
102
- // const inscopeContext = this.getInScopeContext();
103
- const model = this.getModel();
104
- if(!model){
105
- return;
106
- }
107
- let inscopeContext;
108
- if (this.hasAttribute('context')) {
109
- inscopeContext = getInScopeContext(this.getAttributeNode('context') || this, this.context);
110
- }
111
- if (this.hasAttribute('ref')) {
112
- inscopeContext = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
113
- }
114
- if (!inscopeContext && this.getModel().instances.length !== 0) {
115
- // ### always fall back to default context with there's neither a 'context' or 'ref' present
116
- inscopeContext = this.getModel()
117
- .getDefaultInstance()
118
- .getDefaultContext();
119
- // console.warn('no in scopeContext for ', this);
120
- // console.warn('using default context ', this);
121
- // return;
122
- }
123
- if (this.ref === '') {
124
- this.nodeset = inscopeContext;
125
- } else if (Array.isArray(inscopeContext)) {
126
- /*
127
- inscopeContext.forEach(n => {
128
- if (XPathUtil.isSelfReference(this.ref)) {
129
- this.nodeset = inscopeContext;
130
- } else {
131
- const localResult = evaluateXPathToFirstNode(this.ref, n, this);
132
- // console.log('local result: ', localResult);
133
- this.nodeset.push(localResult);
134
- }
135
- });
136
- */
137
- // this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext[0], this);
138
- this.nodeset = evaluateXPath(this.ref, inscopeContext[0], this);
102
+ /**
103
+ * evaluation of fx-bind and UiElements differ in details so that each class needs it's own implementation.
104
+ */
105
+ evalInContext() {
106
+ // const inscopeContext = this.getInScopeContext();
107
+ const model = this.getModel();
108
+ if (!model) {
109
+ return;
110
+ }
111
+ let inscopeContext;
112
+ if (this.hasAttribute('context')) {
113
+ inscopeContext = getInScopeContext(this.getAttributeNode('context') || this, this.context);
114
+ }
115
+ if (this.hasAttribute('ref')) {
116
+ inscopeContext = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
117
+ }
118
+ if (!inscopeContext && this.getModel().instances.length !== 0) {
119
+ // ### always fall back to default context with there's neither a 'context' or 'ref' present
120
+ inscopeContext = this.getModel().getDefaultInstance().getDefaultContext();
121
+ // console.warn('no in scopeContext for ', this);
122
+ // console.warn('using default context ', this);
123
+ // return;
124
+ }
125
+ if (this.ref === '') {
126
+ this.nodeset = inscopeContext;
127
+ } else if (Array.isArray(inscopeContext)) {
128
+ /*
129
+ inscopeContext.forEach(n => {
130
+ if (XPathUtil.isSelfReference(this.ref)) {
131
+ this.nodeset = inscopeContext;
132
+ } else {
133
+ const localResult = evaluateXPathToFirstNode(this.ref, n, this);
134
+ // console.log('local result: ', localResult);
135
+ this.nodeset.push(localResult);
136
+ }
137
+ });
138
+ */
139
+ // this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext[0], this);
140
+ this.nodeset = evaluateXPath(this.ref, inscopeContext[0], this);
141
+ } else {
142
+ // this.nodeset = fx.evaluateXPathToFirstNode(this.ref, inscopeContext, null, {namespaceResolver: this.namespaceResolver});
143
+ if (!inscopeContext) return;
144
+ const { nodeType } = inscopeContext;
145
+ if (nodeType && !XPathUtil.isAbsolutePath(this.ref)) {
146
+ this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, this);
139
147
  } else {
140
- // this.nodeset = fx.evaluateXPathToFirstNode(this.ref, inscopeContext, null, {namespaceResolver: this.namespaceResolver});
141
- if(!inscopeContext) return;
142
- const { nodeType } = inscopeContext;
143
- if (nodeType && !XPathUtil.isAbsolutePath(this.ref)) {
144
- this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, this);
145
- } else {
146
- [this.nodeset] = evaluateXPath(this.ref, inscopeContext, this);
147
- }
148
+ [this.nodeset] = evaluateXPath(this.ref, inscopeContext, this);
148
149
  }
149
- // console.log('UiElement evaluated to nodeset: ', this.nodeset);
150
150
  }
151
+ // console.log('UiElement evaluated to nodeset: ', this.nodeset);
152
+ }
151
153
 
152
- /**
153
- * resolves template expressions for a single attribute
154
- * @param expr an attribute value containing curly brackets containing XPath expressions to evaluate
155
- * @param node the attribute node used for scoped resolution
156
- * @returns {*}
157
- * @private
158
- */
159
- evaluateAttributeTemplateExpression(expr, node) {
160
- const matches = expr.match(/{[^}]*}/g);
161
- if (matches) {
162
- matches.forEach(match => {
163
- // console.log('match ', match);
164
- const naked = match.substring(1, match.length - 1);
165
- const inscope = getInScopeContext(node, naked);
166
- const result = evaluateXPathToString(naked, inscope, this);
167
- const replaced = expr.replaceAll(match, result);
168
- // console.log('replacing ', expr, ' with ', replaced);
169
- expr = replaced;
170
- });
171
- }
172
- return expr;
154
+ /**
155
+ * resolves template expressions for a single attribute
156
+ * @param {string} expr an attribute value containing curly brackets containing XPath expressions to evaluate
157
+ * @param {Node} node the attribute node used for scoped resolution
158
+ * @returns {string}
159
+ * @protected
160
+ */
161
+ evaluateAttributeTemplateExpression(expr, node) {
162
+ const matches = expr.match(/{[^}]*}/g);
163
+ if (matches) {
164
+ matches.forEach(match => {
165
+ // console.log('match ', match);
166
+ const naked = match.substring(1, match.length - 1);
167
+ const inscope = getInScopeContext(node, naked);
168
+ const result = evaluateXPathToString(naked, inscope, this);
169
+ const replaced = expr.replaceAll(match, result);
170
+ // console.log('replacing ', expr, ' with ', replaced);
171
+ expr = replaced;
172
+ });
173
173
  }
174
+ return expr;
175
+ }
174
176
 
177
+ isNotBound() {
178
+ return !this.hasAttribute('ref');
179
+ }
175
180
 
176
- isNotBound() {
177
- return !this.hasAttribute('ref');
178
- }
181
+ isBound() {
182
+ return this.hasAttribute('ref');
183
+ }
179
184
 
180
- isBound() {
181
- return this.hasAttribute('ref');
185
+ getBindingExpr() {
186
+ if (this.hasAttribute('ref')) {
187
+ return this.getAttribute('ref');
182
188
  }
183
-
184
- getBindingExpr() {
185
- if (this.hasAttribute('ref')) {
186
- return this.getAttribute('ref');
187
- }
188
- // try to get closest parent bind
189
- const parent = XPathUtil.getClosest('[ref]', this.parentNode);
190
- if (!parent) {
191
- return 'instance()'; // the default instance
192
- }
193
- return parent.getAttribute('ref');
189
+ // try to get closest parent bind
190
+ const parent = XPathUtil.getClosest('[ref]', this.parentNode);
191
+ if (!parent) {
192
+ return 'instance()'; // the default instance
194
193
  }
194
+ return parent.getAttribute('ref');
195
+ }
195
196
 
196
- getInstance() {
197
- if (this.ref.startsWith('instance(')) {
198
- const instId = XPathUtil.getInstanceId(this.ref);
199
- return this.getModel().getInstance(instId);
200
- }
201
- return this.getModel().getInstance('default');
197
+ /**
198
+ * @returns {import('./fx-instance.js').FxInstance}
199
+ */
200
+ getInstance() {
201
+ if (this.ref.startsWith('instance(')) {
202
+ const instId = XPathUtil.getInstanceId(this.ref);
203
+ return this.getModel().getInstance(instId);
202
204
  }
205
+ return this.getModel().getInstance('default');
206
+ }
203
207
 
204
- _getParentBindingElement(start) {
205
- if (start.parentNode.host) {
206
- const { host } = start.parentNode;
207
- if (host.hasAttribute('ref')) {
208
- return host;
209
- }
210
- } else if (start.parentNode) {
211
- if (start.parentNode.hasAttribute('ref')) {
212
- return this.parentNode;
213
- }
214
- this._getParentBindingElement(this.parentNode);
208
+ _getParentBindingElement(start) {
209
+ if (start.parentNode.host) {
210
+ const { host } = start.parentNode;
211
+ if (host.hasAttribute('ref')) {
212
+ return host;
215
213
  }
216
- return null;
214
+ } else if (start.parentNode) {
215
+ if (start.parentNode.hasAttribute('ref')) {
216
+ return this.parentNode;
217
+ }
218
+ this._getParentBindingElement(this.parentNode);
217
219
  }
220
+ return null;
221
+ }
218
222
 
219
- getModelItem() {
220
- // return this.model.bindingMap.find(m => m.refnode === this.nodeset);
221
- // return this.getModel().bindingMap.find(m => m.refnode === this.nodeset);
223
+ /**
224
+ * @returns {import('./modelitem.js').ModelItem}
225
+ */
226
+ getModelItem() {
227
+ // return this.model.bindingMap.find(m => m.refnode === this.nodeset);
228
+ // return this.getModel().bindingMap.find(m => m.refnode === this.nodeset);
222
229
 
223
- const mi = this.getModel().getModelItem(this.nodeset);
224
- if (mi) {
225
- this.modelItem = mi;
226
- }
230
+ const mi = this.getModel().getModelItem(this.nodeset);
231
+ if (mi) {
232
+ this.modelItem = mi;
233
+ }
227
234
 
228
- const repeated = XPathUtil.getClosest('fx-repeatitem', this);
229
- let existed;
230
- if (repeated) {
231
- const { index } = repeated;
232
- if (Array.isArray(this.nodeset)) {
233
- existed = this.getModel().getModelItem(this.nodeset[index - 1]);
234
- } else {
235
- existed = this.getModel().getModelItem(this.nodeset);
236
- }
235
+ const repeated = XPathUtil.getClosest('fx-repeatitem', this);
236
+ let existed;
237
+ if (repeated) {
238
+ const { index } = repeated;
239
+ if (Array.isArray(this.nodeset)) {
240
+ existed = this.getModel().getModelItem(this.nodeset[index - 1]);
237
241
  } else {
238
242
  existed = this.getModel().getModelItem(this.nodeset);
239
243
  }
244
+ } else {
245
+ existed = this.getModel().getModelItem(this.nodeset);
246
+ }
240
247
 
241
- if (!existed) {
242
- return FxModel.lazyCreateModelItem(this.getModel(), this.ref, this.nodeset);
243
- }
244
- return existed;
248
+ if (!existed) {
249
+ return FxModel.lazyCreateModelItem(this.getModel(), this.ref, this.nodeset);
245
250
  }
251
+ return existed;
252
+ }
246
253
 
247
- /**
248
- * Returns the effective value for the element.
249
- * a: look for 'value' attribute and if present evaluate it and return the resulting value
250
- * b: look for textContent and return the value if present
251
- * c: return null
252
- */
253
- getValue() {
254
- if (this.hasAttribute('value')) {
255
- const valAttr = this.getAttribute('value');
256
- try {
257
- const inscopeContext = getInScopeContext(this, valAttr);
258
- return evaluateXPathToString(valAttr, inscopeContext, this.getOwnerForm());
259
- } catch (error) {
260
- console.error(error);
261
- Fore.dispatch(this, 'error', { message: error });
262
- }
254
+ /**
255
+ * Returns the effective value for the element.
256
+ * a: look for 'value' attribute and if present evaluate it and return the resulting value
257
+ * b: look for textContent and return the value if present
258
+ * c: return null
259
+ * @returns {string}
260
+ */
261
+ getValue() {
262
+ if (this.hasAttribute('value')) {
263
+ const valAttr = this.getAttribute('value');
264
+ try {
265
+ const inscopeContext = getInScopeContext(this, valAttr);
266
+ return evaluateXPathToString(valAttr, inscopeContext, this.getOwnerForm());
267
+ } catch (error) {
268
+ console.error(error);
269
+ Fore.dispatch(this, 'error', { message: error });
263
270
  }
264
- if (this.textContent) {
265
- return this.textContent;
266
- }
267
- return null;
268
271
  }
269
-
270
- getInScopeContext() {
271
- return getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
272
+ if (this.textContent) {
273
+ return this.textContent;
272
274
  }
275
+ return null;
276
+ }
273
277
 
274
- setInScopeVariables(inScopeVariables) {
275
- this.inScopeVariables = inScopeVariables;
276
- }
277
- };
278
+ /**
279
+ * @returns {Node}
280
+ */
281
+ getInScopeContext() {
282
+ return getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
283
+ }
284
+
285
+ /**
286
+ * Set variables in scope here
287
+ * @param {Map} inScopeVariables
288
+ */
289
+ setInScopeVariables(inScopeVariables) {
290
+ this.inScopeVariables = inScopeVariables;
291
+ }
292
+ }