@jinntec/fore 2.2.0 → 2.3.0

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