@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.
- package/README.md +2 -2
- package/dist/fore-dev.js +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/index.js +4 -2
- package/package.json +10 -38
- package/resources/fore.css +263 -262
- package/resources/vars.css +8 -0
- package/src/DependencyNotifyingDomFacade.js +1 -0
- package/src/ForeElementMixin.js +243 -228
- package/src/actions/abstract-action.js +407 -398
- package/src/actions/fx-action.js +6 -6
- package/src/actions/fx-append.js +1 -1
- package/src/actions/fx-call.js +63 -62
- package/src/actions/fx-confirm.js +11 -11
- package/src/actions/fx-construct-done.js +4 -4
- package/src/actions/fx-copy.js +36 -36
- package/src/actions/fx-delete.js +77 -79
- package/src/actions/fx-dispatch.js +14 -14
- package/src/actions/fx-hide.js +15 -15
- package/src/actions/fx-insert.js +9 -12
- package/src/actions/fx-insertchild.js +88 -0
- package/src/actions/fx-load.js +188 -185
- package/src/actions/fx-message.js +18 -19
- package/src/actions/fx-refresh.js +10 -10
- package/src/actions/fx-reload.js +16 -14
- package/src/actions/fx-replace.js +0 -1
- package/src/actions/fx-reset.js +31 -31
- package/src/actions/fx-send.js +59 -52
- package/src/actions/fx-setattribute.js +48 -49
- package/src/actions/fx-setfocus.js +54 -58
- package/src/actions/fx-setvalue.js +85 -81
- package/src/actions/fx-show.js +11 -11
- package/src/actions/fx-toggle.js +2 -2
- package/src/actions/fx-toggleboolean.js +38 -39
- package/src/actions/fx-unmodified.js +27 -0
- package/src/actions/fx-update.js +6 -6
- package/src/dep_graph.js +1 -1
- package/src/drawdown.js +23 -30
- package/src/events.js +19 -19
- package/src/fore.js +143 -125
- package/src/functions/common-function.js +24 -25
- package/src/functions/fx-function.js +6 -99
- package/src/functions/fx-functionlib.js +56 -0
- package/src/functions/registerFunction.js +112 -0
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +844 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +25 -29
- package/src/fx-model.js +405 -380
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +13 -12
- package/src/getInScopeContext.js +102 -89
- package/src/json-util.js +24 -24
- package/src/lab/fore-component.js +48 -52
- package/src/lab/instance-inspector.js +13 -16
- package/src/modelitem.js +48 -10
- package/src/relevance.js +12 -16
- package/src/tools/adi.js +858 -812
- package/src/tools/fx-action-log.js +377 -295
- package/src/tools/fx-devtools.js +210 -210
- package/src/tools/fx-dom-inspector.js +123 -122
- package/src/tools/fx-json-instance.js +262 -253
- package/src/tools/fx-log-item.js +6 -11
- package/src/tools/fx-log-settings.js +358 -301
- package/src/tools/fx-minimap.js +186 -177
- package/src/tools/helpers.js +1 -1
- package/src/ui/abstract-control.js +73 -53
- package/src/ui/fx-case.js +59 -7
- package/src/ui/fx-container.js +13 -14
- package/src/ui/fx-control.js +572 -538
- package/src/ui/fx-dialog.js +3 -3
- package/src/ui/fx-droptarget.js +3 -4
- package/src/ui/fx-group.js +4 -2
- package/src/ui/fx-hint.js +3 -0
- package/src/ui/fx-inspector.js +5 -5
- package/src/ui/fx-items.js +11 -11
- package/src/ui/fx-output.js +10 -12
- package/src/ui/fx-repeat-attributes.js +23 -27
- package/src/ui/fx-repeat.js +347 -344
- package/src/ui/fx-repeatitem.js +75 -68
- package/src/ui/fx-switch.js +39 -14
- package/src/ui/fx-trigger.js +4 -4
- package/src/withDraggability.js +193 -191
- package/src/xpath-evaluation.js +969 -958
- package/src/xpath-util.js +161 -134
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
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
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
295
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
308
|
+
await this.performSafe();
|
|
309
|
+
this._finalizePerform(resolveThisEvent);
|
|
310
|
+
}
|
|
307
311
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
+
if (!XPathUtil.contains(this.getOwnerForm(), this)) {
|
|
318
|
+
// We are no longer in the document. Stop working
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
317
321
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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
|
-
|
|
330
|
-
|
|
338
|
+
this.handleWhileExpr();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
331
341
|
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
338
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
350
|
+
if (contextSequence.length === 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
345
353
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
354
|
+
if (!XPathUtil.contains(this.getOwnerForm(), this)) {
|
|
355
|
+
// We are no longer in the document. Stop working
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
350
358
|
|
|
351
|
-
|
|
352
|
-
|
|
359
|
+
for (const item of contextSequence) {
|
|
360
|
+
if (this.delay) {
|
|
361
|
+
await wait(this.delay || 0);
|
|
362
|
+
}
|
|
353
363
|
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
365
|
-
this.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
474
|
+
window.customElements.define('abstract-action', AbstractAction);
|
|
466
475
|
}
|