@jinntec/fore 2.2.0 → 2.3.1

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 +1 -1
  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 +3 -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 +4 -6
  45. package/src/functions/fx-functionlib.js +39 -42
  46. package/src/functions/registerFunction.js +95 -86
  47. package/src/fx-bind.js +19 -24
  48. package/src/fx-connection.js +226 -0
  49. package/src/fx-fore.js +846 -815
  50. package/src/fx-header.js +4 -4
  51. package/src/fx-instance.js +23 -27
  52. package/src/fx-model.js +405 -383
  53. package/src/fx-submission.js +399 -397
  54. package/src/fx-var.js +6 -5
  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 +74 -56
  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 -959
  88. package/src/xpath-util.js +161 -134
@@ -1,12 +1,16 @@
1
- import {foreElementMixin} from '../ForeElementMixin.js';
2
- import {evaluateXPathToBoolean, resolveId, evaluateXPath} from '../xpath-evaluation.js';
1
+ import ForeElementMixin from '../ForeElementMixin.js';
2
+ import { evaluateXPathToBoolean, resolveId, evaluateXPath } from '../xpath-evaluation.js';
3
3
  import getInScopeContext from '../getInScopeContext.js';
4
- import {Fore} from '../fore.js';
5
- import {FxFore} from '../fx-fore.js';
6
- import {XPathUtil} from '../xpath-util.js';
4
+ import { Fore } from '../fore.js';
5
+ import { FxFore } from '../fx-fore.js';
6
+ import { XPathUtil } from '../xpath-util.js';
7
7
 
8
+ /**
9
+ * @param {number} howLong How long to wait, in ms
10
+ * @returns {Promise<void>}
11
+ */
8
12
  async function wait(howLong) {
9
- return new Promise(resolve => setTimeout(() => resolve(), howLong));
13
+ return new Promise(resolve => setTimeout(() => resolve(), howLong));
10
14
  }
11
15
 
12
16
  /**
@@ -17,356 +21,365 @@ async function wait(howLong) {
17
21
  * @customElement
18
22
  * @demo demo/index.html
19
23
  */
