@jinntec/fore 1.0.0-2 → 1.0.0-3

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.
@@ -1,5 +1,5 @@
1
1
  import XfAbstractControl from './abstract-control.js';
2
- import { evaluateXPath, evaluateXPathToString } from '../xpath-evaluation.js';
2
+ import { evaluateXPath, evaluateXPathToStrings } from '../xpath-evaluation.js';
3
3
  import getInScopeContext from '../getInScopeContext.js';
4
4
  // import {markdown} from '../drawdown.js';
5
5
 
@@ -68,19 +68,16 @@ export class FxOutput extends XfAbstractControl {
68
68
  }
69
69
 
70
70
  async refresh() {
71
- // ### 1. eval 'value' attr
72
- // await super.refresh();
71
+ // Resolve the ref first. The ref will set the `nodeset` which is important for the 'context'
72
+ if (this.ref) {
73
+ await super.refresh();
74
+ }
73
75
 
76
+ // ### 2. Eval the value
74
77
  if (this.valueAttr) {
75
78
  this.value = this.getValue();
76
79
  await this.updateWidgetValue();
77
- return;
78
80
  }
79
- // ### 2. eval 'ref' attr
80
- if (this.ref) {
81
- super.refresh();
82
- }
83
- // ### 3. use inline content which is there anyway
84
81
  }
85
82
 
86
83
  getValue() {
@@ -90,7 +87,8 @@ export class FxOutput extends XfAbstractControl {
90
87
  if (this.hasAttribute('html')) {
91
88
  return evaluateXPath(this.valueAttr, inscopeContext, this);
92
89
  }
93
- return evaluateXPathToString(this.valueAttr, inscopeContext, this);
90
+
91
+ return evaluateXPathToStrings(this.valueAttr, inscopeContext, this)[0];
94
92
  } catch (error) {
95
93
  console.error(error);
96
94
  this.dispatch('error', { message: error });
@@ -119,7 +117,7 @@ export class FxOutput extends XfAbstractControl {
119
117
  return;
120
118
  */
121
119
 
122
- const node = this.modelItem.node;
120
+ const { node } = this.modelItem;
123
121
 
124
122
  if (node.nodeType) {
125
123
  // const mainSlot = this.shadowRoot.querySelector('#main');
@@ -4,6 +4,7 @@ import { Fore } from '../fore.js';
4
4
  import { foreElementMixin } from '../ForeElementMixin.js';
5
5
  import { evaluateXPath } from '../xpath-evaluation.js';
6
6
  import getInScopeContext from '../getInScopeContext.js';
7
+ import {XPathUtil} from "../xpath-util";
7
8
 
8
9
  /**
9
10
  * `fx-repeat`
@@ -115,14 +116,25 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
115
116
  console.log('insert catched', nodes, this.index);
116
117
  });
117
118
 
118
- if (this.getOwnerForm().lazyRefresh) {
119
+ // if (this.getOwnerForm().lazyRefresh) {
119
120
  this.mutationObserver = new MutationObserver(mutations => {
120
121
  console.log('mutations', mutations);
121
- this.refresh(true);
122
+
123
+ if(mutations[0].type === "childList"){
124
+ const added = mutations[0].addedNodes[0];
125
+ if(added){
126
+ const path = XPathUtil.getPath(added);
127
+ console.log('path mutated',path);
128
+ // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
129
+ this.dispatch('path-mutated',{'path':path,'index': this.index});
130
+ }
131
+
132
+ }
122
133
  });
123
- }
134
+ // }
124
135
  this.getOwnerForm().registerLazyElement(this);
125
136
 
137
+
126
138
  const style = `
127
139
  :host{
128
140
  }
@@ -168,7 +180,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
168
180
  */
169
181
  _evalNodeset() {
170
182
  // const inscope = this.getInScopeContext();
171
- const inscope = getInScopeContext(this, this.ref);
183
+ const inscope = getInScopeContext(this.getAttributeNode('ref') || this, this.ref);
172
184
  // console.log('##### inscope ', inscope);
173
185
  // console.log('##### ref ', this.ref);
174
186
  // now we got a nodeset and attach MutationObserver to it
@@ -248,6 +260,8 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
248
260
  newItem.appendChild(clonedTemplate);
249
261
  this.appendChild(newItem);
250
262
 
263
+ this._initVariables(newItem);
264
+
251
265
  newItem.nodeset = this.nodeset[position - 1];
252
266
  newItem.index = position;
253
267
  }
@@ -353,9 +367,24 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
353
367
  if (repeatItem.index === 1) {
354
368
  this.applyIndex(repeatItem);
355
369
  }
370
+
371
+ this._initVariables(repeatItem);
356
372
  });
357
373
  }
358
374
 
375
+ _initVariables(newRepeatItem) {
376
+ const inScopeVariables = new Map(this.inScopeVariables);
377
+ newRepeatItem.setInScopeVariables(inScopeVariables);
378
+ (function registerVariables(node) {
379
+ for (const child of node.children) {
380
+ if ('setInScopeVariables' in child) {
381
+ child.setInScopeVariables(inScopeVariables);
382
+ }
383
+ registerVariables(child);
384
+ }
385
+ })(newRepeatItem);
386
+ }
387
+
359
388
  _clone() {
360
389
  // const content = this.template.content.cloneNode(true);
361
390
  this.template = this.shadowRoot.querySelector('template');
@@ -368,6 +397,12 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
368
397
  item.removeAttribute('repeat-index');
369
398
  });
