@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.
Files changed (71) hide show
  1. package/dist/fore-dev.js +2 -36
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +2 -30
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +13 -0
  6. package/package.json +9 -5
  7. package/resources/fore.css +178 -92
  8. package/src/DependencyNotifyingDomFacade.js +1 -2
  9. package/src/ForeElementMixin.js +31 -5
  10. package/src/actions/abstract-action.js +379 -297
  11. package/src/actions/fx-action.js +0 -1
  12. package/src/actions/fx-append.js +1 -2
  13. package/src/actions/fx-confirm.js +12 -0
  14. package/src/actions/fx-copy.js +0 -1
  15. package/src/actions/fx-delete.js +31 -9
  16. package/src/actions/fx-dispatch.js +19 -5
  17. package/src/actions/fx-hide.js +19 -0
  18. package/src/actions/fx-insert.js +72 -8
  19. package/src/actions/fx-load.js +253 -0
  20. package/src/actions/fx-message.js +22 -7
  21. package/src/actions/fx-refresh.js +11 -1
  22. package/src/actions/fx-reload.js +12 -2
  23. package/src/actions/fx-replace.js +5 -4
  24. package/src/actions/fx-reset.js +48 -0
  25. package/src/actions/fx-return.js +0 -1
  26. package/src/actions/fx-send.js +40 -2
  27. package/src/actions/fx-setfocus.js +25 -7
  28. package/src/actions/fx-setvalue.js +32 -4
  29. package/src/actions/fx-show.js +14 -2
  30. package/src/actions/fx-toggle.js +0 -1
  31. package/src/actions/fx-toggleboolean.js +58 -0
  32. package/src/actions/fx-update.js +9 -0
  33. package/src/events.js +0 -1
  34. package/src/fore.js +118 -63
  35. package/src/functions/common-function.js +28 -0
  36. package/src/fx-bind.js +9 -7
  37. package/src/fx-fore.js +153 -55
  38. package/src/fx-instance.js +55 -17
  39. package/src/fx-model.js +31 -33
  40. package/src/fx-submission.js +50 -47
  41. package/src/getInScopeContext.js +8 -10
  42. package/src/lab/fore-component.js +90 -0
  43. package/src/lab/instance-inspector.js +56 -0
  44. package/src/lab/template.html +16 -0
  45. package/src/relevance.js +27 -1
  46. package/src/tools/adi.js +1056 -0
  47. package/src/tools/fx-action-log.js +662 -0
  48. package/src/tools/fx-devtools.js +444 -0
  49. package/src/tools/fx-dom-inspector.js +609 -0
  50. package/src/tools/fx-json-instance.js +435 -0
  51. package/src/tools/fx-log-item.js +133 -0
  52. package/src/tools/fx-log-settings.js +474 -0
  53. package/src/tools/fx-minimap.js +194 -0
  54. package/src/tools/helpers.js +132 -0
  55. package/src/ui/abstract-control.js +41 -3
  56. package/src/ui/fx-alert.js +0 -1
  57. package/src/ui/fx-container.js +14 -3
  58. package/src/ui/fx-control.js +553 -474
  59. package/src/ui/fx-dialog.js +2 -0
  60. package/src/ui/fx-dom-inspector.js +1255 -0
  61. package/src/ui/fx-group.js +3 -4
  62. package/src/ui/fx-hint.js +2 -4
  63. package/src/ui/fx-inspector.js +5 -6
  64. package/src/ui/fx-items.js +55 -14
  65. package/src/ui/fx-output.js +36 -17
  66. package/src/ui/fx-repeat-attributes.js +10 -43
  67. package/src/ui/fx-repeat.js +5 -7
  68. package/src/ui/fx-switch.js +14 -3
  69. package/src/ui/fx-trigger.js +13 -1
  70. package/src/xpath-evaluation.js +109 -26
  71. package/src/xpath-util.js +55 -1
