@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,194 +1,203 @@
1
1
  class FxMinimap extends HTMLElement {
2
- constructor() {
3
- super();
4
- this.canvas = document.createElement('canvas');
5
- this.options = this.getAttribute('options') ? JSON.parse(this.getAttribute('options')) : {};
6
- this.attachShadow({mode: 'open'});
7
- this.shadowRoot.appendChild(this.canvas);
8
- this.WIN = window;
9
- this.DOC = this.WIN.document;
10
- this.DOC_EL = this.DOC.documentElement;
11
- this.BODY = this.DOC.querySelector('body');
12
-
13
- }
14
-
15
- connectedCallback() {
16
- this.ctx = this.canvas.getContext('2d');
17
- this.black = pc => `rgba(0,0,0,${pc / 100})`;
18
- this.viewport = this.querySelector(this.getAttribute('selector'));
19
- this.settings = Object.assign(
20
- {
21
- viewport: this.viewport,
22
- styles: {
23
- 'header,footer,section,article': this.black(8),
24
- 'h1,a': this.black(10),
25
- 'h2,h3,h4': this.black(8),
26
- },
27
- back: this.black(2),
28
- view: this.black(5),
29
- drag: this.black(10),
30
- interval: null,
31
- },
32
- this.options
33
- );
34
-
35
- const _listener = (el, method, types, fn) => types.split(/\s+/).forEach(type => el[method](type, fn));
36
- this.on = (el, types, fn) => _listener(el, 'addEventListener', types, fn);
37
- this.off = (el, types, fn) => _listener(el, 'removeEventListener', types, fn);
38
-
39
- this.Rect = (x, y, w, h) => {
40
- return {x, y, w, h};
41
- };
42
-
43
- this.rect_rel_to = (rect, pos = {x: 0, y: 0}) => {
44
- return this.Rect(rect.x - pos.x, rect.y - pos.y, rect.w, rect.h);
45
- };
46
-
47
- this.rect_of_doc = () => {
48
- return this.Rect(0, 0, document.documentElement.scrollWidth, document.documentElement.scrollHeight);
49
- };
50
-
51
- this.rect_of_win = () => {
52
- return this.Rect(window.pageXOffset, window.pageYOffset, document.documentElement.clientWidth, document.documentElement.clientHeight);
53
- };
54
-
55
- this.el_get_offset = el => {
56
- const br = el.getBoundingClientRect();
57
- return {x: br.left + window.pageXOffset, y: br.top + window.pageYOffset};
58
- };
59
-
60
- this.rect_of_el = el => {
61
- const {x, y} = this.el_get_offset(el);
62
- return this.Rect(x, y, el.offsetWidth, el.offsetHeight);
63
- };
64
-
65
- this.rect_of_viewport = el => {
66
- const {x, y} = this.el_get_offset(el);
67
- return this.Rect(x + el.clientLeft, y + el.clientTop, el.clientWidth, el.clientHeight);
68
- };
69
-
70
- this.rect_of_content = el => {
71
- const {x, y} = this.el_get_offset(el);
72
- return this.Rect(x + el.clientLeft - el.scrollLeft, y + el.clientTop - el.scrollTop, el.scrollWidth, el.scrollHeight);
73
- };
74
-
75
- this.calc_scale = (() => {
76
- const width = this.canvas.clientWidth;
77
- const height = this.canvas.clientHeight;
78
- return (w, h) => Math.min(width / w, height / h);
79
- })();
80
-
81
- this.resize_canvas = (w, h) => {
82
- this.canvas.width = w;
83
- this.canvas.height = h;
84
- this.canvas.style.width = `${w}px`;
85
- this.canvas.style.height = `${h}px`;
86
- };
87
-
88
- this.viewport = this.settings.viewport;
89
- this.find = sel => Array.from((this.viewport || document).querySelectorAll(sel));
90
-
91
- this.drag = false;
92
- this.root_rect = null;
93
- this.view_rect = null;
94
- this.scale = null;
95
- this.drag_rx = null
96
- this. drag_ry = null;
97
- }
98
-
99
- draw_rect(rect, col){
100
- if (col) {
101
- this.ctx.beginPath();
102
- this.ctx.rect(rect.x, rect.y, rect.w, rect.h);
103
- this.ctx.fillStyle = col;
104
- this.ctx.fill();
105
- }
2
+ constructor() {
3
+ super();
4
+ this.canvas = document.createElement('canvas');
5
+ this.options = this.getAttribute('options') ? JSON.parse(this.getAttribute('options')) : {};
6
+ this.attachShadow({ mode: 'open' });
7
+ this.shadowRoot.appendChild(this.canvas);
8
+ this.WIN = window;
9
+ this.DOC = this.WIN.document;
10
+ this.DOC_EL = this.DOC.documentElement;
11
+ this.BODY = this.DOC.querySelector('body');
12
+ }
13
+
14
+ connectedCallback() {
15
+ this.ctx = this.canvas.getContext('2d');
16
+ this.black = pc => `rgba(0,0,0,${pc / 100})`;
17
+ this.viewport = this.querySelector(this.getAttribute('selector'));
18
+ this.settings = {
19
+ viewport: this.viewport,
20
+ styles: {
21
+ 'header,footer,section,article': this.black(8),
22
+ 'h1,a': this.black(10),
23
+ 'h2,h3,h4': this.black(8),
24
+ },
25
+ back: this.black(2),
26
+ view: this.black(5),
27
+ drag: this.black(10),
28
+ interval: null,
29
+ ...this.options,
106
30
  };
107
31
 
108
- find(sel) {
109
- return Array.from((this.viewport || this.DOC).querySelectorAll(sel));
110
- }
111
-
112
- apply_styles(styles) {
113
- Object.keys(styles).forEach(sel => {
114
- const col = styles[sel];
115
- this.find(sel).forEach(el => {
116
- this.draw_rect(this.rect_rel_to(this.rect_of_el(el), this.root_rect), col);
117
- });
118
- });
32
+ const _listener = (el, method, types, fn) =>
33
+ types.split(/\s+/).forEach(type => el[method](type, fn));
34
+ this.on = (el, types, fn) => _listener(el, 'addEventListener', types, fn);
35
+ this.off = (el, types, fn) => _listener(el, 'removeEventListener', types, fn);
36
+
37
+ this.Rect = (x, y, w, h) => ({
38
+ x,
39
+ y,
40
+ w,
41
+ h,
42
+ });
43
+
44
+ this.rect_rel_to = (rect, pos = { x: 0, y: 0 }) =>
45
+ this.Rect(rect.x - pos.x, rect.y - pos.y, rect.w, rect.h);
46
+
47
+ this.rect_of_doc = () =>
48
+ this.Rect(0, 0, document.documentElement.scrollWidth, document.documentElement.scrollHeight);
49
+
50
+ this.rect_of_win = () =>
51
+ this.Rect(
52
+ window.pageXOffset,
53
+ window.pageYOffset,
54
+ document.documentElement.clientWidth,
55
+ document.documentElement.clientHeight,
56
+ );
57
+
58
+ this.el_get_offset = el => {
59
+ const br = el.getBoundingClientRect();
60
+ return { x: br.left + window.pageXOffset, y: br.top + window.pageYOffset };
119
61
  };
120
62
 
121
-
122
- draw () {
123
- this.root_rect = this.viewport ? this.rect_of_content(this.viewport) : this.rect_of_doc();
124
- this.view_rect = this.viewport ? this.rect_of_viewport(this.viewport) : this.rect_of_win();
125
- this.scale = this.calc_scale(this.root_rect.w, this.root_rect.h);
126
-
127
- this.resize_canvas(this.root_rect.w * this.scale, this.root_rect.h * this.scale);
128
-
129
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
130
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
131
- this.ctx.scale(this.scale, this.scale);
132
-
133
- this.draw_rect(this.rect_rel_to(this.root_rect, this.root_rect), this.settings.back);
134
- this.apply_styles(this.settings.styles);
135
- this.draw_rect(this.rect_rel_to(this.view_rect, this.root_rect), this.drag ? this.settings.drag : this.settings.view);
63
+ this.rect_of_el = el => {
64
+ const { x, y } = this.el_get_offset(el);
65
+ return this.Rect(x, y, el.offsetWidth, el.offsetHeight);
136
66
  };
137
67
 
138
- on_drag(ev) {
139
- ev.preventDefault();
140
- const cr = this.rect_of_viewport(this.canvas);
141
- const x = (ev.pageX - cr.x) / this.scale - this.view_rect.w * this.drag_rx;
142
- const y = (ev.pageY - cr.y) / this.scale - this.view_rect.h * this.drag_ry;
143
-
144
- if (this.viewport) {
145
- this.viewport.scrollLeft = x;
146
- this.viewport.scrollTop = y;
147
- } else {
148
- this.WIN.scrollTo(x, y);
149
- }
150
- this.draw();
68
+ this.rect_of_viewport = el => {
69
+ const { x, y } = this.el_get_offset(el);
70
+ return this.Rect(x + el.clientLeft, y + el.clientTop, el.clientWidth, el.clientHeight);
151
71
  };
152
72
 
153
- on_drag_end (ev) {
154
- this.drag = false;
155
- this.canvas.style.cursor = 'pointer';
156
- this.BODY.style.cursor = 'auto';
157
- this.off(this.WIN, 'mousemove', this.on_drag);
158
- this.off(this.WIN, 'mouseup', this.on_drag_end);
159
- this.on_drag(ev);
73
+ this.rect_of_content = el => {
74
+ const { x, y } = this.el_get_offset(el);
75
+ return this.Rect(
76
+ x + el.clientLeft - el.scrollLeft,
77
+ y + el.clientTop - el.scrollTop,
78
+ el.scrollWidth,
79
+ el.scrollHeight,
80
+ );
160
81
  };
161
82
 
162
- on_drag_start (ev) {
163
- this.drag = true;
164
-
165
- const cr = this.rect_of_viewport(this.canvas);
166
- const vr = this.rect_rel_to(this.view_rect, this.root_rect);
167
- this.drag_rx = ((ev.pageX - cr.x) / this.scale - vr.x) / vr.w;
168
- this.drag_ry = ((ev.pageY - cr.y) / this.scale - vr.y) / vr.h;
169
- if (this.drag_rx < 0 || this.drag_rx > 1 || this.drag_ry < 0 || this.drag_ry > 1) {
170
- this.drag_rx = 0.5;
171
- this.drag_ry = 0.5;
172
- }
173
-
174
- this.canvas.style.cursor = 'crosshair';
175
- this.BODY.style.cursor = 'crosshair';
176
- this.on(this.WIN, 'mousemove', this.on_drag);
177
- this.on(this.WIN, 'mouseup', this.on_drag_end);
178
- this.on_drag(ev);
83
+ this.calc_scale = (() => {
84
+ const width = this.canvas.clientWidth;
85
+ const height = this.canvas.clientHeight;
86
+ return (w, h) => Math.min(width / w, height / h);
87
+ })();
88
+
89
+ this.resize_canvas = (w, h) => {
90
+ this.canvas.width = w;
91
+ this.canvas.height = h;
92
+ this.canvas.style.width = `${w}px`;
93
+ this.canvas.style.height = `${h}px`;
179
94
  };
180
95
 
181
- init () {
182
- this.canvas.style.cursor = 'pointer';
183
- this.on(this.canvas, 'mousedown', this.on_drag_start);
184
- this.on(this.viewport || this.WIN, 'load resize scroll', this.draw());
185
- if (this.settings.interval > 0) {
186
- setInterval(() => this.draw(), this.settings.interval);
187
- }
188
- this.draw();
189
- };
96
+ this.viewport = this.settings.viewport;
97
+ this.find = sel => Array.from((this.viewport || document).querySelectorAll(sel));
98
+
99
+ this.drag = false;
100
+ this.root_rect = null;
101
+ this.view_rect = null;
102
+ this.scale = null;
103
+ this.drag_rx = null;
104
+ this.drag_ry = null;
105
+ }
106
+
107
+ draw_rect(rect, col) {
108
+ if (col) {
109
+ this.ctx.beginPath();
110
+ this.ctx.rect(rect.x, rect.y, rect.w, rect.h);
111
+ this.ctx.fillStyle = col;
112
+ this.ctx.fill();
113
+ }
114
+ }
115
+
116
+ find(sel) {
117
+ return Array.from((this.viewport || this.DOC).querySelectorAll(sel));
118
+ }
119
+
120
+ apply_styles(styles) {
121
+ Object.keys(styles).forEach(sel => {
122
+ const col = styles[sel];
123
+ this.find(sel).forEach(el => {
124
+ this.draw_rect(this.rect_rel_to(this.rect_of_el(el), this.root_rect), col);
125
+ });
126
+ });
127
+ }
128
+
129
+ draw() {
130
+ this.root_rect = this.viewport ? this.rect_of_content(this.viewport) : this.rect_of_doc();
131
+ this.view_rect = this.viewport ? this.rect_of_viewport(this.viewport) : this.rect_of_win();
132
+ this.scale = this.calc_scale(this.root_rect.w, this.root_rect.h);
133
+
134
+ this.resize_canvas(this.root_rect.w * this.scale, this.root_rect.h * this.scale);
135
+
136
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
137
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
138
+ this.ctx.scale(this.scale, this.scale);
139
+
140
+ this.draw_rect(this.rect_rel_to(this.root_rect, this.root_rect), this.settings.back);
141
+ this.apply_styles(this.settings.styles);
142
+ this.draw_rect(
143
+ this.rect_rel_to(this.view_rect, this.root_rect),
144
+ this.drag ? this.settings.drag : this.settings.view,
145
+ );
146
+ }
147
+
148
+ on_drag(ev) {
149
+ ev.preventDefault();
150
+ const cr = this.rect_of_viewport(this.canvas);
151
+ const x = (ev.pageX - cr.x) / this.scale - this.view_rect.w * this.drag_rx;
152
+ const y = (ev.pageY - cr.y) / this.scale - this.view_rect.h * this.drag_ry;
153
+
154
+ if (this.viewport) {
155
+ this.viewport.scrollLeft = x;
156
+ this.viewport.scrollTop = y;
157
+ } else {
158
+ this.WIN.scrollTo(x, y);
159
+ }
160
+ this.draw();
161
+ }
162
+
163
+ on_drag_end(ev) {
164
+ this.drag = false;
165
+ this.canvas.style.cursor = 'pointer';
166
+ this.BODY.style.cursor = 'auto';
167
+ this.off(this.WIN, 'mousemove', this.on_drag);
168
+ this.off(this.WIN, 'mouseup', this.on_drag_end);
169
+ this.on_drag(ev);
170
+ }
171
+
172
+ on_drag_start(ev) {
173
+ this.drag = true;
174
+
175
+ const cr = this.rect_of_viewport(this.canvas);
176
+ const vr = this.rect_rel_to(this.view_rect, this.root_rect);
177
+ this.drag_rx = ((ev.pageX - cr.x) / this.scale - vr.x) / vr.w;
178
+ this.drag_ry = ((ev.pageY - cr.y) / this.scale - vr.y) / vr.h;
179
+ if (this.drag_rx < 0 || this.drag_rx > 1 || this.drag_ry < 0 || this.drag_ry > 1) {
180
+ this.drag_rx = 0.5;
181
+ this.drag_ry = 0.5;
182
+ }
190
183
 
184
+ this.canvas.style.cursor = 'crosshair';
185
+ this.BODY.style.cursor = 'crosshair';
186
+ this.on(this.WIN, 'mousemove', this.on_drag);
187
+ this.on(this.WIN, 'mouseup', this.on_drag_end);
188
+ this.on_drag(ev);
189
+ }
190
+
191
+ init() {
192
+ this.canvas.style.cursor = 'pointer';
193
+ this.on(this.canvas, 'mousedown', this.on_drag_start);
194
+ this.on(this.viewport || this.WIN, 'load resize scroll', this.draw());
195
+ if (this.settings.interval > 0) {
196
+ setInterval(() => this.draw(), this.settings.interval);
197
+ }
198
+ this.draw();
199
+ }
191
200
  }
192
201
  if (!customElements.get('fx-minimap')) {
193
- customElements.define('fx-minimap', FxMinimap);
202
+ customElements.define('fx-minimap', FxMinimap);
194
203
  }
@@ -66,7 +66,7 @@ export function newElement(elem, attrs) {
66
66
  return el;
67
67
  }
68
68
 
69
- // Helper function for options view
69
+ // Helper function for options view
70
70
  export function drawOptionRow(optionCode, optionText) {
71
71
  const row = newElement('span', { class: 'adi-opt' });
72
72
  row.innerHTML = `<label><input type="checkbox" data-opt="${optionCode}">${optionText}</label>`;
@@ -1,18 +1,32 @@
1
1
  import '../fx-model.js';
2
- import { foreElementMixin } from '../ForeElementMixin.js';
2
+ import ForeElementMixin from '../ForeElementMixin.js';
3
3
  import { ModelItem } from '../modelitem.js';
4
4
  import { Fore } from '../fore.js';
5
- import { XPathUtil } from '../xpath-util.js';
6
5
  import getInScopeContext from '../getInScopeContext.js';
7
- import { evaluateXPathToFirstNode} from '../xpath-evaluation.js';
8
- import {Relevance} from "../relevance.js";
6
+ import { evaluateXPathToFirstNode } from '../xpath-evaluation.js';
7
+
8
+ function isDifferent(oldNodeValue, oldControlValue, newControlValue) {
9
+ if (oldNodeValue === null) {
10
+ return false;
11
+ }
12
+
13
+ if (newControlValue && oldControlValue && newControlValue.nodeType && oldControlValue.nodeType) {
14
+ return newControlValue.outerHTML !== oldControlValue.outerHTML;
15
+ }
16
+
17
+ if (oldControlValue === newControlValue) {
18
+ return false;
19
+ }
20
+
21
+ return true;
22
+ }
9
23
 
10
24
  /**
11
25
  * `AbstractControl` -
12
26
  * is a general base class for control elements.
13
27
  *
14
28
  */
15
- export default class AbstractControl extends foreElementMixin(HTMLElement) {
29
+ export default class AbstractControl extends ForeElementMixin {
16
30
  constructor() {
17
31
  super();
18
32
  this.value = null;
@@ -34,10 +48,11 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
34
48
  * (re)apply all modelItem state properties to this control. model -> UI
35
49
  */
36
50
  async refresh(force) {
37
- if(force) this.force=true;
51
+ if (force) this.force = true;
38
52
  // console.log('### AbstractControl.refresh on : ', this);
39
53
 
40
- const currentVal = this.value;
54
+ // Save the old value of this control. this may be the stringified version, contrast to the node in `nodeset`
55
+ const oldValue = this.value;
41
56
 
42
57
  // if(this.repeated) return
43
58
  if (this.isNotBound()) return;
@@ -53,42 +68,45 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
53
68
  if (this.isBound()) {
54
69
  // this.control = this.querySelector('#control');
55
70
 
56
- if(!this.nodeset){
57
-
71
+ if (!this.nodeset) {
58
72
  const create = this.closest('[create]');
59
- if(create){
73
+ if (create) {
60
74
  // ### check if parent element exists
61
- let attrName; let parentPath; let parentNode;
75
+ let attrName;
76
+ let parentPath;
77
+ let parentNode;
62
78
 
63
- if(this.ref.includes('/')){
79
+ if (this.ref.includes('/')) {
64
80
  parentPath = this.ref.substring(0, this.ref.indexOf('/'));
65
81
  const inscope = getInScopeContext(this.parentNode, this.ref);
66
- parentNode = evaluateXPathToFirstNode(parentPath,inscope,this);
67
-
68
- if(parentNode && parentNode.nodeType === Node.ELEMENT_NODE){
69
- if(this.ref.includes('@')){
70
- attrName = this.ref.substring(this.ref.indexOf('/')+2);
71
- parentNode.setAttribute(attrName,'');
72
- }else{
73
- Fore.dispatch(this,'warn',{message:'"create" is not implemented for elements'})
82
+ parentNode = evaluateXPathToFirstNode(parentPath, inscope, this);
83
+
84
+ if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
85
+ if (this.ref.includes('@')) {
86
+ attrName = this.ref.substring(this.ref.indexOf('/') + 2);
87
+ parentNode.setAttribute(attrName, '');
88
+ } else {
89
+ Fore.dispatch(this, 'warn', {
90
+ message: '"create" is not implemented for elements',
91
+ });
74
92
  }
75
93
  }
76
- }else{
94
+ } else {
77
95
  const inscope = getInScopeContext(this, this.ref);
78
96
 
79
- if(this.ref.includes('@')) {
97
+ if (this.ref.includes('@')) {
80
98
  attrName = this.ref.substring(this.ref.indexOf('@') + 1);
81
99
  inscope.setAttribute(attrName, '');
82
- }else{
83
- Fore.dispatch(this,'warn',{message:'"create" is not implemented for elements'})
100
+ } else {
101
+ Fore.dispatch(this, 'warn', { message: '"create" is not implemented for elements' });
84
102
  // inscope = getInScopeContext(this.parentNode, this.ref);
85
103
  }
86
104
  }
87
- }else{
105
+ } else {
88
106
  // ### this actually makes the control nonrelevant
89
107
  // todo: we should call a template function here to allow detachment of event-listeners and resetting eventual state
90
108
  // this.style.display = 'none';
91
- this.setAttribute('nonrelevant','');
109
+ this.setAttribute('nonrelevant', '');
92
110
  }
93
111
  return;
94
112
  }
@@ -140,23 +158,25 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
140
158
  this.handleModelItemProperties();
141
159
 
142
160
  // if(!this.closest('fx-fore').ready) return; // state change event do not fire during init phase (initial refresh)
143
- if(this.getOwnerForm().initialRun){
144
- Fore.dispatch(this,'init',{});
161
+ if (this.getOwnerForm().initialRun) {
162
+ Fore.dispatch(this, 'init', {});
145
163
  }
146
164
  if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
147
- // if oldVal is null we haven't received a concrete value yet
148
-
149
- if (this.localName !== 'fx-control') return;
150
- if (this.oldVal !== null && currentVal !== this.value) {
151
- Fore.dispatch(this, 'value-changed', { path: this.modelItem.path , value:this.modelItem.value});
165
+ // if oldVal is null we haven't received a concrete value yet
166
+
167
+ if (this.localName !== 'fx-control') return;
168
+ if (isDifferent(this.oldVal, this.value, oldValue)) {
169
+ Fore.dispatch(this, 'value-changed', {
170
+ path: this.modelItem.path,
171
+ value: this.modelItem.value,
172
+ oldvalue: this.oldVal,
173
+ });
152
174
  }
153
175
  }
154
176
  }
155
177
  }
156
178
 
157
- refreshFromModelItem(modelItem){
158
-
159
- }
179
+ refreshFromModelItem(modelItem) {}
160
180
 
161
181
  /**
162
182
  *
@@ -192,13 +212,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
192
212
  // eslint-disable-next-line class-methods-use-this
193
213
  handleRequired() {
194
214
  // console.log('mip required', this.modelItem.required);
195
- this.widget = this.getWidget();
196
- const wasRequired = this.isRequired();
215
+ this.widget = this.getWidget();
216
+ const wasRequired = this.isRequired();
197
217
 
198
- if(!this.modelItem.required){
218
+ if (!this.modelItem.required) {
199
219
  this.widget.removeAttribute('required');
200
220
  this.removeAttribute('required');
201
- if (wasRequired !== this.modelItem.required){
221
+ if (wasRequired !== this.modelItem.required) {
202
222
  this._dispatchEvent('optional');
203
223
  }
204
224
  return;
@@ -220,20 +240,19 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
220
240
  this._dispatchEvent('required');
221
241
  }
222
242
 
223
- /*
243
+ /*
224
244
  if (this.isRequired() !== this.modelItem.required) {
225
245
  this._updateRequired();
226
246
  }
227
247
  */
228
248
  }
229
249
 
230
-
231
250
  _updateRequired() {
232
251
  if (this.modelItem.required) {
233
252
  // if (this.getOwnerForm().ready){
234
253
  if (this.visited || this.force) {
235
- // if (this.visited ) {
236
- // if (this.widget.value === '') {
254
+ // if (this.visited ) {
255
+ // if (this.widget.value === '') {
237
256
  if (this.modelItem.value === '') {
238
257
  this.classList.add('isEmpty');
239
258
  this._toggleValid(false);
@@ -252,13 +271,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
252
271
  }
253
272
  }
254
273
 
255
- _toggleValid(valid){
256
- if(valid){
274
+ _toggleValid(valid) {
275
+ if (valid) {
257
276
  this.removeAttribute('invalid');
258
- this.setAttribute('valid','');
259
- }else{
277
+ this.setAttribute('valid', '');
278
+ } else {
260
279
  this.removeAttribute('valid');
261
- this.setAttribute('invalid','');
280
+ this.setAttribute('invalid', '');
262
281
  }
263
282
  }
264
283
 
@@ -287,19 +306,18 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
287
306
  if (this.modelItem.constraint) {
288
307
  // if (alert) alert.style.display = 'none';
289
308
  this._dispatchEvent('valid');
290
- this.setAttribute('valid','');
309
+ this.setAttribute('valid', '');
291
310
  this.removeAttribute('invalid');
292
311
  } else {
293
312
  this.setAttribute('invalid', '');
294
313
  this.removeAttribute('valid');
295
314
  // ### constraint is invalid - handle alerts
296
- /*
315
+ /*
297
316
  if (alert) {
298
317
  alert.style.display = 'block';
299
318
  }
300
319
  */
301
320
  if (this.modelItem.alerts.length !== 0) {
302
-
303
321
  const controlAlert = this.querySelector('fx-alert');
304
322
  if (!controlAlert) {
305
323
  const { alerts } = this.modelItem;
@@ -327,7 +345,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
327
345
  this.removeAttribute('nonrelevant');
328
346
  if (Array.isArray(item) && item.length === 0) {
329
347
  this._dispatchEvent('nonrelevant');
330
- this.setAttribute('nonrelevant','');
348
+ this.setAttribute('nonrelevant', '');
331
349
  // this.style.display = 'none';
332
350
  return;
333
351
  }
@@ -335,12 +353,12 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
335
353
  if (this.modelItem.relevant) {
336
354
  this._dispatchEvent('relevant');
337
355
  // this._fadeIn(this, this.display);
338
- this.setAttribute('relevant','');
356
+ this.setAttribute('relevant', '');
339
357
  // this.style.display = this.display;
340
358
  } else {
341
359
  this._dispatchEvent('nonrelevant');
342
360
  // this._fadeOut(this);
343
- this.setAttribute('nonrelevant','');
361
+ this.setAttribute('nonrelevant', '');
344
362
  // this.style.display = 'none';
345
363
  }
346
364
  }
@@ -351,7 +369,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
351
369
  }
352
370
 
353
371
  isValid() {
354
- return this.hasAttribute('invalid') ? false : true;
372
+ return !this.hasAttribute('invalid');
355
373
  }
356
374
 
357
375
  isReadonly() {