370
399
  }
400
+
401
+ setInScopeVariables(inScopeVariables) {
402
+ // Repeats are interesting: the variables should be scoped per repeat item, they should not be
403
+ // able to see the variables in adjacent repeat items!
404
+ this.inScopeVariables = new Map(inScopeVariables);
405
+ }
371
406
  }
372
407
 
373
408
  window.customElements.define('fx-repeat', FxRepeat);
@@ -80,6 +80,9 @@ export class FxTrigger extends XfAbstractControl {
80
80
  repeatedItem.click();
81
81
  }
82
82
 
83
+ // Update all child variables, but only once
84
+ this.querySelectorAll('fx-var').forEach(variableElement => variableElement.refresh());
85
+
83
86
  const forLoop = async () => {
84
87
  for (let i = 0; i < this.children.length; i += 1) {
85
88
  const child = this.children[i];
@@ -217,6 +217,32 @@ function functionNameResolver({ prefix, localName }, _arity) {
217
217
  }
218
218
  }
219
219
 
220
+ /**
221
+ * Get the variables in scope of the form element. These are the values of the variables that
222
+ * logically precede the formElement that declares the XPath
223
+ *
224
+ * @param {Node} formElement The element that declares the XPath
225
+ *
226
+ * @return {Object} A key-value mapping of the variables
227
+ */
228
+ function getVariablesInScope(formElement) {
229
+ let closestActualFormElement = formElement;
230
+ while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
231
+ closestActualFormElement = closestActualFormElement.parentNode;
232
+ }
233
+
234
+ if (!closestActualFormElement) {
235
+ return {};
236
+ }
237
+
238
+ const variables = {};
239
+ for (const key of closestActualFormElement.inScopeVariables.keys()) {
240
+ const varElement = closestActualFormElement.inScopeVariables.get(key);
241
+ variables[key] = varElement.value;
242
+ }
243
+ return variables;
244
+ }
245
+
220
246
  /**
221
247
  * Evaluate an XPath to _any_ type. When possible, prefer to use any other function to ensure the
222
248
  * type of the output is more predictable.
@@ -227,15 +253,23 @@ function functionNameResolver({ prefix, localName }, _arity) {
227
253
  */
228
254
  export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
229
255
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
256
+ const variablesInScope = getVariablesInScope(formElement);
230
257
 