package/src/fx-model.js CHANGED
@@ -6,7 +6,13 @@ import {evaluateXPath, evaluateXPathToBoolean} from './xpath-evaluation.js';
6
6
  import {XPathUtil} from './xpath-util.js';
7
7
 
8
8
  /**
9
- * @ts-check
9
+ * The model of this Fore scope. It holds all the intances, binding, submissions and custom functions that
10
+ * as required.
11
+ *
12
+ * The model is updatin by executing rebuild (as needed), recalculate and revalidate in turn.
13
+ *
14
+ * After the cycle is run all modelItems have updated their stete to reflect latest computations.
15
+ *
10
16
  */
11
17
  export class FxModel extends HTMLElement {
12
18
  static dataChanged = false;
@@ -103,7 +109,6 @@ export class FxModel extends HTMLElement {
103
109
  // console.time('instance-loading');
104
110
  const instances = this.querySelectorAll('fx-instance');
105
111
  if (instances.length > 0) {
106
- console.group('init instances');
107
112
  const promises = [];
108
113
  instances.forEach(instance => {
109
114
  promises.push(instance.init());
@@ -119,7 +124,6 @@ export class FxModel extends HTMLElement {
119
124
  await Fore.dispatch(this, 'model-construct-done', {model: this});
120
125
  this.inited = true;
121
126
  this.updateModel();
122
- console.groupEnd();
123
127
  } else {
124
128
  // ### if there's no instance one will created
125
129
  await this.dispatchEvent(
@@ -145,17 +149,20 @@ export class FxModel extends HTMLElement {
145
149
  updateModel() {
146
150
  // console.time('updateModel');
147
151
  this.rebuild();
152
+ /*
148
153
  if (this.skipUpdate){
149
154
  console.info('%crecalculate/revalidate skipped - no bindings', 'font-style: italic; background: #90a4ae; color:lightgrey; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
150
155
  return;
151
156
  }
157
+ */
152
158
  this.recalculate();
153
159
  this.revalidate();
160
+ // console.log('updateModel finished with modelItems ', this.modelItems);
161
+
154
162
  // console.timeEnd('updateModel');
155
163
  }
156
164
 
157
165
  rebuild() {
158
- console.info('%crebuild', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
159
166
  this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
160
167
  this.modelItems = [];
161
168
 
@@ -172,14 +179,11 @@ export class FxModel extends HTMLElement {
172
179
  });
173
180
 
174
181
  console.log(`mainGraph`, this.mainGraph);
175
- // console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
182
+ console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
176
183
 
177
184
  // this.dispatchEvent(new CustomEvent('rebuild-done', {detail: {maingraph: this.mainGraph}}));
178
185
  Fore.dispatch(this,'rebuild-done',{maingraph:this.mainGraph});
179
- console.log(
180
- `rebuild finished with modelItems ${this.modelItems.length} item(s)`,
181
- this.modelItems,
182
- );
186
+ console.log('mainGraph', this.mainGraph);
183
187
  }
184
188
 
185
189
  /**
@@ -188,20 +192,15 @@ export class FxModel extends HTMLElement {
188
192
  * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
189
193
  */
190
194
  recalculate() {
191
- console.info('%crecalculate', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
192
-
193
195
  if (!this.mainGraph) {
194
196
  return;
195
197
  }
196
198
 
197
- console.group('### recalculate');
198
- console.log('changed nodes ', this.changed);
199
-
200
-
201
- console.time('recalculate');
199
+ // console.log('changed nodes ', this.changed);
202
200
  this.computes = 0;
203
201
 
204
202
  this.subgraph = new DepGraph(false);
203
+ // ### create the subgraph for all changed modelItems
205
204
  if (this.changed.length !== 0) {
206
205
  // ### build the subgraph
207
206
  this.changed.forEach(modelItem => {
@@ -246,22 +245,16 @@ export class FxModel extends HTMLElement {
246
245
  const toRefresh = [...this.changed];
247
246
  this.formElement.toRefresh = toRefresh;
248
247
  this.changed = [];
249
- console.log('subgraph', this.subgraph);
250
- this.dispatchEvent(
251
- new CustomEvent('recalculate-done', {detail: {subgraph: this.subgraph}}),
252
- );
248
+ Fore.dispatch(this,'recalculate-done',{graph:this.subgraph,computes:this.computes})
253
249
  } else {
254
250
  const v = this.mainGraph.overallOrder(false);
255
251
  v.forEach(path => {
256
252
  const node = this.mainGraph.getNodeData(path);
257
253
  this.compute(node, path);
258
254
  });
255
+ Fore.dispatch(this,'recalculate-done',{graph:this.mainGraph,computes:this.computes})
259
256
  }
260
- console.log(`recalculated ${this.computes} modelItems`);
261
-
262
- console.timeEnd('recalculate');
263
257
  console.log('recalculate finished with modelItems ', this.modelItems);
264
- console.groupEnd();
265
258
  }
266
259
 
267
260
  /*
@@ -323,13 +316,13 @@ export class FxModel extends HTMLElement {
323
316
  }
324
317
  }
325
318
  */
319
+ const expr = modelItem.bind[property];
326
320
  if (property === 'calculate') {
327
- const expr = modelItem.bind[property];
328
321
  const compute = evaluateXPath(expr, modelItem.node, this);
329
322
  modelItem.value = compute;
330
323
  modelItem.readonly = true; // calculated nodes are always readonly
331
324
  } else if (property !== 'constraint' && property !== 'type') {
332
- const expr = modelItem.bind[property];
325
+ // ### re-compute the Boolean value of all facets expect 'constraint' and 'type' which are handled in revalidate()
333
326
  if (expr) {
334
327
  const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
335
328
  modelItem[property] = compute;
@@ -363,11 +356,9 @@ export class FxModel extends HTMLElement {
363
356
  *
364
357
  */
365
358
  revalidate() {
366
- console.info('%crevalidate', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
367
359
 
368
360
  if (this.modelItems.length === 0) return true;
369
361
 
370
- console.group('### revalidate');
371
362
  console.time('revalidate');
372
363
 
373
364
  // reset submission validation
@@ -384,7 +375,7 @@ export class FxModel extends HTMLElement {
384
375
  */
385
376
  if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('constraint')) {
386
377
  const constraint = bind.getAttribute('constraint');
387
- if (constraint) {
378
+ if (constraint && modelItem.node) {
388
379
  const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
389
380
  // console.log('modelItem validity computed: ', compute);
390
381
  modelItem.constraint = compute;
@@ -400,7 +391,6 @@ export class FxModel extends HTMLElement {
400
391
  modelItem.required = compute;
401
392
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
402
393
  if (!modelItem.node.textContent) {
403
- console.log('modelItem required check failed: ');
404
394
  valid = false;
405
395
  }
406
396
  // if (!compute) valid = false;
@@ -417,12 +407,16 @@ export class FxModel extends HTMLElement {
417
407
  }
418
408
  }
419
409
  });
420
- console.timeEnd('revalidate');
421
410
  console.log('modelItems after revalidate: ', this.modelItems);
422
- console.groupEnd();
423
411
  return valid;
424
412
  }
425
413
 
414
+ addChanged(modelItem){
415
+ if(this.inited){
416
+ this.changed.push(modelItem);
417
+ }
418
+ }
419
+
426
420
  /**
427
421
  *
428
422
  * @param node
@@ -455,7 +449,11 @@ export class FxModel extends HTMLElement {
455
449
  // console.log('instances array ',Array.from(this.instances));
456
450
 
457
451
  const instArray = Array.from(this.instances);
458
- return instArray.find(inst => inst.id === id);
452
+ const found = instArray.find(inst => inst.id === id);
453
+ if(!found){
454
+ return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
455
+ }
456
+ return found;
459
457
  }
460
458
 
461
459
  evalBinding(bindingExpr) {
@@ -3,6 +3,7 @@ import {Relevance} from './relevance.js';
3
3
  import {foreElementMixin} from './ForeElementMixin.js';
4
4
  import {evaluateXPathToString, evaluateXPath} from './xpath-evaluation.js';
5
5
  import getInScopeContext from './getInScopeContext.js';
6
+ import {XPathUtil} from "./xpath-util.js";
6
7
 
7
8
  /**
8
9
  * todo: validate='false'
@@ -11,6 +12,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
11
12
  constructor() {
12
13
  super();
13
14
  this.attachShadow({mode: 'open'});
15
+ this.parameters = new Map();
14
16
  }
15
17
 
16
18
  connectedCallback() {
@@ -45,9 +47,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
45
47
  ? this.getAttribute('serialization')
46
48
  : 'xml';
47
49
 
48
- // if (!this.hasAttribute('url')) throw new Error(`url is required for submission: ${this.id}`);
49
- if (!this.hasAttribute('url')) console.warn(`url is required for submission: ${this.id}`);
50
- this.url = this.getAttribute('url');
50
+ this.url = this.hasAttribute('url') ? this.getAttribute('url'):null;
51
51
 
52
52
  this.targetref = this.hasAttribute('targetref') ? this.getAttribute('targetref') : null;
53
53
 
@@ -90,31 +90,15 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
90
90
  return;
91
91
  }
92
92
  }
93
- console.log('model updated....');
94
93
  await this._serializeAndSend();
95
94
  }
96
95
 
97
- /**
98
- * resolves template expressions for a single attribute
99
- * @param expr the attribute value to evaluate
100
- * @param node the attribute node used for scoped resolution
101
- * @returns {*}
102
- * @private
103
- */
104
- _evaluateAttributeTemplateExpression(expr, node) {
105
- const matches = expr.match(/{[^}]*}/g);
106
- if (matches) {
107
- matches.forEach(match => {
108
- console.log('match ', match);
109
- const naked = match.substring(1, match.length - 1);
110
- const inscope = getInScopeContext(node, naked);
111
- const result = evaluateXPathToString(naked, inscope, this.getOwnerForm());
112
- const replaced = expr.replaceAll(match, result);
113
- console.log('replacing ', expr, ' with ', replaced);
114
- expr = replaced;
115
- });
96
+ _getProperty(attrName){
97
+ if(this.parameters.has(attrName)){
98
+ return this.parameters.get(attrName);
99
+ } else {
100
+ return this.getAttribute(attrName);
116
101
  }
117
- return expr;
118
102
  }
119
103
 
120
104
  /**
@@ -123,18 +107,23 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
123
107
  * @private
124
108
  */
125
109
  async _serializeAndSend() {
126
- const resolvedUrl = this._evaluateAttributeTemplateExpression(this.url, this);
127
-
110
+ const url = this._getProperty('url');
111
+ const resolvedUrl = this.evaluateAttributeTemplateExpression(url,this);
112
+ console.log('resolvedUrl',resolvedUrl);
128
113
  const instance = this.getInstance();
129
- console.log('instance type', instance.type);
114
+ if (!instance) {
115
+ Fore.dispatch(this, 'warn', {message: `instance not found ${instance.getAttribute('id')}`})
116
+ }
117
+ const instType = instance.getAttribute('type');
118
+ // console.log('instance type', instance.type);
130
119
 
131
120
  let serialized;
132
121
  if (this.serialization === 'none') {
133
122
  serialized = undefined;
134
123
  } else {
135
124
  // const relevant = this.selectRelevant(instance.type);
136
- const relevant = Relevance.selectRelevant(this, instance.type);
137
- serialized = this._serialize(instance.type, relevant);
125
+ const relevant = Relevance.selectRelevant(this, instType);
126
+ serialized = this._serialize(instType, relevant);
138
127
  }
139
128
 
140
129
  // let serialized = serializer.serializeToString(relevant);
@@ -150,6 +139,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
150
139
  this._handleResponse(data);
151
140
  // this.dispatch('submit-done', {});
152
141
  Fore.dispatch(this, 'submit-done', {});
142
+ this.parameters.clear();
153
143
  return;
154
144
  }
155
145
 
@@ -162,9 +152,10 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
162
152
  const serialized = localStorage.getItem(key);
163
153
  if (!serialized) {
164
154
  Fore.dispatch(this, 'submit-error', {message: `Error reading key ${key} from localstorage`});
155
+ this.parameters.clear();
165
156
  return;
166
157
  }
167
- let data = this._parse(serialized, instance);
158
+ const data = this._parse(serialized, instance);
168
159
  this._handleResponse(data);
169
160
  if (this.method === 'consume') {
170
161
  localStorage.removeItem(key);
@@ -191,7 +182,6 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
191
182
 
192
183
  // ### setting headers
193
184
  const headers = this._getHeaders();
194
- console.log('headers', headers);
195
185
 
196
186
  if (!this.methods.includes(this.method.toLowerCase())) {
197
187
  // this.dispatch('error', { message: `Unknown method ${this.method}` });
@@ -201,8 +191,10 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
201
191
  try {
202
192
  const response = await fetch(resolvedUrl, {
203
193
  method: this.method,
204
- mode: 'cors',
205
- credentials: 'include',
194
+ /*
195
+ mode: 'cors',
196
+ credentials: 'include',
197
+ */
206
198
  headers,
207
199
  body: serialized,
208
200
  });
@@ -220,32 +212,34 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
220
212
  contentType.startsWith('text/markdown')
221
213
  ) {
222
214
  const text = await response.text();
223
- this._handleResponse(text);
215
+ this._handleResponse(text, resolvedUrl,contentType);
224
216
  } else if (contentType.startsWith('application/json')) {
225
217
  const json = await response.json();
226
- this._handleResponse(json);
218
+ this._handleResponse(json, resolvedUrl,contentType);
227
219
  } else if (contentType.startsWith('application/xml')) {
228
220
  const text = await response.text();
229
221
  const xml = new DOMParser().parseFromString(text, 'application/xml');
230
- this._handleResponse(xml);
222
+ this._handleResponse(xml, resolvedUrl,contentType);
231
223
  } else {
232
224
  const blob = await response.blob();
233
- this._handleResponse(blob);
225
+ this._handleResponse(blob, resolvedUrl,contentType);
234
226
  }
235
227
 
236
228
  // this.dispatch('submit-done', {});
237
229
  Fore.dispatch(this, 'submit-done', {});
238
230
  } catch (error) {
239
231
  Fore.dispatch(this, 'submit-error', {error: error.message});
232
+ } finally {
233
+ this.parameters.clear();
240
234
  }
241
235
  }
242
236
 
243
237
  _parse(serialized, instance) {
244
238
  let data = null;
245
- if (serialized && instance.type === 'xml') {
239
+ if (serialized && instance.getAttribute('type') === 'xml') {
246
240
  data = new DOMParser().parseFromString(serialized, 'application/xml');
247
241
  }
248
- if (serialized && instance.type === 'json') {
242
+ if (serialized && instance.getAttribute('type') === 'json') {
249
243
  data = JSON.parse(serialized);
250
244
  }
251
245
  return data;
@@ -319,8 +313,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
319
313
  * @param data
320
314
  * @private
321
315
  */
322
- _handleResponse(data) {
323
- console.log('_handleResponse ', data);
316
+ _handleResponse(data, resolvedUrl, contentType) {
317
+ // console.log('_handleResponse ', data);
324
318
 
325
319
  /*
326
320
  // ### responses need to be handled depending on their type.
@@ -358,8 +352,8 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
358
352
  } else {
359
353
  const instanceData = data;
360
354
  targetInstance.instanceData = instanceData;
361
- console.log('### replaced instance ', this.getModel().instances);
362
- console.log('### replaced instance ', targetInstance.instanceData);
355
+ // console.log('### replaced instance ', this.getModel().instances);
356
+ // console.log('### replaced instance ', targetInstance.instanceData);
363
357
  }
364
358
 
365
359
  // Skip any refreshes if the model is not yet inited
@@ -373,12 +367,21 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
373
367
  }
374
368
 
375
369
  if (this.replace === 'all') {
376
- document.getElementsByTagName('html')[0].innerHTML = data;
370
+ document.open();
371
+ document.write(data);
372
+ document.close();
373
+ window.location.href = resolvedUrl;
374
+ // document.getElementsByTagName('html')[0].innerHTML = data;
377
375
  }
378
- if (this.replace === 'target') {
379
- const target = this.getAttribute('target');
376
+ if (this.replace === 'target' && contentType.startsWith('text/html')) {
377
+ // const target = this.getAttribute('target');
378
+ const target = this._getProperty('target');
380
379
  const targetNode = document.querySelector(target);
381
- targetNode.innerHTML = data;
380
+ if(targetNode){
381
+ targetNode.innerHTML = data;
382
+ }else{
383
+ Fore.dispatch(this, 'submit-error', {message:`targetNode for selector ${target} not found`});
384
+ }
382
385
  }
383
386
  if (this.replace === 'redirect') {
384
387
  window.location.href = data;
@@ -1,6 +1,4 @@
1
1
  import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
2
- import {Fore} from './fore.js';
3
-
4
2
  import {XPathUtil} from './xpath-util.js';
5
3
 
6
4
  function _getElement(node) {
@@ -29,8 +27,8 @@ function _getModelInContext(node) {
29
27
  }
30
28
 
31
29
  function _getInitialContext(node, ref) {
32
- const parentBind = Fore.getClosest('[ref]', node);
33
- const localFore = Fore.getClosest('fx-fore', node);
30
+ const parentBind = XPathUtil.getClosest('[ref]', node);
31
+ const localFore = XPathUtil.getClosest('fx-fore', node);
34
32
 
35
33
  const model = _getModelInContext(node);
36
34
 
@@ -62,12 +60,12 @@ export default function getInScopeContext(node, ref) {
62
60
  if(parentElement.nodeName === 'FX-FORE'){
63
61
  return parentElement.getModel().getDefaultInstance().getDefaultContext();
64
62
  }
65
- const parentBind = Fore.getClosest('[ref]', parentElement.parentNode);
66
- if (parentBind && parentBind.nodeName === 'FX-GROUP') {
63
+ const parentBind = XPathUtil.getClosest('[ref]', parentElement.parentNode);
64
+ if (parentBind && (parentBind.nodeName === 'FX-GROUP' || parentBind.nodeName === 'FX-CONTROL')) {
67
65
  return parentBind.nodeset;
68
66
  }
69
67
 
70
- const repeatItem = Fore.getClosest('fx-repeatitem', parentElement);
68
+ const repeatItem = XPathUtil.getClosest('fx-repeatitem', parentElement);
71
69
  if (repeatItem) {
72
70
  if (node.nodeName === 'context') {
73
71
  return evaluateXPathToFirstNode(
@@ -80,8 +78,8 @@ export default function getInScopeContext(node, ref) {
80
78
  }
81
79
 
82
80
  // ### check for repeatitems created by fx-repeat-attributes - this could possibly be unified with standard repeats
83
- // const repeatItemFromAttrs = Fore.getClosest('.fx-repeatitem', parentElement);
84
- // const repeatItemFromAttrs = Fore.getClosest('.fx-repeatitem', parentElement);
81
+ // const repeatItemFromAttrs = XPathUtil.getClosest('.fx-repeatitem', parentElement);
82
+ // const repeatItemFromAttrs = XPathUtil.getClosest('.fx-repeatitem', parentElement);
85
83
  const repeatItemFromAttrs = parentElement.closest('.fx-repeatitem');
86
84
 
87
85
  if (repeatItemFromAttrs) {
@@ -91,7 +89,7 @@ export default function getInScopeContext(node, ref) {
91
89
  const index = Array.from(parent.children).indexOf(repeatItemFromAttrs);
92
90
 
93
91
  // ### fetching nodeset from fx-repeat-attributes element
94
- const repeatFromAttributes = Fore.getClosest('fx-repeat-attributes', parentElement);
92
+ const repeatFromAttributes = XPathUtil.getClosest('fx-repeat-attributes', parentElement);
95
93
  return repeatFromAttributes.nodeset[index];
96
94
  }
97
95
 
@@ -0,0 +1,90 @@
1
+ /**
2
+ * a simple component that wraps a Fore page and puts it into shadowDom.
3
+ *
4
+ * HTML link elements passed as children will be used to construct a CSSStyleSheet that is passed
5
+ * to the shadowDOM.
6
+ * @customElement
7
+ */
8
+ export class ForeComponent extends HTMLElement {
9
+ constructor() {
10
+ super();
11
+ this.attachShadow({mode: 'open'})
12
+ this.src = ''
13
+ }
14
+
15
+ connectedCallback() {
16
+ this.src= this.getAttribute('src');
17
+ const style = `
18
+ :host {
19
+ display:block;
20
+ }
21
+ `;
22
+ const html = `
23
+ <fx-fore src="${this.src}">
24
+ </fx-fore>
25
+ <slot id="default"></slot>
26
+ `;
27
+
28
+ this.shadowRoot.innerHTML = `
29
+ <style>
30
+ ${style}
31
+ </style>
32
+ ${html}
33
+ `;
34
+
35
+ /*
36
+ * wait for slotchange, then filter document.stylesheets to construct CSSStyleSheet
37
+ */
38
+ const slot = this.shadowRoot.querySelector('#default');
39
+ slot.addEventListener('slotchange', async event => {
40
+ const children = event.target.assignedElements();
41
+ const hostedStylesheet = children.filter(
42
+ linkElem => linkElem.nodeName.toUpperCase() === 'LINK',
43
+ );
44
+ if(!hostedStylesheet) return;
45
+ const allCSS = [...document.styleSheets]
46
+ .map((styleSheet) => {
47
+ if(hostedStylesheet.find(sh => sh.href === styleSheet.href)){
48
+ try {
49
+ return [...styleSheet.cssRules]
50
+ .map((rule) => rule.cssText)
51
+ .join('');
52
+ } catch (e) {
53
+ console.log('Access to stylesheet %s is denied. Ignoring…', styleSheet.href);
54
+ }
55
+ }
56
+ })
57
+ .filter(Boolean)
58
+ .join('\n');
59
+
60
+ const sheet = new CSSStyleSheet();
61
+ sheet.replaceSync(allCSS);
62
+ this.shadowRoot.adoptedStyleSheets = [sheet];
63
+ });
64
+
65
+ /*
66
+ const eventSlot = this.shadowRoot.querySelector('slot[name="event"]');
67
+ eventSlot.addEventListener('slotchange', async event => {
68
+ const children = event.target.assignedElements();
69
+ console.log('events', children)
70
+ });
71
+ */
72
+ const eventTmpl = this.querySelector('fx-action');
73
+ if(eventTmpl){
74
+ // const clone = eventTmpl.content.cloneNode(true);
75
+ const clone = eventTmpl.cloneNode(true);
76
+ this.removeChild(eventTmpl);
77
+ // const content = document.importNode(clone, true);
78
+
79
+ const fore = this.shadowRoot.querySelector('fx-fore');
80
+ // fore.appendChild(content.firstElementChild);
81
+ fore.appendChild(clone);
82
+ }
83
+
84
+ }
85
+
86
+ }
87
+
88
+ if (!customElements.get('fore-component')) {
89
+ customElements.define('fore-component', ForeComponent);
90
+ }
@@ -0,0 +1,56 @@
1
+ import '../../index.js';
2
+ // import '@jinntec/jinn-codemirror/src/jinn-code-mirror.js';
3
+
4
+ /**
5
+ * a simple component that wraps a Fore page and puts it into shadowDom.
6
+ *
7
+ * HTML link elements passed as children will be used to construct a CSSStyleSheet that is passed
8
+ * to the shadowDOM.
9
+ * @customElement
10
+ */
11
+ export class InstanceInspector extends HTMLElement {
12
+ constructor() {
13
+ super();
14
+ this.attachShadow({mode: 'open'})
15
+ }
16
+
17
+ connectedCallback() {
18
+ this.id= this.getAttribute('id');
19
+ const style = `
20
+ :host {
21
+ display:block;
22
+ background:blue;
23
+ }
24
+ section, fx-control, jinn-codemirror{
25
+ display:block;
26
+ width:100%;
27
+ height:100%;
28
+ }
29
+ `;
30
+ const html = `
31
+ <section>
32
+ <fx-control ref="instance('${this.id}')" as="node">
33
+ <label>${this.id}</label>
34
+ <jinn-codemirror mode="xml" class="widget"></jinn-codemirror>
35
+ </fx-control>
36
+ </section>
37
+ <slot></slot>
38
+ `;
39
+
40
+ this.innerHTML = `
41
+ <style>
42
+ ${style}
43
+ </style>
44
+ ${html}
45
+ `;
46
+
47
+
48
+ this.closest('fx-fore').refresh();
49
+
50
+ }
51
+
52
+ }
53
+
54
+ if (!customElements.get('instance-inspector')) {
55
+ customElements.define('instance-inspector', InstanceInspector);
56
+ }
@@ -0,0 +1,16 @@
1
+ <fx-fore>
2
+ <fx-message event="ready">hey from component</fx-message>
3
+ <fx-model>
4
+ <fx-instance>
5
+ <data>
6
+ <greeting>Hello Universe</greeting>
7
+ </data>
8
+ </fx-instance>
9
+ </fx-model>
10
+
11
+ <div class="static {greeting}">Greeting: {greeting}</div>
12
+ <fx-control ref="greeting" update-event="input">
13
+ <label>Hey!</label>
14
+ </fx-control>
15
+ <!-- <slot></slot>-->
16
+ </fx-fore>
package/src/relevance.js CHANGED
@@ -38,6 +38,19 @@ export class Relevance {
38
38
  const { attributes } = n;
39
39
  if (attributes) {
40
40
  Array.from(attributes).forEach(attr => {
41
+ if (element.nonrelevant === 'empty' && !Relevance._isRelevant(element, attr)) {
42
+ clone.setAttribute(attr.nodeName, '');
43
+ }else
44
+ if (Relevance._isRelevant(element, attr)) {
45
+ clone.setAttribute(attr.nodeName, attr.value);
46
+ } else {
47
+ // if (element.nonrelevant === 'empty') {
48
+ // clone.setAttribute(attr.nodeName, '');
49
+ // } else{
50
+ clone.removeAttribute(attr.nodeName);
51
+ // }
52
+ }
53
+ /*
41
54
  if (Relevance._isRelevant(element, attr)) {
42
55
  clone.setAttribute(attr.nodeName, attr.value);
43
56
  } else if (element.nonrelevant === 'empty') {
@@ -45,6 +58,7 @@ export class Relevance {
45
58
  } else {
46
59
  clone.removeAttribute(attr.nodeName);
47
60
  }
61
+ */
48
62
  });
49
63
  }
50
64
  return Relevance._filterRelevant(element, n, clone);
@@ -56,9 +70,21 @@ export class Relevance {
56
70
 
57
71
  static _isRelevant(element, node) {
58
72
  const mi = element.getModel().getModelItem(node);
59
- if (!mi || mi.relevant) {
73
+
74
+ // ### remove empty attributes as these usually are not expected in most XML languages.
75
+ if(node.nodeType === Node.ATTRIBUTE_NODE
76
+ && node.nodeValue === ''){
77
+ return false;
78
+ }
79
+ // ### no modelItem means no constraints
80
+ if(!mi){
60
81
  return true;
61
82
  }
83
+ // ### modelItem 'relevant' is defined and 'true'
84
+ if ( mi.relevant) {
85
+ return true;
86
+ }
87
+
62
88
  return false;
63
89
  }
64
90
  }