20
- export class AbstractAction extends foreElementMixin(HTMLElement) {
21
- static dataChanged = false;
22
-
23
- static get properties() {
24
- return {
25
- ...super.properties,
26
- /**
27
- * can be either 'cancel' or 'perform' (default)
28
- */
29
- defaultAction: {
30
- type: String
31
- },
32
- /**
33
- * delay before executing action in milliseconds
34
- */
35
- delay: {
36
- type: Number,
37
- },
38
- /**
39
- * detail - event detail object
40
- */
41
- detail: {
42
- type: Object,
43
- },
44
- /**
45
- * event to listen for
46
- */
47
- event: {
48
- type: Object,
49
- },
50
- handler: {
51
- type: Object,
52
- },
53
- /**
54
- * boolean XPath expression. If true the action will be executed.
55
- */
56
- ifExpr: {
57
- type: String,
58
- },
59
- /**
60
- * The iterate attribute can be added to any XForms action. It contains an expression
61
- * that is evaluated once using the in-scope evaluation context before the action is
62
- * executed, which will result in a sequence of items. The action will be executed with
63
- * each item in the sequence as its context. This context replaces the default in scope
64
- * evaluation context.
65
- *
66
- * The interaction with `@while` and `@if` is undefined.
67
- */
68
- iterateExpr: {
69
- type: String
70
- },
71
- /**
72
- * whether nor not an action needs to run the update cycle
73
- */
74
- needsUpdate: {
75
- type: Boolean,
76
- },
77
- /**
78
- * The observer if given is the element on which an event is triggered. It must be an ancestor of the target
79
- * element of an event.
80
- */
81
- observer: {
82
- type: Object,
83
- },
84
- /**
85
- * can be either 'capture' or 'default' (default)
86
- */
87
- phase: {
88
- type: String,
89
- },
90
- /**
91
- * can be either 'stop' or 'continue' (default)
92
- */
93
- propagate: {
94
- type: String,
95
- },
96
- /**
97
- * id of target element to attach listener to
98
- */
99
- target: {
100
- type: String,
101
- },
102
- /**
103
- * boolean XPath expression. If true loop will be executed. If an ifExpr is present this
104
- * also needs to be true to actually run the action.
105
- */
106
- whileExpr: {
107
- type: String,
108
- },
109
- };
24
+ export class AbstractAction extends ForeElementMixin {
25
+ static dataChanged = false;
26
+
27
+ static get properties() {
28
+ return {
29
+ ...super.properties,
30
+ /**
31
+ * can be either 'cancel' or 'perform' (default)
32
+ */
33
+ defaultAction: {
34
+ type: String,
35
+ },
36
+ /**
37
+ * delay before executing action in milliseconds
38
+ */
39
+ delay: {
40
+ type: Number,
41
+ },
42
+ /**
43
+ * detail - event detail object
44
+ */
45
+ detail: {
46
+ type: Object,
47
+ },
48
+ /**
49
+ * event to listen for
50
+ */
51
+ event: {
52
+ type: Object,
53
+ },
54
+ handler: {
55
+ type: Object,
56
+ },
57
+ /**
58
+ * boolean XPath expression. If true the action will be executed.
59
+ */
60
+ ifExpr: {
61
+ type: String,
62
+ },
63
+ /**
64
+ * The iterate attribute can be added to any XForms action. It contains an expression
65
+ * that is evaluated once using the in-scope evaluation context before the action is
66
+ * executed, which will result in a sequence of items. The action will be executed with
67
+ * each item in the sequence as its context. This context replaces the default in scope
68
+ * evaluation context.
69
+ *
70
+ * The interaction with `@while` and `@if` is undefined.
71
+ */
72
+ iterateExpr: {
73
+ type: String,
74
+ },
75
+ /**
76
+ * whether nor not an action needs to run the update cycle
77
+ */
78
+ needsUpdate: {
79
+ type: Boolean,
80
+ },
81
+ /**
82
+ * The observer if given is the element on which an event is triggered. It must be an ancestor of the target
83
+ * element of an event.
84
+ */
85
+ observer: {
86
+ type: Object,
87
+ },
88
+ /**
89
+ * can be either 'capture' or 'default' (default)
90
+ */
91
+ phase: {
92
+ type: String,
93
+ },
94
+ /**
95
+ * can be either 'stop' or 'continue' (default)
96
+ */
97
+ propagate: {
98
+ type: String,
99
+ },
100
+ /**
101
+ * id of target element to attach listener to
102
+ */
103
+ target: {
104
+ type: String,
105
+ },
106
+ /**
107
+ * boolean XPath expression. If true loop will be executed. If an ifExpr is present this
108
+ * also needs to be true to actually run the action.
109
+ */
110
+ whileExpr: {
111
+ type: String,
112
+ },
113
+ };
114
+ }
115
+
116
+ constructor() {
117
+ super();
118
+ this.detail = {};
119
+ this.needsUpdate = false;
120
+ }
121
+
122
+ disconnectedCallback() {}
123
+
124
+ connectedCallback() {
125
+ this.setAttribute('inert', 'true');
126
+ this.style.display = 'none';
127
+ this.propagate = this.hasAttribute('propagate') ? this.getAttribute('propagate') : 'continue';
128
+ this.repeatContext = undefined;
129
+
130
+ if (this.hasAttribute('event')) {
131
+ this.event = this.getAttribute('event');
110
132
  }
111
-
112
- constructor() {
113
- super();
114
- this.detail = {};
115
- this.needsUpdate = false;
133
+ if (this.hasAttribute('defaultAction')) {
134
+ this.defaultAction = this.getAttribute('defaultAction');
135
+ } else {
136
+ this.defaultAction = 'perform';
137
+ }
138
+ if (this.hasAttribute('phase')) {
139
+ this.phase = this.getAttribute('phase');
140
+ } else {
141
+ this.phase = 'default';
116
142
  }
117
143
 
118
- disconnectedCallback() {
119
- }
120
-
121
- connectedCallback() {
122
- this.style.display = 'none';
123
- this.propagate = this.hasAttribute('propagate') ? this.getAttribute('propagate') : 'continue';
124
- this.repeatContext = undefined;
125
-
126
- if (this.hasAttribute('event')) {
127
- this.event = this.getAttribute('event');
128
- }
129
- if (this.hasAttribute('defaultAction')) {
130
- this.defaultAction = this.getAttribute('defaultAction');
131
- } else {
132
- this.defaultAction = 'perform';
133
- }
134
- if (this.hasAttribute('phase')) {
135
- this.phase = this.getAttribute('phase');
136
- } else {
137
- this.phase = 'default';
138
- }
139
-
140
- /*
144
+ /*
141
145
  this.addEventListener('click', e => {
142
146
  e.preventDefault();
143
147
  e.stopPropagation();
144
148
  });
145
149
  */
146
150
 
147
- this.ifExpr = this.hasAttribute('if') ? this.getAttribute('if') : null;
148
- this.whileExpr = this.hasAttribute('while') ? this.getAttribute('while') : null;
149
- this.delay = this.hasAttribute('delay') ? Number(this.getAttribute('delay')) : 0;
150
- this.iterateExpr = this.hasAttribute('iterate') ? this.getAttribute('iterate') : null;
151
-
152
- this._addUpdateListener();
153
-
154
- }
155
-
156
- _addUpdateListener() {
157
- this.target = this.getAttribute('target');
158
- if (this.target) {
159
- if (this.target === '#window') {
160
- window.addEventListener(this.event, e => this.execute(e), {capture: this.phase === 'capture'});
161
- } else if (this.target === '#document') {
162
- document.addEventListener(this.event, e => this.execute(e), {capture: this.phase === 'capture'});
163
- } else {
164
- this.targetElement = resolveId(this.target, this);
165
- if (!this.targetElement) return; //does not or does not yet exist
166
- this?.targetElement.addEventListener(this.event, e => this.execute(e), {capture: this.phase === 'capture'});
167
- }
168
- } else {
169
- this.targetElement = this.parentNode;
170
- this.targetElement.addEventListener(this.event, e => this.execute(e), {capture: this.phase === 'capture'});
171
- // console.log('adding listener for ', this.event , ` to `, this);
172
- }
151
+ this.ifExpr = this.hasAttribute('if') ? this.getAttribute('if') : null;
152
+ this.whileExpr = this.hasAttribute('while') ? this.getAttribute('while') : null;
153
+ this.delay = this.hasAttribute('delay') ? Number(this.getAttribute('delay')) : 0;
154
+ this.iterateExpr = this.hasAttribute('iterate') ? this.getAttribute('iterate') : null;
155
+
156
+ this._addUpdateListener();
157
+ }
158
+
159
+ _addUpdateListener() {
160
+ this.target = this.getAttribute('target');
161
+ if (this.target) {
162
+ if (this.target === '#window') {
163
+ window.addEventListener(this.event, e => this.execute(e), {
164
+ capture: this.phase === 'capture',
165
+ });
166
+ } else if (this.target === '#document') {
167
+ document.addEventListener(this.event, e => this.execute(e), {
168
+ capture: this.phase === 'capture',
169
+ });
170
+ } else {
171
+ this.targetElement = resolveId(this.target, this);
172
+ if (!this.targetElement) return; // does not or does not yet exist
173
+ this?.targetElement.addEventListener(this.event, e => this.execute(e), {
174
+ capture: this.phase === 'capture',
175
+ });
176
+ }
177
+ } else {
178
+ this.targetElement = this.parentNode;
179
+ this.targetElement.addEventListener(this.event, e => this.execute(e), {
180
+ capture: this.phase === 'capture',
181
+ });
182
+ // console.log('adding listener for ', this.event , ` to `, this);
173
183
  }
174
-
175
- async performSafe() {
176
- try {
177
- await this.perform();
178
- // Return true to indicate success
179
- return true;
180
- } catch (error) {
181
- await Fore.dispatch(this, 'error', {
182
- origin: this,
183
- message: `Action execution failed`,
184
- expr:XPathUtil.getDocPath(this),
185
- level:'Error'
186
- });
187
- // Return false to indicate failure. Any loops must be canceled
188
- return false;
189
- }
184
+ }
185
+
186
+ async performSafe() {
187
+ try {
188
+ await this.perform();
189
+ // Return true to indicate success
190
+ return true;
191
+ } catch (error) {
192
+ await Fore.dispatch(this, 'error', {
193
+ origin: this,
194
+ message: 'Action execution failed',
195
+ expr: XPathUtil.getDocPath(this),
196
+ level: 'Error',
197
+ });
198
+ // Return false to indicate failure. Any loops must be canceled
199
+ return false;
190
200
  }
191
-
192
- /**
193
- * executes the action.
194
- *
195
- * Will first evaluate ifExpr and continue only if it evaluates to 'true'. The 'whileExpr' will be executed
196
- * considering the delay if present.
197
- *
198
- * After calling `perform' which actually implements the semantics of an concrete action
199
- * `actionPerformed` will make sure that update cycle is run if 'needsUpdate' is true.
200
- *
201
- * @param e
202
- */
203
- async execute(e) {
204
- console.log(this, this.event);
205
-
206
- if (e && e.target.nodeType !== Node.DOCUMENT_NODE && e.target !== window ){
207
- /*
201
+ }
202
+
203
+ /**
204
+ * executes the action.
205
+ *
206
+ * Will first evaluate ifExpr and continue only if it evaluates to 'true'. The 'whileExpr' will be executed
207
+ * considering the delay if present.
208
+ *
209
+ * After calling `perform' which actually implements the semantics of an concrete action
210
+ * `actionPerformed` will make sure that update cycle is run if 'needsUpdate' is true.
211
+ *
212
+ * @param e
213
+ */
214
+ async execute(e) {
215
+ if(!this.getModel().modelConstructed) return;
216
+ // console.log(this, this.event);
217
+
218
+ if (e && e.target.nodeType !== Node.DOCUMENT_NODE && e.target !== window) {
219
+ /*
208
220
  ### ignore event if there's a parent fore and the current element is NOT part of it. This avoids
209
221
  ### an event to fire twice on an inner one and the surrounding one(s).
210
222
  ### e.target might be outside an fx-fore element and shouldn't get cancelled in that case.
211
223
  */
212
- if(e.target.closest('fx-fore') && e.target.closest('fx-fore') !== this.closest('fx-fore')) {
213
- // Event originates from a sub-component. Ignore it!
214
- // No need to stop propagation. All other listeners will also ignore it from here
215
- return;
216
- }
217
- }
218
-
219
- if (this.propagate === 'stop') {
220
- // console.log('event propagation stopped', e)
221
- e.stopPropagation();
222
- }
223
- if (this.defaultAction === 'cancel') {
224
- e.preventDefault();
225
- }
226
-
227
- let resolveThisEvent = () => {};
228
- if (e && e.listenerPromises) {
229
- e.listenerPromises.push(
230
- new Promise(resolve => {
231
- resolveThisEvent = resolve;
232
- }),
233
- );
234
- }
224
+ if (e.target.closest('fx-fore') && e.target.closest('fx-fore') !== this.closest('fx-fore')) {
225
+ // Event originates from a sub-component. Ignore it!
226
+ // No need to stop propagation. All other listeners will also ignore it from here
227
+ return;
228
+ }
229
+ }
235
230
 
236
- // Outermost handling
237
- if (FxFore.outermostHandler === null) {
238
- FxFore.outermostHandler = this;
239
- this.dispatchEvent(new CustomEvent('outermost-action-start', {
240
- composed: true,
241
- bubbles: true,
242
- cancelable: true, detail: {cause: e?.type}
243
- }));
244
- }
231
+ if (this.propagate === 'stop') {
232
+ // console.log('event propagation stopped', e)
233
+ e.stopPropagation();
234
+ }
235
+ if (this.defaultAction === 'cancel') {
236
+ e.preventDefault();
237
+ }
245
238
 
239
+ let resolveThisEvent = () => {};
240
+ if (e && e.listenerPromises) {
241
+ e.listenerPromises.push(
242
+ new Promise((resolve) => {
243
+ resolveThisEvent = resolve;
244
+ }),
245
+ );
246
+ }
246
247
 
247
- if (e) {
248
- this.currentEvent = e;
249
- }
250
- this.needsUpdate = false;
248
+ // Outermost handling
249
+ if (FxFore.outermostHandler === null) {
250
+ FxFore.outermostHandler = this;
251
+ this.dispatchEvent(
252
+ new CustomEvent('outermost-action-start', {
253
+ composed: true,
254
+ bubbles: true,
255
+ cancelable: true,
256
+ detail: { cause: e?.type },
257
+ }),
258
+ );
259
+ }
251
260
 
252
- try {
253
- this.evalInContext();
254
- } catch (error) {
255
- console.warn('evaluation failed', error);
256
- }
257
- if (this.targetElement && this.targetElement.nodeset) {
258
- this.nodeset = this.targetElement.nodeset;
259
- }
261
+ if (e) {
262
+ this.currentEvent = e;
263
+ }
264
+ this.needsUpdate = false;
260
265
 
261
- // Order of application between if / while and iterate is undefined. See
262
- // https://www.w3.org/MarkUp/Forms/wiki/@iterate
263
- if (this.iterateExpr) {
264
- // Same as whileExpr, let it go update UI afterwards
265
- await this.handleIterateExpr();
266
- this._finalizePerform(resolveThisEvent);
267
- return;
268
- }
269
-
270
- // Check if 'if' condition is true - otherwise exist right away
271
- if (this.ifExpr && !evaluateXPathToBoolean(this.ifExpr, getInScopeContext(this), this)) {
272
- this._finalizePerform(resolveThisEvent);
273
- return;
274
- }
266
+ try {
267
+ this.evalInContext();
268
+ } catch (error) {
269
+ console.warn('evaluation failed', error);
270
+ }
271
+ if (this.targetElement && this.targetElement.nodeset) {
272
+ this.nodeset = this.targetElement.nodeset;
273
+ }
275
274
 
276
- if (this.whileExpr) {
277
- // After loop is done call actionPerformed to update the model and UI
278
- await this.handleWhileExpr();
279
- this._finalizePerform(resolveThisEvent);
280
- return;
281
- }
275
+ // Order of application between if / while and iterate is undefined. See
276
+ // https://www.w3.org/MarkUp/Forms/wiki/@iterate
277
+ if (this.iterateExpr) {
278
+ // Same as whileExpr, let it go update UI afterwards
279
+ await this.handleIterateExpr();
280
+ this._finalizePerform(resolveThisEvent);
281
+ return;
282
+ }
282
283
 
283
- if (this.delay) {
284
- // Delay further execution until the delay is done
285
- await wait(this.delay);
286
- if (!XPathUtil.contains(this.getOwnerForm(), this)) {
287
- // We are no longer in the document. Stop working
288
- this.actionPerformed();
289
- resolveThisEvent();
290
- return;
291
- }
292
- }
284
+ // Check if 'if' condition is true - otherwise exist right away
285
+ if (this.ifExpr && !evaluateXPathToBoolean(this.ifExpr, getInScopeContext(this), this)) {
286
+ this._finalizePerform(resolveThisEvent);
287
+ return;
288
+ }
293
289
 
294
- await this.performSafe();
295
- this._finalizePerform(resolveThisEvent);
290
+ if (this.whileExpr) {
291
+ // After loop is done call actionPerformed to update the model and UI
292
+ await this.handleWhileExpr();
293
+ this._finalizePerform(resolveThisEvent);
294
+ return;
296
295
  }
297
296
 
298
- async handleWhileExpr () {
299
- // While: while the condition is true, delay a bit and execute the action
300
- // Start by waiting
301
- await wait(this.delay || 0);
297
+ if (this.delay) {
298
+ // Delay further execution until the delay is done
299
+ await wait(this.delay);
300
+ if (!XPathUtil.contains(this.getOwnerForm(), this)) {
301
+ // We are no longer in the document. Stop working
302
+ this.actionPerformed();
303
+ resolveThisEvent();
304
+ return;
305
+ }
306
+ }
302
307
 
303
- if (!XPathUtil.contains(this.getOwnerForm(), this)) {
304
- // We are no longer in the document. Stop working
305
- return;
306
- }
308
+ await this.performSafe();
309
+ this._finalizePerform(resolveThisEvent);
310
+ }
307
311
 
308
- if (!evaluateXPathToBoolean(this.whileExpr, getInScopeContext(this), this)) {
309
- // Done with iterating
310
- return;
311
- }
312
+ async handleWhileExpr() {
313
+ // While: while the condition is true, delay a bit and execute the action
314
+ // Start by waiting
315
+ await wait(this.delay || 0);
312
316
 
313
- // Perform the action once. But quit if it failed
314
- if (!this.performSafe()) {
315
- return;
316
- }
317
+ if (!XPathUtil.contains(this.getOwnerForm(), this)) {
318
+ // We are no longer in the document. Stop working
319
+ return;
320
+ }
317
321
 
318
- // Go for one more iteration
319
- if (this.delay) {
320
- // If we have a delay, fire and forget this.
321
- // Otherwise, if we have no delay, keep waiting for all iterations to be done.
322
- // The while is then uninterruptable and immediate
322
+ if (!evaluateXPathToBoolean(this.whileExpr, getInScopeContext(this), this)) {
323
+ // Done with iterating
324
+ return;
325
+ }
323
326
 
324
- this.handleWhileExpr();
325
- return;
326
- }
327
+ // Perform the action once. But quit if it failed
328
+ if (!this.performSafe()) {
329
+ return;
330
+ }
327
331
 
332
+ // Go for one more iteration
333
+ if (this.delay) {
334
+ // If we have a delay, fire and forget this.
335
+ // Otherwise, if we have no delay, keep waiting for all iterations to be done.
336
+ // The while is then uninterruptable and immediate
328
337
 
329
- await this.handleWhileExpr();
330
- }
338
+ this.handleWhileExpr();
339
+ return;
340
+ }
331
341
 
332
- async handleIterateExpr () {
333
- try {
334
- // Iterate: get the context sequence and perform the action once per item.
335
- const contextSequence = evaluateXPath(this.iterateExpr, getInScopeContext(this), this);
342
+ await this.handleWhileExpr();
343
+ }
336
344
 
337
- if (contextSequence.length === 0) {
338
- return;
339
- }
345
+ async handleIterateExpr() {
346
+ try {
347
+ // Iterate: get the context sequence and perform the action once per item.
348
+ const contextSequence = evaluateXPath(this.iterateExpr, getInScopeContext(this), this);
340
349
 
341
- if (!XPathUtil.contains(this.getOwnerForm(), this)) {
342
- // We are no longer in the document. Stop working
343
- return;
344
- }
350
+ if (contextSequence.length === 0) {
351
+ return;
352
+ }
345
353
 
346
- for (const item of contextSequence) {
347
- if (this.delay) {
348
- await wait(this.delay || 0);
349
- }
354
+ if (!XPathUtil.contains(this.getOwnerForm(), this)) {
355
+ // We are no longer in the document. Stop working
356
+ return;
357
+ }
350
358
 
351
- // This will be picked up in `getInscopeContext`
352
- this.currentContext = item;
359
+ for (const item of contextSequence) {
360
+ if (this.delay) {
361
+ await wait(this.delay || 0);
362
+ }
353
363
 
354
- // Perform the action once. But quit if it failed
355
- if (!await this.performSafe()) {
356
- return;
357
- }
358
- }
359
- } finally {
360
- this.currentContext = null;
361
- }
362
- }
364
+ // This will be picked up in `getInscopeContext`
365
+ this.currentContext = item;
363
366
 
364
- _finalizePerform(resolveThisEvent) {
365
- this.currentEvent = null;
366
- this.actionPerformed();
367
- if (FxFore.outermostHandler === this) {
368
- FxFore.outermostHandler = null;
369
- /*
367
+ // Perform the action once. But quit if it failed
368
+ if (!(await this.performSafe())) {
369
+ return;
370
+ }
371
+ }
372
+ } finally {
373
+ this.currentContext = null;
374
+ }
375
+ }
376
+
377
+ _finalizePerform(resolveThisEvent) {
378
+ this.currentEvent = null;
379
+ this.actionPerformed();
380
+ if (FxFore.outermostHandler === this) {
381
+ FxFore.outermostHandler = null;
382
+ /*
370
383
  console.info(
371
384
  `%coutermost Action done`,
372
385
  'background:#e65100; color:white; padding:0.3rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;',
@@ -374,93 +387,89 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
374
387
  );
375
388
  console.timeEnd('outermostHandler');
376
389
  */
377
- this.dispatchEvent(new CustomEvent('outermost-action-end', {
378
- composed: true,
379
- bubbles: true,
380
- cancelable: true
381
- }));
382
-
383
-
384
- }
385
- resolveThisEvent();
390
+ this.dispatchEvent(
391
+ new CustomEvent('outermost-action-end', {
392
+ composed: true,
393
+ bubbles: true,
394
+ cancelable: true,
395
+ }),
396
+ );
386
397
  }
387
-
388
- /**
389
- * Template method to be implemented by each action that is called by execute() as part of
390
- * the processing.
391
- *
392
- * This function should not called on any action directly - call execute() instead to ensure proper execution of 'if' and 'while'
393
- */
394
- async perform() {
395
- // await Fore.dispatch(document, 'execute-action', {action:this, event:this.event});
396
-
397
- //todo: review - this evaluation seems redundant as we already evaluated in execute
398
- if (this.isBound() || this.nodeName === 'FX-ACTION') {
399
- this.evalInContext();
400
- }
401
-
402
- this.dispatchEvent(
403
- new CustomEvent('execute-action', {
404
- composed: true,
405
- bubbles: true,
406
- cancelable: true,
407
- detail: {action: this, event: this.event},
408
- }),
409
- );
410
-
398
+ resolveThisEvent();
399
+ }
400
+
401
+ /**
402
+ * Template method to be implemented by each action that is called by execute() as part of
403
+ * the processing.
404
+ *
405
+ * This function should not called on any action directly - call execute() instead to ensure proper execution of 'if' and 'while'
406
+ */
407
+ async perform() {
408
+ // await Fore.dispatch(document, 'execute-action', {action:this, event:this.event});
409
+
410
+ // todo: review - this evaluation seems redundant as we already evaluated in execute
411
+ if (this.isBound() || this.nodeName === 'FX-ACTION') {
412
+ this.evalInContext();
411
413
  }
412
414
 
413
- /**
414
- * calls the update cycle if action signalled that update is needed.
415
- */
416
- actionPerformed() {
417
- const model = this.getModel();
418
- if (!model) {
419
- return;
420
- }
421
- if (!model.inited) {
422
- return;
423
- }
424
- if (
425
- FxFore.outermostHandler &&
426
- !XPathUtil.contains(FxFore.outermostHandler.ownerDocument, FxFore.outermostHandler)
427
- ) {
428
- // The old outermostHandler fell out of the document. An error has happened.
429
- // Just remove the old one and act like we are starting anew.
430
- // console.warn('Unsetting outermost handler');
431
- FxFore.outermostHandler = null;
432
- }
433
- // console.log('actionPerformed action parentNode ', this.parentNode);
434
- if (
435
- this.needsUpdate &&
436
- (FxFore.outermostHandler === this || !FxFore.outermostHandler)
437
- ) {
438
- // console.log('running update cycle for outermostHandler', this);
439
- model.recalculate();
440
- model.revalidate();
441
- model.parentNode.refresh(true);
442
- this.dispatchActionPerformed();
443
- } else if (this.needsUpdate) {
444
- // console.log('Update delayed!');
445
- // We need an update, but the outermost action handler is not done yet. Make this clear!
446
- // console.log('running actionperformed on', this, ' to be updated by ', FxFore.outermostHandler);
447
- FxFore.outermostHandler.needsUpdate = true;
448
- }
449
-
450
- // console.log('running actionperformed on', this, ' outermostHandler', FxFore.outermostHandler);
415
+ this.dispatchEvent(
416
+ new CustomEvent('execute-action', {
417
+ composed: true,
418
+ bubbles: true,
419
+ cancelable: true,
420
+ detail: { action: this, event: this.event },
421
+ }),
422
+ );
423
+ }
424
+
425
+ /**
426
+ * calls the update cycle if action signalled that update is needed.
427
+ */
428
+ actionPerformed() {
429
+ const model = this.getModel();
430
+ if (!model) {
431
+ return;
451
432
  }
452
-
453
- /**
454
- * dispatches action-performed event
455
- *
456
- * @event action-performed - whenever an action has been run
457
- */
458
- dispatchActionPerformed() {
459
- // console.log('action-performed ', this);
460
- Fore.dispatch(this, 'action-performed', {});
433
+ if (!model.inited) {
434
+ return;
435
+ }
436
+ if (
437
+ FxFore.outermostHandler
438
+ && !XPathUtil.contains(FxFore.outermostHandler.ownerDocument, FxFore.outermostHandler)
439
+ ) {
440
+ // The old outermostHandler fell out of the document. An error has happened.
441
+ // Just remove the old one and act like we are starting anew.
442
+ // console.warn('Unsetting outermost handler');
443
+ FxFore.outermostHandler = null;
461
444
  }
445
+ // console.log('actionPerformed action parentNode ', this.parentNode);
446
+ if (this.needsUpdate && (FxFore.outermostHandler === this || !FxFore.outermostHandler)) {
447
+ // console.log('running update cycle for outermostHandler', this);
448
+ model.recalculate();
449
+ model.revalidate();
450
+ model.parentNode.refresh(true);
451
+ this.dispatchActionPerformed();
452
+ } else if (this.needsUpdate) {
453
+ // console.log('Update delayed!');
454
+ // We need an update, but the outermost action handler is not done yet. Make this clear!
455
+ // console.log('running actionperformed on', this, ' to be updated by ', FxFore.outermostHandler);
456
+ FxFore.outermostHandler.needsUpdate = true;
457
+ }
458
+
459
+ // console.log('running actionperformed on', this, ' outermostHandler', FxFore.outermostHandler);
460
+ }
461
+
462
+ /**
463
+ * dispatches action-performed event
464
+ *
465
+ * @event action-performed - whenever an action has been run
466
+ */
467
+ dispatchActionPerformed() {
468
+ // console.log('action-performed ', this);
469
+ Fore.dispatch(this, 'action-performed', {});
470
+ }
462
471
  }
463
472
 
464
473
  if (!customElements.get('abstract-action')) {
465
- window.customElements.define('abstract-action', AbstractAction);
474
+ window.customElements.define('abstract-action', AbstractAction);
466
475
  }