231
- return fxEvaluateXPath(xpath, contextNode, null, variables, 'xs:anyType', {
232
- currentContext: { formElement, variables },
233
- moduleImports: {
234
- xf: XFORMS_NAMESPACE_URI,
258
+ return fxEvaluateXPath(
259
+ xpath,
260
+ contextNode,
261
+ null,
262
+ { ...variablesInScope, ...variables },
263
+ 'xs:anyType',
264
+ {
265
+ currentContext: { formElement, variables },
266
+ moduleImports: {
267
+ xf: XFORMS_NAMESPACE_URI,
268
+ },
269
+ functionNameResolver,
270
+ namespaceResolver,
235
271
  },
236
- functionNameResolver,
237
- namespaceResolver,
238
- });
272
+ );
239
273
  }
240
274
 
241
275
  /**
@@ -248,20 +282,15 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
248
282
  */
249
283
  export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
250
284
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
251
- return fxEvaluateXPathToFirstNode(
252
- xpath,
253
- contextNode,
254
- null,
255
- {},
256
- {
257
- defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
258
- moduleImports: {
259
- xf: XFORMS_NAMESPACE_URI,
260
- },
261
- currentContext: { formElement },
262
- namespaceResolver,
285
+ const variablesInScope = getVariablesInScope(formElement);
286
+ return fxEvaluateXPathToFirstNode(xpath, contextNode, null, variablesInScope, {
287
+ defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
288
+ moduleImports: {
289
+ xf: XFORMS_NAMESPACE_URI,
263
290
  },
264
- );
291
+ currentContext: { formElement },
292
+ namespaceResolver,
293
+ });
265
294
  }
266
295
 
267
296
  /**
@@ -274,20 +303,16 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
274
303
  */
