@jinntec/fore 1.5.0 → 1.7.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/dist/fore-dev.js +2 -36
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -30
- package/dist/fore.js.map +1 -1
- package/index.js +13 -0
- package/package.json +9 -5
- package/resources/fore.css +178 -92
- package/src/DependencyNotifyingDomFacade.js +1 -2
- package/src/ForeElementMixin.js +31 -5
- package/src/actions/abstract-action.js +379 -297
- package/src/actions/fx-action.js +0 -1
- package/src/actions/fx-append.js +1 -2
- package/src/actions/fx-confirm.js +12 -0
- package/src/actions/fx-copy.js +0 -1
- package/src/actions/fx-delete.js +31 -9
- package/src/actions/fx-dispatch.js +19 -5
- package/src/actions/fx-hide.js +19 -0
- package/src/actions/fx-insert.js +72 -8
- package/src/actions/fx-load.js +253 -0
- package/src/actions/fx-message.js +22 -7
- package/src/actions/fx-refresh.js +11 -1
- package/src/actions/fx-reload.js +12 -2
- package/src/actions/fx-replace.js +5 -4
- package/src/actions/fx-reset.js +48 -0
- package/src/actions/fx-return.js +0 -1
- package/src/actions/fx-send.js +40 -2
- package/src/actions/fx-setfocus.js +25 -7
- package/src/actions/fx-setvalue.js +32 -4
- package/src/actions/fx-show.js +14 -2
- package/src/actions/fx-toggle.js +0 -1
- package/src/actions/fx-toggleboolean.js +58 -0
- package/src/actions/fx-update.js +9 -0
- package/src/events.js +0 -1
- package/src/fore.js +118 -63
- package/src/functions/common-function.js +28 -0
- package/src/fx-bind.js +9 -7
- package/src/fx-fore.js +153 -55
- package/src/fx-instance.js +55 -17
- package/src/fx-model.js +31 -33
- package/src/fx-submission.js +50 -47
- package/src/getInScopeContext.js +8 -10
- package/src/lab/fore-component.js +90 -0
- package/src/lab/instance-inspector.js +56 -0
- package/src/lab/template.html +16 -0
- package/src/relevance.js +27 -1
- package/src/tools/adi.js +1056 -0
- package/src/tools/fx-action-log.js +662 -0
- package/src/tools/fx-devtools.js +444 -0
- package/src/tools/fx-dom-inspector.js +609 -0
- package/src/tools/fx-json-instance.js +435 -0
- package/src/tools/fx-log-item.js +133 -0
- package/src/tools/fx-log-settings.js +474 -0
- package/src/tools/fx-minimap.js +194 -0
- package/src/tools/helpers.js +132 -0
- package/src/ui/abstract-control.js +41 -3
- package/src/ui/fx-alert.js +0 -1
- package/src/ui/fx-container.js +14 -3
- package/src/ui/fx-control.js +553 -474
- package/src/ui/fx-dialog.js +2 -0
- package/src/ui/fx-dom-inspector.js +1255 -0
- package/src/ui/fx-group.js +3 -4
- package/src/ui/fx-hint.js +2 -4
- package/src/ui/fx-inspector.js +5 -6
- package/src/ui/fx-items.js +55 -14
- package/src/ui/fx-output.js +36 -17
- package/src/ui/fx-repeat-attributes.js +10 -43
- package/src/ui/fx-repeat.js +5 -7
- package/src/ui/fx-switch.js +14 -3
- package/src/ui/fx-trigger.js +13 -1
- package/src/xpath-evaluation.js +109 -26
- package/src/xpath-util.js +55 -1
package/src/ui/fx-control.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import XfAbstractControl from './abstract-control.js';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
evaluateXPath,
|
|
4
|
+
evaluateXPathToString,
|
|
5
|
+
evaluateXPathToFirstNode, resolveId,
|
|
6
6
|
} from '../xpath-evaluation.js';
|
|
7
7
|
import getInScopeContext from '../getInScopeContext.js';
|
|
8
|
-
import {
|
|
8
|
+
import {Fore} from '../fore.js';
|
|
9
9
|
import {ModelItem} from "../modelitem.js";
|
|
10
10
|
import {debounce} from "../events.js";
|
|
11
|
-
import {FxModel} from "../fx-model";
|
|
11
|
+
import {FxModel} from "../fx-model.js";
|
|
12
12
|
|
|
13
13
|
const WIDGETCLASS = 'widget';
|
|
14
14
|
|
|
@@ -34,532 +34,611 @@ function debounce( func, timeout = 300) {
|
|
|
34
34
|
}
|
|
35
35
|
*/
|
|
36
36
|
export default class FxControl extends XfAbstractControl {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
this.inited = false;
|
|
40
|
+
this.attachShadow({mode: 'open'});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static get properties() {
|
|
44
|
+
return {
|
|
45
|
+
...XfAbstractControl.properties,
|
|
46
|
+
initial: {
|
|
47
|
+
type: Boolean
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_getValueFromHtmlDom() {
|
|
53
|
+
if (this.valueProp === 'selectedOptions') {
|
|
54
|
+
// We have multiple! Just return that as space-separated for now
|
|
55
|
+
return [...this.widget.selectedOptions].map(option => option.value).join(' ');
|
|
56
|
+
}
|
|
57
|
+
return this.widget[this.valueProp];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
connectedCallback() {
|
|
61
|
+
this.initial = this.hasAttribute('initial') ? this.getAttribute('initial') : null;
|
|
62
|
+
this.url = this.hasAttribute('url') ? this.getAttribute('url') : null;
|
|
63
|
+
this.loaded = false;
|
|
64
|
+
this.initialNode = null;
|
|
65
|
+
this.debounceDelay = this.hasAttribute('debounce') ? this.getAttribute('debounce') : null;
|
|
66
|
+
|
|
67
|
+
this.updateEvent = this.hasAttribute('update-event')
|
|
68
|
+
? this.getAttribute('update-event')
|
|
69
|
+
: 'blur';
|
|
70
|
+
this.label = this.hasAttribute('label') ? this.getAttribute('label') : null;
|
|
71
|
+
const style = `
|
|
56
72
|
:host{
|
|
57
73
|
display:inline-block;
|
|
58
74
|
}
|
|
59
75
|
`;
|
|
60
76
|
|
|
61
|
-
|
|
77
|
+
this.shadowRoot.innerHTML = `
|
|
62
78
|
<style>
|
|
63
79
|
${style}
|
|
64
80
|
</style>
|
|
65
81
|
${this.renderHTML(this.ref)}
|
|
66
82
|
`;
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
// console.log('widget ', this.widget);
|
|
70
|
-
let listenOn = this.widget // default: usually listening on widget
|
|
84
|
+
this.widget = this.getWidget();
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
this.addEventListener('mousedown', e => {
|
|
87
|
+
// ### prevent mousedown events on all control content that is not the widget or within the widget
|
|
88
|
+
if (!Fore.isWidget(e.target) && !e.target?.classList.contains('fx-hint')) {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
// e.stopImmediatePropagation();
|
|
91
|
+
}
|
|
92
|
+
this.widget.focus();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
const defaultValueProp = this.widget.hasAttribute('multiple') ? 'selectedOptions' : 'value';
|
|
97
|
+
this.valueProp = this.hasAttribute('value-prop') ?
|
|
98
|
+
this.getAttribute('value-prop') :
|
|
99
|
+
defaultValueProp;
|
|
79
100
|
|
|
80
101
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
event.preventDefault();
|
|
91
|
-
this.setValue(this.widget[this.valueProp]);
|
|
102
|
+
// console.log('widget ', this.widget);
|
|
103
|
+
let listenOn = this.widget // default: usually listening on widget
|
|
104
|
+
|
|
105
|
+
if (this.hasAttribute('listen-on')) {
|
|
106
|
+
const q = this.getAttribute('listen-on');
|
|
107
|
+
const target = this.querySelector(q);
|
|
108
|
+
if (target) {
|
|
109
|
+
listenOn = target;
|
|
110
|
+
}
|
|
92
111
|
}
|
|
93
|
-
});
|
|
94
|
-
this.updateEvent = 'blur'; // needs to be registered too
|
|
95
|
-
}
|
|
96
|
-
if (this.debounceDelay) {
|
|
97
|
-
listenOn.addEventListener(
|
|
98
|
-
this.updateEvent,
|
|
99
|
-
debounce(this,() => {
|
|
100
|
-
// console.log('eventlistener ', this.updateEvent);
|
|
101
|
-
// console.info('handling Event:', event.type, listenOn);
|
|
102
|
-
this.setValue(this.widget[this.valueProp]);
|
|
103
|
-
}, this.debounceDelay),
|
|
104
|
-
);
|
|
105
|
-
} else {
|
|
106
|
-
listenOn.addEventListener(this.updateEvent, () => {
|
|
107
|
-
// console.info('handling Event:', event.type, listenOn);
|
|
108
|
-
this.setValue(this.widget[this.valueProp]);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
112
|
|
|
112
|
-
this.addEventListener('return', e => {
|
|
113
|
-
console.log('catched return action on ', this);
|
|
114
|
-
console.log('return detail', e.detail);
|
|
115
|
-
|
|
116
|
-
console.log('return triggered on ', this);
|
|
117
|
-
console.log('this.ref', this.ref);
|
|
118
|
-
console.log('current outer instance', this.getInstance());
|
|
119
|
-
|
|
120
|
-
console.log(
|
|
121
|
-
'???? why ???? current nodeset should point to the node of the outer control',
|
|
122
|
-
e.currentTarget.nodeset,
|
|
123
|
-
);
|
|
124
|
-
console.log(
|
|
125
|
-
'???? why ???? current nodeset should point to the node of the outer control',
|
|
126
|
-
this.nodeset,
|
|
127
|
-
);
|
|
128
|
-
const newNodes = e.detail.nodeset;
|
|
129
|
-
console.log('new nodeset', newNodes);
|
|
130
|
-
console.log('currentTarget', e.currentTarget);
|
|
131
|
-
console.log('target', e.target);
|
|
132
|
-
|
|
133
|
-
e.stopPropagation();
|
|
134
|
-
|
|
135
|
-
this._replaceNode(newNodes);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
this.template = this.querySelector('template');
|
|
139
|
-
this.boundInitialized = false;
|
|
140
|
-
this.static = this.widget.hasAttribute('static')? true:false;
|
|
141
|
-
// console.log('template',this.template);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
_debounce(func, timeout = 300) {
|
|
145
|
-
let timer;
|
|
146
|
-
return (...args) => {
|
|
147
|
-
const context = this;
|
|
148
|
-
clearTimeout(timer);
|
|
149
|
-
timer = setTimeout(() => {
|
|
150
|
-
func.apply(context, args);
|
|
151
|
-
}, timeout);
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* updates the model with a new value by executing it's `<fx-setvalue>` action.
|
|
157
|
-
*
|
|
158
|
-
* In case the `as='node'` is given the bound node is replaced with the widgets' value with is
|
|
159
|
-
* expected to be a node again.
|
|
160
|
-
*
|
|
161
|
-
* @param val the new value to be set
|
|
162
|
-
*/
|
|
163
|
-
setValue(val) {
|
|
164
|
-
const modelitem = this.getModelItem();
|
|
165
|
-
|
|
166
|
-
this.classList.add('visited');
|
|
167
|
-
|
|
168
|
-
if (modelitem?.readonly){
|
|
169
|
-
console.warn('attempt to change readonly node', modelitem);
|
|
170
|
-
return; // do nothing when modelItem is readonly
|
|
171
|
-
}
|
|
172
113
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
114
|
+
this.addEventListener('keyup', () => {
|
|
115
|
+
FxModel.dataChanged = true;
|
|
116
|
+
/*
|
|
117
|
+
if (!this.classList.contains('visited')) {
|
|
118
|
+
this.classList.add('visited');
|
|
119
|
+
}
|
|
120
|
+
*/
|
|
121
|
+
});
|
|
122
|
+
// ### convenience marker event
|
|
123
|
+
if (this.updateEvent === 'enter') {
|
|
124
|
+
this.widget.addEventListener('keyup', event => {
|
|
125
|
+
if (event.keyCode === 13) {
|
|
126
|
+
// console.info('handling Event:', event.type, listenOn);
|
|
127
|
+
// Cancel the default action, if needed
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
this.setValue(this._getValueFromHtmlDom());
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
this.updateEvent = 'blur'; // needs to be registered too
|
|
133
|
+
}
|
|
134
|
+
if (this.debounceDelay) {
|
|
135
|
+
listenOn.addEventListener(
|
|
136
|
+
this.updateEvent,
|
|
137
|
+
debounce(this, () => {
|
|
138
|
+
// console.log('eventlistener ', this.updateEvent);
|
|
139
|
+
// console.info('handling Event:', event.type, listenOn);
|
|
140
|
+
this.setValue(this._getValueFromHtmlDom());
|
|
141
|
+
}, this.debounceDelay),
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
listenOn.addEventListener(this.updateEvent, (event) => {
|
|
145
|
+
this.setValue(this._getValueFromHtmlDom());
|
|
146
|
+
});
|
|
147
|
+
listenOn.addEventListener('blur', (event) => {
|
|
148
|
+
this.setValue(this._getValueFromHtmlDom());
|
|
149
|
+
},{once:true});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.addEventListener('return', e => {
|
|
153
|
+
// console.log('catched return action on ', this);
|
|
154
|
+
// console.log('return detail', e.detail);
|
|
155
|
+
|
|
156
|
+
// console.log('return triggered on ', this);
|
|
157
|
+
// console.log('this.ref', this.ref);
|
|
158
|
+
// console.log('current outer instance', this.getInstance());
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
console.log(
|
|
162
|
+
'???? why ???? current nodeset should point to the node of the outer control',
|
|
163
|
+
e.currentTarget.nodeset,
|
|
164
|
+
);
|
|
165
|
+
console.log(
|
|
166
|
+
'???? why ???? current nodeset should point to the node of the outer control',
|
|
167
|
+
this.nodeset,
|
|
168
|
+
);
|
|
169
|
+
*/
|
|
170
|
+
const newNodes = e.detail.nodeset;
|
|
171
|
+
// console.log('new nodeset', newNodes);
|
|
172
|
+
// console.log('currentTarget', e.currentTarget);
|
|
173
|
+
// console.log('target', e.target);
|
|
174
|
+
|
|
175
|
+
e.stopPropagation();
|
|
176
|
+
|
|
177
|
+
this._replaceNode(newNodes);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.widget.addEventListener('focus', () => {
|
|
181
|
+
/*
|
|
182
|
+
if (!this.classList.contains('visited')) {
|
|
183
|
+
this.classList.add('visited');
|
|
184
|
+
}
|
|
185
|
+
*/
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
this.template = this.querySelector('template');
|
|
189
|
+
this.boundInitialized = false;
|
|
190
|
+
this.static = !!this.widget.hasAttribute('static');
|
|
191
|
+
// console.log('template',this.template);
|
|
183
192
|
}
|
|
184
|
-
const setval = this.shadowRoot.getElementById('setvalue');
|
|
185
|
-
setval.setValue(modelitem, val);
|
|
186
193
|
|
|
187
|
-
|
|
188
|
-
|
|
194
|
+
_debounce(func, timeout = 300) {
|
|
195
|
+
let timer;
|
|
196
|
+
return (...args) => {
|
|
197
|
+
const context = this;
|
|
198
|
+
clearTimeout(timer);
|
|
199
|
+
timer = setTimeout(() => {
|
|
200
|
+
func.apply(context, args);
|
|
201
|
+
}, timeout);
|
|
202
|
+
};
|
|
189
203
|
}
|
|
190
204
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
205
|
+
/**
|
|
206
|
+
* updates the model with a new value by executing it's `<fx-setvalue>` action.
|
|
207
|
+
*
|
|
208
|
+
* In case the `as='node'` is given the bound node is replaced with the widgets' value with is
|
|
209
|
+
* expected to be a node again.
|
|
210
|
+
*
|
|
211
|
+
* @param val the new value to be set
|
|
212
|
+
*/
|
|
213
|
+
setValue(val) {
|
|
214
|
+
const modelitem = this.getModelItem();
|
|
215
|
+
|
|
216
|
+
if(this.getAttribute('class')){
|
|
217
|
+
this.classList.add('visited');
|
|
218
|
+
}else{
|
|
219
|
+
this.setAttribute('class','visited');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (modelitem?.readonly) {
|
|
223
|
+
console.warn('attempt to change readonly node', modelitem);
|
|
224
|
+
return; // do nothing when modelItem is readonly
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (this.getAttribute('as') === 'node') {
|
|
228
|
+
const replace = this.shadowRoot.getElementById('replace');
|
|
229
|
+
const widgetValue = this.getWidget()[this.valueProp];
|
|
230
|
+
replace.replace(this.nodeset, widgetValue);
|
|
231
|
+
if (modelitem && widgetValue && widgetValue !== modelitem.value) {
|
|
232
|
+
modelitem.value = widgetValue;
|
|
233
|
+
FxModel.dataChanged = true;
|
|
234
|
+
replace.actionPerformed();
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const setval = this.shadowRoot.getElementById('setvalue');
|
|
239
|
+
setval.setValue(modelitem, val);
|
|
240
|
+
|
|
241
|
+
if (this.modelItem instanceof ModelItem && !this.modelItem?.boundControls.includes(this)) {
|
|
242
|
+
this.modelItem.boundControls.push(this);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setval.actionPerformed();
|
|
246
|
+
// this.visited = true;
|
|
247
|
+
}
|
|
194
248
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
249
|
+
_replaceNode(node) {
|
|
250
|
+
// Note: clone the node while replacing to prevent the instances to leak through
|
|
251
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
252
|
+
this.modelItem.node.nodeValue = node.nodeValue;
|
|
253
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
254
|
+
this.modelItem.node.replaceWith(node.cloneNode(true));
|
|
255
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
256
|
+
this.modelItem.node.nodeValue = node.textContent;
|
|
257
|
+
} else (
|
|
258
|
+
Fore.dispatch(this, "warn", {'message': 'trying to replace a node that is neither an Attribute, Elemment or Text node'})
|
|
259
|
+
)
|
|
260
|
+
this.getOwnerForm().refresh();
|
|
261
|
+
}
|
|
200
262
|
|
|
201
|
-
|
|
202
|
-
|
|
263
|
+
renderHTML(ref) {
|
|
264
|
+
return `
|
|
203
265
|
${this.label ? `${this.label}` : ''}
|
|
204
266
|
<slot></slot>
|
|
205
267
|
${
|
|
206
|
-
|
|
268
|
+
this.hasAttribute('as') && this.getAttribute('as') === 'node'
|
|
207
269
|
? `<fx-replace id="replace" ref=".">`
|
|
208
270
|
: `<fx-setvalue id="setvalue" ref="${ref}"></fx-setvalue>`
|
|
209
|
-
|
|
271
|
+
}
|
|
210
272
|
|
|
211
273
|
`;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* The widget is the actual control being used in the UI e.g. a native input control or any
|
|
216
|
-
* other component that presents a control that can be interacted with.
|
|
217
|
-
*
|
|
218
|
-
* This function returns the widget by querying the children of this control for an element
|
|
219
|
-
* with `class="widget"`. If that cannot be found it searches for an native `input` of any type.
|
|
220
|
-
* If either cannot be found a `<input type="text">` is created.
|
|
221
|
-
*
|
|
222
|
-
* @returns {HTMLElement|*}
|
|
223
|
-
*/
|
|
224
|
-
getWidget() {
|
|
225
|
-
if (this.widget) return this.widget;
|
|
226
|
-
let widget = this.querySelector(`.${WIDGETCLASS}`);
|
|
227
|
-
if (!widget) {
|
|
228
|
-
widget = this.querySelector('input');
|
|
229
274
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
widget.checked = true;
|
|
259
|
-
} else {
|
|
260
|
-
widget.checked = false;
|
|
261
|
-
}
|
|
262
|
-
return;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The widget is the actual control being used in the UI e.g. a native input control or any
|
|
278
|
+
* other component that presents a control that can be interacted with.
|
|
279
|
+
*
|
|
280
|
+
* This function returns the widget by querying the children of this control for an element
|
|
281
|
+
* with `class="widget"`. If that cannot be found it searches for an native `input` of any type.
|
|
282
|
+
* If either cannot be found a `<input type="text">` is created.
|
|
283
|
+
*
|
|
284
|
+
* @returns {HTMLElement|*}
|
|
285
|
+
*/
|
|
286
|
+
getWidget() {
|
|
287
|
+
if (this.widget) return this.widget;
|
|
288
|
+
let widget = this.querySelector(`.${WIDGETCLASS}`);
|
|
289
|
+
if (!widget) {
|
|
290
|
+
widget = this.querySelector('input');
|
|
291
|
+
if(widget && !widget.classList.contains('widget')){
|
|
292
|
+
widget.classList.add('widget');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (!widget) {
|
|
296
|
+
const input = document.createElement('input');
|
|
297
|
+
input.classList.add(WIDGETCLASS);
|
|
298
|
+
input.setAttribute('type', 'text');
|
|
299
|
+
this.appendChild(input);
|
|
300
|
+
return input;
|
|
301
|
+
}
|
|
302
|
+
return widget;
|
|
263
303
|
}
|
|
264
304
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (widget
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
305
|
+
/**
|
|
306
|
+
* updates the widget from the modelItem value. During refresh the a control
|
|
307
|
+
* evaluates it's binding expression to determine the bound node. The bound node corresponds
|
|
308
|
+
* to a modelItem which acts a the state object of a node. The modelItem determines the value
|
|
309
|
+
* and the state of the node and set the `value` property of this class.
|
|
310
|
+
*
|
|
311
|
+
* @returns {Promise<void>}
|
|
312
|
+
*/
|
|
313
|
+
async updateWidgetValue() {
|
|
314
|
+
// this._getValueFromHtmlDom() = this.value;
|
|
315
|
+
|
|
316
|
+
let {widget} = this;
|
|
317
|
+
if (!widget) {
|
|
318
|
+
widget = this;
|
|
319
|
+
}
|
|
320
|
+
// ### value is bound to checkbox
|
|
321
|
+
if (this.valueProp === 'checked') {
|
|
322
|
+
if (this.value === 'true') {
|
|
323
|
+
widget.checked = true;
|
|
324
|
+
} else {
|
|
325
|
+
widget.checked = false;
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.valueProp === 'selectedOptions') {
|
|
331
|
+
const valueSet = new Set(this.value.split(' '));
|
|
332
|
+
for (const option of [...this.widget.querySelectorAll('option')]) {
|
|
333
|
+
if (valueSet.has(option.value)) {
|
|
334
|
+
option.selected = true;
|
|
335
|
+
} else {
|
|
336
|
+
option.selected = false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (this.hasAttribute('as')) {
|
|
343
|
+
const as = this.getAttribute('as');
|
|
344
|
+
|
|
345
|
+
// ### when there's an `as=text` attribute serialize nodeset to prettified string
|
|
346
|
+
if (as === 'text') {
|
|
347
|
+
const serializer = new XMLSerializer();
|
|
348
|
+
const pretty = Fore.prettifyXml(serializer.serializeToString(this.nodeset));
|
|
349
|
+
widget.value = pretty;
|
|
350
|
+
}
|
|
351
|
+
if (as === 'node' && this.nodeset !== widget.value) {
|
|
352
|
+
// const oldVal = this.nodeset.innerHTML;
|
|
353
|
+
const oldVal = this.nodeset;
|
|
354
|
+
if (widget.value) {
|
|
355
|
+
if (oldVal !== this.widget.value) {
|
|
356
|
+
// console.log('changed');
|
|
357
|
+
widget.value = this.nodeset.cloneNode(true);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
widget.value = this.nodeset.cloneNode(true);
|
|
363
|
+
// todo: should be more like below but that can cause infinite loop when controll trigger update event due to calling a setter for property
|
|
364
|
+
// widget[this.valueProp] = this.nodeset.cloneNode(true);
|
|
365
|
+
// console.log('passed value to widget', widget.value);
|
|
366
|
+
}
|
|
367
|
+
|
|
281
368
|
return;
|
|
282
|
-
}
|
|
283
369
|
}
|
|
284
370
|
|
|
285
|
-
widget
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
371
|
+
// ### when there's a url Fore is used as widget and will be loaded from external file
|
|
372
|
+
if (this.url && !this.loaded) {
|
|
373
|
+
// ### evaluate initial data if necessary
|
|
374
|
+
|
|
375
|
+
if (this.initial) {
|
|
376
|
+
this.initialNode = evaluateXPathToFirstNode(this.initial, this.nodeset, this);
|
|
377
|
+
// console.log('initialNodes', this.initialNode);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ### load the markup from Url
|
|
381
|
+
await this._loadForeFromUrl();
|
|
382
|
+
this.loaded = true;
|
|
383
|
+
|
|
384
|
+
// ### replace default instance of embedded Fore with initial nodes
|
|
385
|
+
// const innerInstance = this.querySelector('fx-instance');
|
|
386
|
+
// console.log('innerInstance',innerInstance);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
290
389
|
|
|
291
|
-
|
|
390
|
+
/*
|
|
391
|
+
if(this.url && !this.loaded){
|
|
392
|
+
this._loadForeFromUrl();
|
|
393
|
+
this.loaded=true;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
*/
|
|
397
|
+
if (widget.value !== this.value) {
|
|
398
|
+
widget.value = this.value;
|
|
399
|
+
}
|
|
292
400
|
}
|
|
293
401
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
402
|
+
/**
|
|
403
|
+
* loads an external Fore from an HTML file given by `url` attribute.
|
|
404
|
+
*
|
|
405
|
+
* Will look for the `<fx-fore>` element within the returned HTML file and return that element.
|
|
406
|
+
*
|
|
407
|
+
* If that cannot be found an error is dispatched.
|
|
408
|
+
*
|
|
409
|
+
* todo: dispatch link error
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
async _loadForeFromUrl() {
|
|
413
|
+
console.info(
|
|
414
|
+
`%cFore is processing URL ${this.url}`,
|
|
415
|
+
"background:#64b5f6; color:white; padding:0.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;",
|
|
416
|
+
);
|
|
417
|
+
try {
|
|
418
|
+
const response = await fetch(this.url, {
|
|
419
|
+
method: 'GET',
|
|
420
|
+
/*
|
|
421
|
+
mode: 'cors',
|
|
422
|
+
credentials: 'include',
|
|
423
|
+
*/
|
|
424
|
+
headers: {
|
|
425
|
+
'Content-Type': 'text/html',
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
430
|
+
// console.log('********** responseContentType *********', responseContentType);
|
|
431
|
+
let data;
|
|
432
|
+
if (responseContentType.startsWith('text/html')) {
|
|
433
|
+
data = await response.text().then(result =>
|
|
434
|
+
// console.log('xml ********', result);
|
|
435
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
436
|
+
);
|
|
437
|
+
} else {
|
|
438
|
+
data = 'done';
|
|
439
|
+
}
|
|
440
|
+
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
441
|
+
const theFore = data.querySelector('fx-fore');
|
|
442
|
+
const imported = document.importNode(theFore, true);
|
|
443
|
+
|
|
444
|
+
imported.classList.add('widget'); // is the new widget
|
|
445
|
+
imported.addEventListener(
|
|
446
|
+
'model-construct-done',
|
|
447
|
+
e => {
|
|
448
|
+
const defaultInst = imported.querySelector('fx-instance');
|
|
449
|
+
if (this.initialNode) {
|
|
450
|
+
const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
|
|
451
|
+
// Note: Clone the input to prevent the inner fore from editing the outer node
|
|
452
|
+
doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
|
|
453
|
+
defaultInst.setInstanceData(doc);
|
|
454
|
+
}
|
|
455
|
+
imported.getModel().updateModel();
|
|
456
|
+
imported.refresh();
|
|
457
|
+
},
|
|
458
|
+
{once: true},
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
const dummy = this.querySelector('input');
|
|
462
|
+
if (this.hasAttribute('shadow')) {
|
|
463
|
+
dummy.parentNode.removeChild(dummy);
|
|
464
|
+
this.shadowRoot.appendChild(imported);
|
|
465
|
+
} else if (!this.loaded) {
|
|
466
|
+
dummy.replaceWith(imported);
|
|
467
|
+
}
|
|
297
468
|
|
|
298
|
-
if (this.initial) {
|
|
299
|
-
this.initialNode = evaluateXPathToFirstNode(this.initial, this.nodeset, this);
|
|
300
|
-
console.log('initialNodes', this.initialNode);
|
|
301
|
-
}
|
|
302
469
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
470
|
+
if (!theFore) {
|
|
471
|
+
this.dispatchEvent(
|
|
472
|
+
new CustomEvent('error', {
|
|
473
|
+
detail: {
|
|
474
|
+
message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`,
|
|
475
|
+
},
|
|
476
|
+
}),
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
this.dispatchEvent(new CustomEvent('loaded', {detail: {fore: theFore}}));
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// console.log('error', error);
|
|
482
|
+
this.getOwnerForm().dispatchEvent(
|
|
483
|
+
new CustomEvent('error', {detail: {message: `${this.url} not found`}}),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
306
487
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// console.log('innerInstance',innerInstance);
|
|
310
|
-
return;
|
|
488
|
+
getTemplate() {
|
|
489
|
+
return this.querySelector('template');
|
|
311
490
|
}
|
|
312
491
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
492
|
+
async refresh(force) {
|
|
493
|
+
// console.log('fx-control refresh', this);
|
|
494
|
+
super.refresh(force);
|
|
495
|
+
// console.log('refresh template', this.template);
|
|
496
|
+
// const {widget} = this;
|
|
497
|
+
|
|
498
|
+
// ### if we find a ref on control we have a 'select' control of some kind
|
|
499
|
+
const widget = this.getWidget();
|
|
500
|
+
this._handleBoundWidget(widget);
|
|
501
|
+
this._handleDataAttributeBinding();
|
|
502
|
+
Fore.refreshChildren(this, force);
|
|
318
503
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* handle non-Fore elements like 'select' and 'datalist' which have a 'data-ref' attribute
|
|
508
|
+
* @private
|
|
509
|
+
*/
|
|
510
|
+
_handleDataAttributeBinding() {
|
|
511
|
+
const dataRefd = this.querySelector('[data-ref]');
|
|
512
|
+
if (dataRefd) {
|
|
513
|
+
this.boundList = dataRefd;
|
|
514
|
+
const ref = dataRefd.getAttribute('data-ref');
|
|
515
|
+
this._handleBoundWidget(dataRefd);
|
|
516
|
+
}
|
|
322
517
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* If the widget itself has a `ref` it binds to another nodeset to provide some
|
|
521
|
+
* dynamic items to be created from a template usually. Examples are dynamic select option lists
|
|
522
|
+
* or a set of checkboxes.
|
|
523
|
+
*
|
|
524
|
+
* @param widget the widget to handle
|
|
525
|
+
* @private
|
|
526
|
+
*/
|
|
527
|
+
_handleBoundWidget(widget) {
|
|
528
|
+
if (this.boundInitialized && this.static) return;
|
|
529
|
+
|
|
530
|
+
const ref = widget.hasAttribute('ref') ? widget.getAttribute('ref') : widget.getAttribute('data-ref');
|
|
531
|
+
// if (widget && widget.hasAttribute('ref')) {
|
|
532
|
+
if (widget && ref) {
|
|
533
|
+
// ### eval nodeset for list control
|
|
534
|
+
const inscope = getInScopeContext(this, ref);
|
|
535
|
+
// const nodeset = evaluateXPathToNodes(ref, inscope, this);
|
|
536
|
+
const nodeset = evaluateXPath(ref, inscope, this);
|
|
537
|
+
|
|
538
|
+
// ### bail out when nodeset is array and empty
|
|
539
|
+
if (Array.isArray(nodeset) && nodeset.length === 0) return;
|
|
540
|
+
|
|
541
|
+
// ### clear items
|
|
542
|
+
const {children} = widget;
|
|
543
|
+
Array.from(children).forEach(child => {
|
|
544
|
+
if (child.nodeName.toLowerCase() !== 'template') {
|
|
545
|
+
child.parentNode.removeChild(child);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// ### build the items
|
|
550
|
+
const {template} = this;
|
|
551
|
+
if (template) {
|
|
552
|
+
// ### handle 'selection' open and insert an empty option in that case
|
|
553
|
+
if (this.widget.nodeName === "SELECT" &&
|
|
554
|
+
this.widget.hasAttribute('selection') &&
|
|
555
|
+
this.widget.getAttribute('selection') === 'open') {
|
|
556
|
+
const firstTemplateChild = this.template.firstElementChild;
|
|
557
|
+
// todo: create the element which is used in the template instead of 'option'
|
|
558
|
+
const option = document.createElement('option');
|
|
559
|
+
this.widget.insertBefore(option, firstTemplateChild);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (nodeset.length) {
|
|
563
|
+
// console.log('nodeset', nodeset);
|
|
564
|
+
const fragment = document.createDocumentFragment();
|
|
565
|
+
// console.time('offscreen');
|
|
566
|
+
Array.from(nodeset).forEach(node => {
|
|
567
|
+
// console.log('#### node', node);
|
|
568
|
+
// ### initialize new entry
|
|
569
|
+
const newEntry = this.createEntry();
|
|
570
|
+
fragment.appendChild(newEntry);
|
|
571
|
+
// ### set value
|
|
572
|
+
this.updateEntry(newEntry, node);
|
|
573
|
+
});
|
|
574
|
+
this.template.parentNode.appendChild(fragment);
|
|
575
|
+
// console.timeEnd('offscreen');
|
|
576
|
+
} else {
|
|
577
|
+
const newEntry = this.createEntry();
|
|
578
|
+
this.template.parentNode.appendChild(newEntry);
|
|
579
|
+
this.updateEntry(newEntry, nodeset);
|
|
580
|
+
}
|
|
581
|
+
this.boundInitialized = true;
|
|
381
582
|
}
|
|
382
|
-
// console.log('new data', defaultInst.getInstanceData());
|
|
383
|
-
// theFore.getModel().modelConstruct();
|
|
384
|
-
imported.getModel().updateModel();
|
|
385
|
-
imported.refresh();
|
|
386
|
-
},
|
|
387
|
-
{ once: true },
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
const dummy = this.querySelector('input');
|
|
391
|
-
if (this.hasAttribute('shadow')) {
|
|
392
|
-
dummy.parentNode.removeChild(dummy);
|
|
393
|
-
this.shadowRoot.appendChild(imported);
|
|
394
|
-
} else {
|
|
395
|
-
console.log(this, 'replacing widget with',theFore);
|
|
396
|
-
dummy.replaceWith(imported);
|
|
397
|
-
// this.appendChild(imported);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (!theFore) {
|
|
402
|
-
this.dispatchEvent(
|
|
403
|
-
new CustomEvent('error', {
|
|
404
|
-
detail: {
|
|
405
|
-
message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`,
|
|
406
|
-
},
|
|
407
|
-
}),
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
console.log('loaded');
|
|
411
|
-
this.dispatchEvent(new CustomEvent('loaded', { detail: { fore: theFore } }));
|
|
412
|
-
} catch (error) {
|
|
413
|
-
console.log('error', error);
|
|
414
|
-
this.getOwnerForm().dispatchEvent(
|
|
415
|
-
new CustomEvent('error', { detail: { message: `${this.url} not found` } }),
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
getTemplate() {
|
|
421
|
-
return this.querySelector('template');
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
async refresh(force) {
|
|
425
|
-
// console.log('fx-control refresh', this);
|
|
426
|
-
super.refresh(force);
|
|
427
|
-
// console.log('refresh template', this.template);
|
|
428
|
-
// const {widget} = this;
|
|
429
|
-
|
|
430
|
-
// ### if we find a ref on control we have a 'select' control of some kind
|
|
431
|
-
const widget = this.getWidget();
|
|
432
|
-
this._handleBoundWidget(widget);
|
|
433
|
-
Fore.refreshChildren(this, force);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* If the widget itself has a `ref` it binds to another nodeset to provide some
|
|
438
|
-
* dynamic items to be created from a template usually. Examples are dynamic select option lists
|
|
439
|
-
* or a set of checkboxes.
|
|
440
|
-
*
|
|
441
|
-
* @param widget the widget to handle
|
|
442
|
-
* @private
|
|
443
|
-
*/
|
|
444
|
-
_handleBoundWidget(widget) {
|
|
445
|
-
if(this.boundInitialized && this.static) return;
|
|
446
|
-
|
|
447
|
-
if (widget && widget.hasAttribute('ref')) {
|
|
448
|
-
// ### eval nodeset for list control
|
|
449
|
-
const ref = widget.getAttribute('ref');
|
|
450
|
-
/*
|
|
451
|
-
actually a ref on a select or similar component should point to a different instance
|
|
452
|
-
with an absolute expr e.g. 'instance('theId')/...'
|
|
453
|
-
|
|
454
|
-
todo: even bail out if ref is not absolute?
|
|
455
|
-
*/
|
|
456
|
-
|
|
457
|
-
const inscope = getInScopeContext(this, ref);
|
|
458
|
-
// const nodeset = evaluateXPathToNodes(ref, inscope, this);
|
|
459
|
-
const nodeset = evaluateXPath(ref, inscope, this);
|
|
460
|
-
|
|
461
|
-
// ### bail out when nodeset is array and empty
|
|
462
|
-
if (Array.isArray(nodeset) && nodeset.length === 0) return;
|
|
463
|
-
|
|
464
|
-
// ### clear items
|
|
465
|
-
const { children } = widget;
|
|
466
|
-
Array.from(children).forEach(child => {
|
|
467
|
-
if (child.nodeName.toLowerCase() !== 'template') {
|
|
468
|
-
child.parentNode.removeChild(child);
|
|
469
583
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
updateEntry(newEntry, node) {
|
|
587
|
+
// ### >>> todo: needs rework this code is heavily assuming a select control with 'value' attribute - not generic at all yet.
|
|
588
|
+
|
|
589
|
+
// if (this.widget.nodeName !== 'SELECT') return;
|
|
590
|
+
const valueAttribute = this._getValueAttribute(newEntry);
|
|
591
|
+
if (!valueAttribute) {
|
|
592
|
+
// Fore.dispatch(this,'warn',{message:'no value attribute specified for template entry.'});
|
|
593
|
+
return;
|
|
480
594
|
}
|
|
481
595
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const newEntry = this.createEntry();
|
|
490
|
-
this.template.parentNode.appendChild(newEntry);
|
|
491
|
-
// ### initialize new entry
|
|
492
|
-
// ### set value
|
|
493
|
-
this.updateEntry(newEntry, node);
|
|
494
|
-
});
|
|
495
|
-
*/
|
|
496
|
-
// this should actually perform better than the above but does not seem to make a measurable difference.
|
|
497
|
-
Array.from(nodeset).forEach(node => {
|
|
498
|
-
// console.log('#### node', node);
|
|
499
|
-
const newEntry = this.createEntry();
|
|
500
|
-
fragment.appendChild(newEntry);
|
|
501
|
-
|
|
502
|
-
// ### initialize new entry
|
|
503
|
-
// ### set value
|
|
504
|
-
this.updateEntry(newEntry, node);
|
|
505
|
-
});
|
|
506
|
-
this.template.parentNode.appendChild(fragment);
|
|
507
|
-
// console.timeEnd('offscreen');
|
|
508
|
-
} else {
|
|
509
|
-
const newEntry = this.createEntry();
|
|
510
|
-
this.template.parentNode.appendChild(newEntry);
|
|
511
|
-
this.updateEntry(newEntry, nodeset);
|
|
596
|
+
const valueExpr = valueAttribute.value;
|
|
597
|
+
const cutted = valueExpr.substring(1, valueExpr.length - 1);
|
|
598
|
+
const evaluated = evaluateXPathToString(cutted, node, newEntry);
|
|
599
|
+
valueAttribute.value = evaluated;
|
|
600
|
+
|
|
601
|
+
if (this.value === evaluated) {
|
|
602
|
+
newEntry.setAttribute('selected', 'selected');
|
|
512
603
|
}
|
|
513
|
-
|
|
514
|
-
|
|
604
|
+
|
|
605
|
+
// ### set label
|
|
606
|
+
const optionLabel = newEntry.textContent;
|
|
607
|
+
this.evalLabel(optionLabel, node, newEntry);
|
|
608
|
+
// ### <<< needs rework
|
|
515
609
|
}
|
|
516
|
-
}
|
|
517
610
|
|
|
518
|
-
updateEntry(newEntry, node) {
|
|
519
|
-
// ### >>> todo: needs rework this code is heavily assuming a select control with 'value' attribute - not generic at all yet.
|
|
520
611
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
612
|
+
evalLabel(optionLabel, node, newEntry) {
|
|
613
|
+
const labelExpr = optionLabel.substring(1, optionLabel.length - 1);
|
|
614
|
+
if (!labelExpr) return;
|
|
615
|
+
|
|
616
|
+
const label = evaluateXPathToString(labelExpr, node, this);
|
|
617
|
+
newEntry.textContent = label;
|
|
618
|
+
}
|
|
527
619
|
|
|
528
|
-
|
|
529
|
-
|
|
620
|
+
createEntry() {
|
|
621
|
+
return this.template.content.firstElementChild.cloneNode(true);
|
|
622
|
+
// const content = this.template.content.firstElementChild.cloneNode(true);
|
|
623
|
+
// return content;
|
|
624
|
+
// const newEntry = document.importNode(content, true);
|
|
625
|
+
// this.template.parentNode.appendChild(newEntry);
|
|
626
|
+
// return newEntry;
|
|
530
627
|
}
|
|
531
628
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
// return content;
|
|
545
|
-
// const newEntry = document.importNode(content, true);
|
|
546
|
-
// this.template.parentNode.appendChild(newEntry);
|
|
547
|
-
// return newEntry;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// eslint-disable-next-line class-methods-use-this
|
|
551
|
-
_getValueAttribute(element) {
|
|
552
|
-
let result;
|
|
553
|
-
Array.from(element.attributes).forEach(attribute => {
|
|
554
|
-
const attrVal = attribute.value;
|
|
555
|
-
if (attrVal.indexOf('{') !== -1) {
|
|
556
|
-
// console.log('avt found ', attribute);
|
|
557
|
-
result = attribute;
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
return result;
|
|
561
|
-
}
|
|
629
|
+
// eslint-disable-next-line class-methods-use-this
|
|
630
|
+
_getValueAttribute(element) {
|
|
631
|
+
let result;
|
|
632
|
+
Array.from(element.attributes).forEach(attribute => {
|
|
633
|
+
const attrVal = attribute.value;
|
|
634
|
+
if (attrVal.indexOf('{') !== -1) {
|
|
635
|
+
// console.log('avt found ', attribute);
|
|
636
|
+
result = attribute;
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
562
641
|
}
|
|
563
642
|
if (!customElements.get('fx-control')) {
|
|
564
|
-
|
|
643
|
+
window.customElements.define('fx-control', FxControl);
|
|
565
644
|
}
|