@jinntec/fore 0.25.0 → 1.0.0-2
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 +75 -22
- package/dist/fore-all.js +11 -11
- package/dist/fore.js +1 -1
- package/index.js +5 -1
- package/package.json +17 -6
- package/resources/fore.css +121 -4
- package/resources/toastify.css +3 -1
- package/src/DependencyNotifyingDomFacade.js +9 -1
- package/src/ForeElementMixin.js +83 -12
- package/src/actions/abstract-action.js +101 -27
- package/src/actions/fx-action.js +4 -2
- package/src/actions/fx-append.js +21 -18
- package/src/actions/fx-confirm.js +22 -0
- package/src/actions/fx-dispatch.js +10 -2
- package/src/actions/fx-insert.js +35 -30
- package/src/actions/fx-message.js +7 -1
- package/src/actions/fx-send.js +1 -1
- package/src/actions/fx-setvalue.js +2 -9
- package/src/dep_graph.js +9 -0
- package/src/drawdown.js +172 -0
- package/src/fore.js +126 -18
- package/src/functions/fx-function.js +2 -2
- package/src/fx-bind.js +11 -7
- package/src/fx-fore.js +283 -67
- package/src/fx-header.js +20 -0
- package/src/fx-instance.js +54 -10
- package/src/fx-model.js +175 -38
- package/src/fx-submission.js +235 -53
- package/src/getInScopeContext.js +2 -3
- package/src/ui/abstract-control.js +23 -44
- package/src/ui/fx-alert.js +20 -19
- package/src/ui/fx-container.js +9 -4
- package/src/ui/fx-control.js +92 -37
- package/src/ui/fx-inspector.js +44 -0
- package/src/ui/fx-items.js +104 -20
- package/src/ui/fx-output.js +92 -3
- package/src/ui/fx-repeat.js +87 -80
- package/src/ui/fx-repeatitem.js +38 -48
- package/src/ui/fx-trigger.js +49 -27
- package/src/xpath-evaluation.js +533 -235
- package/src/xpath-util.js +50 -12
package/resources/fore.css
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
@import 'toastify.css';
|
|
2
|
+
@import 'vars.css';
|
|
2
3
|
|
|
4
|
+
html{
|
|
5
|
+
--inspector-bg:var(--paper-grey-500);
|
|
6
|
+
--inspector-pre-bg:var(--paper-grey-100);
|
|
7
|
+
--inspector-color:var(--paper-grey-800);
|
|
8
|
+
--inspector-instance-height:200px;
|
|
9
|
+
}
|
|
3
10
|
[unresolved]{
|
|
4
11
|
display: none;
|
|
5
12
|
}
|
|
13
|
+
[disabled] {
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
cursor: default;
|
|
16
|
+
}
|
|
17
|
+
fx-trigger a[disabled] {
|
|
18
|
+
color:lightgrey;
|
|
19
|
+
}
|
|
20
|
+
fx-trigger img[disabled]{
|
|
21
|
+
filter:blur(2px);
|
|
22
|
+
}
|
|
6
23
|
|
|
24
|
+
.error{
|
|
25
|
+
background: var(--paper-red-500);
|
|
26
|
+
}
|
|
7
27
|
fx-alert{
|
|
8
28
|
color:darkred;
|
|
9
29
|
font-size: 0.9rem;
|
|
@@ -13,16 +33,91 @@ fx-model, fx-model *, fx-model ::slotted(fx-instance), fx-instance{
|
|
|
13
33
|
display:none;
|
|
14
34
|
}
|
|
15
35
|
|
|
16
|
-
fx-control{
|
|
36
|
+
fx-control, fx-trigger{
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
position: relative;
|
|
39
|
+
}
|
|
40
|
+
.fx-checkbox{
|
|
17
41
|
white-space: nowrap;
|
|
18
42
|
}
|
|
43
|
+
fx-hint{
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
fx-repeatitem{
|
|
47
|
+
position:relative;
|
|
48
|
+
/*
|
|
49
|
+
opacity:1;
|
|
50
|
+
-webkit-transition: opacity 3s;
|
|
51
|
+
-moz-transition: opacity 3s;
|
|
52
|
+
transition: opacity 3s;
|
|
53
|
+
*/
|
|
54
|
+
}
|
|
55
|
+
.hidden {
|
|
56
|
+
visibility: hidden;
|
|
57
|
+
opacity: 0;
|
|
58
|
+
transition: visibility 0s 2s, opacity 2s linear;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* fx-inspector styles */
|
|
62
|
+
fx-inspector{
|
|
63
|
+
position:fixed;
|
|
64
|
+
left:0;
|
|
65
|
+
right:0;
|
|
66
|
+
bottom:0;
|
|
67
|
+
width: 100%;
|
|
68
|
+
background: var(--inspector-bg);
|
|
69
|
+
color: white;
|
|
70
|
+
max-height: 33%;
|
|
71
|
+
overflow: scroll;
|
|
72
|
+
border-top:3px solid black;
|
|
73
|
+
opacity: 0.9;
|
|
74
|
+
}
|
|
75
|
+
fx-inspector::before{
|
|
76
|
+
content:'Instance Inspector';
|
|
77
|
+
font-style:italic;
|
|
78
|
+
position:absolute;
|
|
79
|
+
top:0;
|
|
80
|
+
right: 0.3rem;
|
|
81
|
+
z-index: 10;
|
|
82
|
+
font-size: 0.8rem;
|
|
83
|
+
}
|
|
84
|
+
fx-inspector details{
|
|
85
|
+
padding: 1rem;
|
|
86
|
+
}
|
|
87
|
+
fx-inspector pre{
|
|
88
|
+
background: var(--inspector-pre-bg);
|
|
89
|
+
border-radius: 0.5rem;
|
|
90
|
+
padding: 0.3rem;
|
|
91
|
+
max-height: var(--inspector-instance-height);
|
|
92
|
+
/*overflow: scroll;*/
|
|
93
|
+
}
|
|
94
|
+
fx-inspector details{
|
|
95
|
+
overflow: auto;
|
|
96
|
+
margin:0;
|
|
97
|
+
}
|
|
98
|
+
fx-inspector summary{
|
|
99
|
+
padding: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/*
|
|
104
|
+
.visible {
|
|
105
|
+
visibility: visible;
|
|
106
|
+
opacity: 1;
|
|
107
|
+
transition: opacity 2s linear;
|
|
108
|
+
}
|
|
109
|
+
*/
|
|
110
|
+
|
|
19
111
|
.required:after {
|
|
20
112
|
content: '*';
|
|
21
113
|
color: red;
|
|
22
114
|
padding-left: 4px;
|
|
115
|
+
right:3px;
|
|
116
|
+
top:3px;
|
|
117
|
+
z-index: 1;
|
|
23
118
|
}
|
|
24
119
|
|
|
25
|
-
|
|
120
|
+
[required]:after {
|
|
26
121
|
content: "*";
|
|
27
122
|
display: inline;
|
|
28
123
|
color: red;
|
|
@@ -34,8 +129,30 @@ fx-control [required]:after {
|
|
|
34
129
|
.logtree details summary{
|
|
35
130
|
|
|
36
131
|
}
|
|
37
|
-
|
|
132
|
+
.non-relevant{
|
|
133
|
+
opacity: 0;
|
|
134
|
+
height: 0;
|
|
135
|
+
transition: opacity 1s;
|
|
136
|
+
}
|
|
38
137
|
.vertical label{
|
|
39
138
|
display: block;
|
|
40
139
|
}
|
|
41
|
-
|
|
140
|
+
[refresh-on-view]{
|
|
141
|
+
/*opacity: 0;*/
|
|
142
|
+
}
|
|
143
|
+
.loaded{
|
|
144
|
+
animation: fadein 0.3s forwards;
|
|
145
|
+
}
|
|
146
|
+
/* avoid flicker from nested lazily loaded elements */
|
|
147
|
+
.loaded .loaded{
|
|
148
|
+
animation: none;
|
|
149
|
+
opacity: 1;
|
|
150
|
+
}
|
|
151
|
+
@keyframes fadein {
|
|
152
|
+
0% {
|
|
153
|
+
opacity:0;
|
|
154
|
+
}
|
|
155
|
+
100% {
|
|
156
|
+
opacity:1;
|
|
157
|
+
}
|
|
158
|
+
}
|
package/resources/toastify.css
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
display: inline-block;
|
|
13
13
|
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
|
|
14
14
|
background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
|
|
15
|
-
background: linear-gradient(135deg, #
|
|
15
|
+
background: linear-gradient(135deg, #f8f8f9, #cfcfcf);
|
|
16
16
|
position: fixed;
|
|
17
17
|
opacity: 0;
|
|
18
18
|
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
text-decoration: none;
|
|
22
22
|
max-width: calc(50% - 20px);
|
|
23
23
|
z-index: 2147483647;
|
|
24
|
+
font-weight: 300;
|
|
25
|
+
color:black;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
.toastify.on {
|
|
@@ -9,7 +9,7 @@ import { getBucketsForNode } from 'fontoxpath';
|
|
|
9
9
|
*/
|
|
10
10
|
export class DependencyNotifyingDomFacade {
|
|
11
11
|
/**
|
|
12
|
-
* @param
|
|
12
|
+
* @param onNodeTouched - onNodeTouched A function what will be executed whenever a node is 'touched' by the XPath
|
|
13
13
|
*/
|
|
14
14
|
constructor(onNodeTouched) {
|
|
15
15
|
this._onNodeTouched = onNodeTouched;
|
|
@@ -45,13 +45,21 @@ export class DependencyNotifyingDomFacade {
|
|
|
45
45
|
* @param bucket - The bucket that matches the attribute that will be used.
|
|
46
46
|
*/
|
|
47
47
|
// eslint-disable-next-line class-methods-use-this
|
|
48
|
+
/*
|
|
48
49
|
getChildNodes(node, bucket) {
|
|
49
50
|
const matchingNodes = Array.from(node.childNodes).filter(
|
|
50
51
|
childNode => !bucket || getBucketsForNode(childNode).includes(bucket),
|
|
51
52
|
);
|
|
52
53
|
return matchingNodes;
|
|
53
54
|
}
|
|
55
|
+
*/
|
|
54
56
|
|
|
57
|
+
getChildNodes(node, bucket) {
|
|
58
|
+
const matchingNodes = Array.from(node.childNodes).filter(
|
|
59
|
+
childNode => !bucket || getBucketsForNode(childNode).includes(bucket));
|
|
60
|
+
matchingNodes.forEach(matchingNode => this._onNodeTouched(matchingNode));
|
|
61
|
+
return matchingNodes;
|
|
62
|
+
}
|
|
55
63
|
/**
|
|
56
64
|
* Get the data of this node.
|
|
57
65
|
*
|
package/src/ForeElementMixin.js
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
import { XPathUtil } from './xpath-util.js';
|
|
2
2
|
import { FxModel } from './fx-model.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
evaluateXPath,
|
|
5
|
+
evaluateXPathToFirstNode,
|
|
6
|
+
evaluateXPathToString,
|
|
7
|
+
} from './xpath-evaluation.js';
|
|
4
8
|
import getInScopeContext from './getInScopeContext.js';
|
|
5
9
|
|
|
6
10
|
export const foreElementMixin = superclass =>
|
|
7
11
|
class ForeElementMixin extends superclass {
|
|
8
12
|
static get properties() {
|
|
9
13
|
return {
|
|
14
|
+
/**
|
|
15
|
+
* context object for evaluation
|
|
16
|
+
*/
|
|
10
17
|
context: {
|
|
11
18
|
type: Object,
|
|
12
19
|
},
|
|
20
|
+
/**
|
|
21
|
+
* the model of this element
|
|
22
|
+
*/
|
|
13
23
|
model: {
|
|
14
24
|
type: Object,
|
|
15
25
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The modelitem object associated to the bound node holding the evaluated state.
|
|
28
|
+
*/
|
|
19
29
|
modelItem: {
|
|
20
30
|
type: Object,
|
|
21
31
|
},
|
|
32
|
+
/**
|
|
33
|
+
* the node(s) bound by this element
|
|
34
|
+
*/
|
|
35
|
+
nodeset: {
|
|
36
|
+
type: Object,
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* XPath binding expression pointing to bound node
|
|
40
|
+
*/
|
|
41
|
+
ref: {
|
|
42
|
+
type: String,
|
|
43
|
+
},
|
|
22
44
|
};
|
|
23
45
|
}
|
|
24
46
|
|
|
@@ -41,6 +63,10 @@ export const foreElementMixin = superclass =>
|
|
|
41
63
|
return ownerForm.querySelector('fx-model');
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @returns {{parentNode}|ForeElementMixin}
|
|
69
|
+
*/
|
|
44
70
|
getOwnerForm() {
|
|
45
71
|
let currentElement = this;
|
|
46
72
|
while (currentElement && currentElement.parentNode) {
|
|
@@ -65,29 +91,33 @@ export const foreElementMixin = superclass =>
|
|
|
65
91
|
evalInContext() {
|
|
66
92
|
// const inscopeContext = this.getInScopeContext();
|
|
67
93
|
const inscopeContext = getInScopeContext(this, this.ref);
|
|
94
|
+
if (!inscopeContext) {
|
|
95
|
+
console.warn('no in scopeContext for ', this);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
68
98
|
if (this.ref === '') {
|
|
69
99
|
this.nodeset = inscopeContext;
|
|
70
100
|
} else if (Array.isArray(inscopeContext)) {
|
|
101
|
+
/*
|
|
71
102
|
inscopeContext.forEach(n => {
|
|
72
103
|
if (XPathUtil.isSelfReference(this.ref)) {
|
|
73
104
|
this.nodeset = inscopeContext;
|
|
74
105
|
} else {
|
|
75
|
-
const localResult = evaluateXPathToFirstNode(this.ref, n,
|
|
106
|
+
const localResult = evaluateXPathToFirstNode(this.ref, n, this);
|
|
76
107
|
// console.log('local result: ', localResult);
|
|
77
108
|
this.nodeset.push(localResult);
|
|
78
109
|
}
|
|
79
110
|
});
|
|
111
|
+
*/
|
|
112
|
+
this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext[0], this);
|
|
80
113
|
} else {
|
|
81
114
|
// this.nodeset = fx.evaluateXPathToFirstNode(this.ref, inscopeContext, null, {namespaceResolver: this.namespaceResolver});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (inscopeContext.nodeType) {
|
|
86
|
-
this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, formElement);
|
|
115
|
+
const { nodeType } = inscopeContext;
|
|
116
|
+
if (nodeType) {
|
|
117
|
+
this.nodeset = evaluateXPathToFirstNode(this.ref, inscopeContext, this);
|
|
87
118
|
} else {
|
|
88
|
-
this.nodeset = evaluateXPath(this.ref, inscopeContext,
|
|
119
|
+
this.nodeset = evaluateXPath(this.ref, inscopeContext, this);
|
|
89
120
|
}
|
|
90
|
-
// this.nodeset = evaluateXPath(this.ref,inscopeContext,formElement)
|
|
91
121
|
}
|
|
92
122
|
// console.log('UiElement evaluated to nodeset: ', this.nodeset);
|
|
93
123
|
}
|
|
@@ -112,6 +142,14 @@ export const foreElementMixin = superclass =>
|
|
|
112
142
|
return parent.getAttribute('ref');
|
|
113
143
|
}
|
|
114
144
|
|
|
145
|
+
getInstance() {
|
|
146
|
+
if (this.ref.startsWith('instance(')) {
|
|
147
|
+
const instId = XPathUtil.getInstanceId(this.ref);
|
|
148
|
+
return this.getModel().getInstance(instId);
|
|
149
|
+
}
|
|
150
|
+
return this.getModel().getInstance('default');
|
|
151
|
+
}
|
|
152
|
+
|
|
115
153
|
_getParentBindingElement(start) {
|
|
116
154
|
if (start.parentNode.host) {
|
|
117
155
|
const { host } = start.parentNode;
|
|
@@ -155,7 +193,40 @@ export const foreElementMixin = superclass =>
|
|
|
155
193
|
return existed;
|
|
156
194
|
}
|
|
157
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Returns the effective value for the element.
|
|
198
|
+
* a: look for 'value' attribute and if present evaluate it and return the resulting value
|
|
199
|
+
* b: look for textContent and return the value if present
|
|
200
|
+
* c: return null
|
|
201
|
+
*/
|
|
202
|
+
getValue() {
|
|
203
|
+
if (this.hasAttribute('value')) {
|
|
204
|
+
const valAttr = this.getAttribute('value');
|
|
205
|
+
try {
|
|
206
|
+
const inscopeContext = getInScopeContext(this, valAttr);
|
|
207
|
+
return evaluateXPathToString(valAttr, inscopeContext, this.getOwnerForm());
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(error);
|
|
210
|
+
this.dispatch('error', { message: error });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (this.textContent) {
|
|
214
|
+
return this.textContent;
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
158
219
|
getInScopeContext() {
|
|
159
220
|
return getInScopeContext(this, this.ref);
|
|
160
221
|
}
|
|
222
|
+
|
|
223
|
+
dispatch(eventName, detail) {
|
|
224
|
+
const event = new CustomEvent(eventName, {
|
|
225
|
+
composed: true,
|
|
226
|
+
bubbles: true,
|
|
227
|
+
detail,
|
|
228
|
+
});
|
|
229
|
+
// console.log('firing', event);
|
|
230
|
+
this.dispatchEvent(event);
|
|
231
|
+
}
|
|
161
232
|
};
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { foreElementMixin } from '../ForeElementMixin.js';
|
|
2
2
|
import { evaluateXPathToBoolean } from '../xpath-evaluation.js';
|
|
3
3
|
|
|
4
|
+
async function wait(howLong) {
|
|
5
|
+
return new Promise(resolve => setTimeout(() => resolve(), howLong));
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
9
|
+
* Superclass for all action elements. Provides basic wiring of events to targets as well as
|
|
10
|
+
* handle conditionals and loops of actions.
|
|
7
11
|
*
|
|
12
|
+
* @fires action-performed - is dispatched after each execution of an action.
|
|
8
13
|
* @customElement
|
|
9
14
|
* @demo demo/index.html
|
|
10
15
|
*/
|
|
@@ -12,12 +17,49 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
12
17
|
static get properties() {
|
|
13
18
|
return {
|
|
14
19
|
...super.properties,
|
|
20
|
+
/**
|
|
21
|
+
* detail - event detail object
|
|
22
|
+
*/
|
|
15
23
|
detail: {
|
|
16
24
|
type: Object,
|
|
17
25
|
},
|
|
26
|
+
/**
|
|
27
|
+
* wether nor not an action needs to run the update cycle
|
|
28
|
+
*/
|
|
18
29
|
needsUpdate: {
|
|
19
30
|
type: Boolean,
|
|
20
31
|
},
|
|
32
|
+
/**
|
|
33
|
+
* event to listen for
|
|
34
|
+
*/
|
|
35
|
+
event: {
|
|
36
|
+
type: Object,
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* id of target element to attach listener to
|
|
40
|
+
*/
|
|
41
|
+
target: {
|
|
42
|
+
type: String,
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* boolean XPath expression. If true the action will be executed.
|
|
46
|
+
*/
|
|
47
|
+
ifExpr: {
|
|
48
|
+
type: String,
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* boolean XPath expression. If true loop will be executed. If an ifExpr is present this also needs to be true
|
|
52
|
+
* to actually run the action.
|
|
53
|
+
*/
|
|
54
|
+
whileExpr: {
|
|
55
|
+
type: String,
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* delay before executing action in milliseconds
|
|
59
|
+
*/
|
|
60
|
+
delay: {
|
|
61
|
+
type: Number,
|
|
62
|
+
},
|
|
21
63
|
};
|
|
22
64
|
}
|
|
23
65
|
|
|
@@ -39,7 +81,9 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
39
81
|
|
|
40
82
|
this.target = this.getAttribute('target');
|
|
41
83
|
if (this.target) {
|
|
42
|
-
if (this.target === '#
|
|
84
|
+
if (this.target === '#window') {
|
|
85
|
+
window.addEventListener(this.event, e => this.execute(e));
|
|
86
|
+
} else if (this.target === '#document') {
|
|
43
87
|
document.addEventListener(this.event, e => this.execute(e));
|
|
44
88
|
} else {
|
|
45
89
|
this.targetElement = document.getElementById(this.target);
|
|
@@ -57,12 +101,18 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
57
101
|
}
|
|
58
102
|
|
|
59
103
|
/**
|
|
60
|
-
* executes the action.
|
|
104
|
+
* executes the action.
|
|
105
|
+
*
|
|
106
|
+
* Will first evaluate ifExpr and continue only if it evaluates to 'true'. The 'whileExpr' will be executed
|
|
107
|
+
* considering the delay if present.
|
|
108
|
+
*
|
|
109
|
+
* After calling `perform' which actually implements the semantics of an concrete action
|
|
110
|
+
* `actionPerformed` will make sure that update cycle is run if 'needsUpdate' is true.
|
|
61
111
|
*
|
|
62
112
|
* @param e
|
|
63
113
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
114
|
+
async execute(e) {
|
|
115
|
+
console.log('executing', this);
|
|
66
116
|
if (e && e.detail) {
|
|
67
117
|
this.detail = e.detail;
|
|
68
118
|
}
|
|
@@ -73,38 +123,57 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
73
123
|
this.nodeset = this.targetElement.nodeset;
|
|
74
124
|
}
|
|
75
125
|
|
|
76
|
-
if
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
126
|
+
// First check if 'if' condition is true - otherwise exist right away
|
|
127
|
+
if (this.ifExpr && !evaluateXPathToBoolean(this.ifExpr, this.nodeset, this.getOwnerForm())) {
|
|
128
|
+
return;
|
|
80
129
|
}
|
|
81
130
|
|
|
82
131
|
if (this.whileExpr) {
|
|
83
|
-
while
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
// While: while the condition is true, delay a bit and execute the action
|
|
133
|
+
const loop = async () => {
|
|
134
|
+
// Start by waiting
|
|
135
|
+
await wait(this.delay || 0);
|
|
136
|
+
|
|
137
|
+
if (!this.ownerDocument.contains(this)) {
|
|
138
|
+
// We are no longer in the document. Stop working
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!evaluateXPathToBoolean(this.whileExpr, this.nodeset, this.getOwnerForm())) {
|
|
143
|
+
// Done with iterating
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Perform the action once
|
|
89
148
|
this.perform();
|
|
90
|
-
|
|
91
|
-
|
|
149
|
+
|
|
150
|
+
// Go for one more iteration
|
|
151
|
+
await loop();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// After loop is done call actionPerformed to update the model and UI
|
|
155
|
+
await loop();
|
|
92
156
|
this.actionPerformed();
|
|
93
157
|
return;
|
|
94
158
|
}
|
|
95
159
|
|
|
96
160
|
if (this.delay) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
161
|
+
// Delay further execution until the delay is done
|
|
162
|
+
await wait(this.delay);
|
|
163
|
+
if (!this.ownerDocument.contains(this)) {
|
|
164
|
+
// We are no longer in the document. Stop working
|
|
165
|
+
this.actionPerformed();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
102
168
|
}
|
|
169
|
+
|
|
170
|
+
this.perform();
|
|
103
171
|
this.actionPerformed();
|
|
104
172
|
}
|
|
105
173
|
|
|
106
174
|
/**
|
|
107
|
-
*
|
|
175
|
+
* Template method to be implemented by each action that is called by execute() as part of
|
|
176
|
+
* the processing.
|
|
108
177
|
*
|
|
109
178
|
* todo: review - this could probably just be empty or throw error signalling that extender needs to implement it
|
|
110
179
|
*/
|
|
@@ -114,18 +183,23 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
114
183
|
}
|
|
115
184
|
}
|
|
116
185
|
|
|
186
|
+
/**
|
|
187
|
+
* calls the update cycle if action signalled that update is needed.
|
|
188
|
+
*/
|
|
117
189
|
actionPerformed() {
|
|
118
190
|
// console.log('actionPerformed action parentNode ', this.parentNode);
|
|
119
191
|
if (this.needsUpdate) {
|
|
120
192
|
const model = this.getModel();
|
|
121
193
|
model.recalculate();
|
|
122
194
|
model.revalidate();
|
|
123
|
-
model.parentNode.refresh();
|
|
124
|
-
this.
|
|
195
|
+
model.parentNode.refresh(true);
|
|
196
|
+
this.dispatchActionPerformed();
|
|
125
197
|
}
|
|
126
198
|
}
|
|
127
199
|
|
|
128
|
-
|
|
200
|
+
/**
|
|
201
|
+
*/
|
|
202
|
+
dispatchActionPerformed() {
|
|
129
203
|
console.log('action-performed ', this);
|
|
130
204
|
this.dispatchEvent(
|
|
131
205
|
new CustomEvent('action-performed', { composed: true, bubbles: true, detail: {} }),
|
package/src/actions/fx-action.js
CHANGED
|
@@ -43,9 +43,11 @@ export class FxAction extends AbstractAction {
|
|
|
43
43
|
} else {
|
|
44
44
|
Array.from(children).forEach(action => {
|
|
45
45
|
action.detail = this.detail;
|
|
46
|
-
action.perform();
|
|
46
|
+
// action.perform();
|
|
47
|
+
action.execute();
|
|
47
48
|
});
|
|
48
|
-
this.
|
|
49
|
+
this.dispatchActionPerformed();
|
|
50
|
+
// this.needsUpdate = false;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
}
|
package/src/actions/fx-append.js
CHANGED
|
@@ -2,31 +2,32 @@ import { AbstractAction } from './abstract-action.js';
|
|
|
2
2
|
import { Fore } from '../fore.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* `fx-append`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `fx-append` appends an entry to a repeat.
|
|
8
6
|
*
|
|
9
7
|
*
|
|
10
8
|
*
|
|
9
|
+
* @deprecated - will be replaced with `fx-insert`
|
|
10
|
+
* @fires index-changed - fired after new item is appended
|
|
11
11
|
* @customElement
|
|
12
12
|
*/
|
|
13
13
|
// class FxAppend extends FxAction {
|
|
14
14
|
class FxAppend extends AbstractAction {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
static get properties() {
|
|
16
|
+
return {
|
|
17
|
+
ref: {
|
|
18
|
+
type: String,
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* the repeat this action is appending to.
|
|
22
|
+
*/
|
|
23
|
+
repeat: {
|
|
24
|
+
type: String,
|
|
25
|
+
},
|
|
26
|
+
clear: {
|
|
27
|
+
type: String,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
30
31
|
|
|
31
32
|
constructor() {
|
|
32
33
|
super();
|
|
@@ -138,6 +139,8 @@ class FxAppend extends AbstractAction {
|
|
|
138
139
|
* clear all text nodes and attribute values to get a 'clean' template.
|
|
139
140
|
* @param n
|
|
140
141
|
* @private
|
|
142
|
+
*
|
|
143
|
+
*
|
|
141
144
|
*/
|
|
142
145
|
_clear(n) {
|
|
143
146
|
let node = n.firstChild;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FxAction } from './fx-action.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `fx-confirm`
|
|
5
|
+
* Displays a simple confirmation before actually executing the nested actions.
|
|
6
|
+
*
|
|
7
|
+
* @customElement
|
|
8
|
+
* @demo demo/project.html
|
|
9
|
+
*/
|
|
10
|
+
export class FxConfirm extends FxAction {
|
|
11
|
+
connectedCallback() {
|
|
12
|
+
this.message = this.hasAttribute('message') ? this.getAttribute('message') : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
perform() {
|
|
16
|
+
if (window.confirm(this.message)) {
|
|
17
|
+
super.perform();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
window.customElements.define('fx-confirm', FxConfirm);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AbstractAction } from './abstract-action.js';
|
|
2
|
-
import { evaluateXPath } from '../xpath-evaluation.js';
|
|
2
|
+
import { evaluateXPath, resolveId } from '../xpath-evaluation.js';
|
|
3
|
+
import { XPathUtil } from '../xpath-util.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* `fx-dispatch`
|
|
@@ -89,7 +90,14 @@ export class FxDispatch extends AbstractAction {
|
|
|
89
90
|
|
|
90
91
|
// ### when targetid is given dispatch to that if present (throw an error if not) - otherwise dispatch to document
|
|
91
92
|
if (this.targetid) {
|
|
92
|
-
const target = document.getElementById(this.targetid);
|
|
93
|
+
// const target = document.getElementById(this.targetid);
|
|
94
|
+
let target;
|
|
95
|
+
if (XPathUtil.isRepeated(this)) {
|
|
96
|
+
target = resolveId(this.targetid, this.parentNode, null);
|
|
97
|
+
} else {
|
|
98
|
+
target = document.getElementById(this.targetid);
|
|
99
|
+
}
|
|
100
|
+
console.log('target', target);
|
|
93
101
|
if (!target) {
|
|
94
102
|
throw new Error(`targetid ${this.targetid} does not exist in document`);
|
|
95
103
|
}
|