275
304
  export function evaluateXPathToNodes(xpath, contextNode, formElement) {
276
305
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
277
- return fxEvaluateXPathToNodes(
278
- xpath,
279
- contextNode,
280
- null,
281
- {},
282
- {
283
- currentContext: { formElement },
284
- functionNameResolver,
285
- moduleImports: {
286
- xf: XFORMS_NAMESPACE_URI,
287
- },
288
- namespaceResolver,
306
+ const variablesInScope = getVariablesInScope(formElement);
307
+
308
+ return fxEvaluateXPathToNodes(xpath, contextNode, null, variablesInScope, {
309
+ currentContext: { formElement },
310
+ functionNameResolver,
311
+ moduleImports: {
312
+ xf: XFORMS_NAMESPACE_URI,
289
313
  },
290
- );
314
+ namespaceResolver,
315
+ });
291
316
  }
292
317
 
293
318
  /**
@@ -300,20 +325,16 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
300
325
  */
301
326
  export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
302
327
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
303
- return fxEvaluateXPathToBoolean(
304
- xpath,
305
- contextNode,
306
- null,
307
- {},
308
- {
309
- currentContext: { formElement },
310
- functionNameResolver,
311
- moduleImports: {
312
- xf: XFORMS_NAMESPACE_URI,
313
- },
314
- namespaceResolver,
328
+ const variablesInScope = getVariablesInScope(formElement);
329
+
330
+ return fxEvaluateXPathToBoolean(xpath, contextNode, null, variablesInScope, {
331
+ currentContext: { formElement },
332
+ functionNameResolver,
333
+ moduleImports: {
334
+ xf: XFORMS_NAMESPACE_URI,
315
335
  },
316
- );
336
+ namespaceResolver,
337
+ });
317
338
  }
318
339
 
319
340
  /**
@@ -335,7 +356,38 @@ export function evaluateXPathToString(
335
356
  namespaceReferenceNode = formElement,
336
357
  ) {
337
358
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
338
- return fxEvaluateXPathToString(
359
+ const variablesInScope = getVariablesInScope(formElement);
360
+
361
+ return fxEvaluateXPathToString(xpath, contextNode, domFacade, variablesInScope, {
362
+ currentContext: { formElement },
363
+ functionNameResolver,
364
+ moduleImports: {
365
+ xf: XFORMS_NAMESPACE_URI,
366
+ },
367
+ namespaceResolver,
368
+ });
369
+ }
370
+
371
+ /**
372
+ * Evaluate an XPath to a set of strings
373
+ *
374
+ * @param {string} xpath The XPath to run
375
+ * @param {Node} contextNode The start of the XPath
376
+ * @param {Node} formElement The form element associated to the XPath
377
+ * @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
378
+ * access. This is used to determine dependencies between bind elements.
379
+ * @param {Node} formElement The element where the XPath is defined: used for namespace resolving
380
+ * @return {string}
381
+ */
382
+ export function evaluateXPathToStrings(
383
+ xpath,
384
+ contextNode,
385
+ formElement,
386
+ domFacade = null,
387
+ namespaceReferenceNode = formElement,
388
+ ) {
389
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
390
+ return fxEvaluateXPathToStrings(
339
391
  xpath,
340
392
  contextNode,
341
393
  domFacade,
@@ -371,20 +423,16 @@ export function evaluateXPathToNumber(
371
423
  namespaceReferenceNode = formElement,
372
424
  ) {
373
425
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
374
- return fxEvaluateXPathToNumber(
375
- xpath,
376
- contextNode,
377
- domFacade,
378
- {},
379
- {
380
- currentContext: { formElement },
381
- functionNameResolver,
382
- moduleImports: {
383
- xf: XFORMS_NAMESPACE_URI,
384
- },
385
- namespaceResolver,
426
+ const variablesInScope = getVariablesInScope(formElement);
427
+
428
+ return fxEvaluateXPathToNumber(xpath, contextNode, domFacade, variablesInScope, {
429
+ currentContext: { formElement },
430
+ functionNameResolver,
431
+ moduleImports: {
432
+ xf: XFORMS_NAMESPACE_URI,
386
433
  },
387
- );
434
+ namespaceResolver,
435
+ });
388
436
  }
389
437
 
390
438
  /**
@@ -487,6 +535,31 @@ export function resolveId(id, sourceObject, nodeName = null) {
487
535
  return null;
488
536
  }
489
537
 
538
+ const contextFunction = (dynamicContext, string) => {
539
+ const caller = dynamicContext.currentContext.formElement;
540
+ if (string) {
541
+ const instance = resolveId(string, caller);
542
+ if (instance) {
543
+ if (instance.nodeName === 'FX-REPEAT') {
544
+ const { nodeset } = instance;
545
+ for (let parent = caller; parent; parent = parent.parentNode) {
546
+ if (parent.parentNode === instance) {
547
+ const offset = Array.from(parent.parentNode.children).indexOf(parent);
548
+ return nodeset[offset];
549
+ }
550
+ }
551
+ }
552
+ return instance.nodeset;
553
+ }
554
+ }
555
+ const parent = XPathUtil.getParentBindingElement(caller);
556
+ const p = caller.nodeName;
557
+ // const p = dynamicContext.domFacade.getParentElement();
558
+
559
+ if (parent) return parent;
560
+ return caller.getInScopeContext();
561
+ };
562
+
490
563
  /**
491
564
  * @param id as string
492
565
  * @return instance data for given id serialized to string.
@@ -495,16 +568,18 @@ registerCustomXPathFunction(
495
568
  { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
496
569
  [],
497
570
  'item()?',
498
- (dynamicContext, string) => {
499
- const caller = dynamicContext.currentContext.formElement;
500
- const parent = XPathUtil.getParentBindingElement(caller);
501
- // const instance = resolveId('default', caller, 'fx-instance');
502
- const p = caller.nodeName;
503
- // const p = dynamicContext.domFacade.getParentElement();
504
-
505
- if (parent) return parent;
506
- return caller.getInScopeContext();
507
- },
571
+ contextFunction,
572
+ );
573
+
574
+ /**
575
+ * @param id as string
576
+ * @return instance data for given id serialized to string.
577
+ */
578
+ registerCustomXPathFunction(
579
+ { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
580
+ ['xs:string'],
581
+ 'item()?',
582
+ contextFunction,
508
583
  );
509
584
 
510
585
  /**
package/src/xpath-util.js CHANGED
@@ -88,4 +88,9 @@ export class XPathUtil {
88
88
  // ### cut-off root node ref
89
89
  return tmp1.substring(tmp1.indexOf('/'), tmp.length);
90
90
  }
91
+
92
+ static getBasePath(dep) {
93
+ const split = dep.split(':');
94
+ return split[0];
95
+ }
91
96
  }