@jinntec/fore 1.8.0 → 1.10.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.
@@ -2,7 +2,7 @@ import XfAbstractControl from './abstract-control.js';
2
2
  import {
3
3
  evaluateXPath,
4
4
  evaluateXPathToString,
5
- evaluateXPathToFirstNode, resolveId,
5
+ evaluateXPathToFirstNode,
6
6
  } from '../xpath-evaluation.js';
7
7
  import getInScopeContext from '../getInScopeContext.js';
8
8
  import {Fore} from '../fore.js';
@@ -43,6 +43,9 @@ export default class FxControl extends XfAbstractControl {
43
43
  static get properties() {
44
44
  return {
45
45
  ...XfAbstractControl.properties,
46
+ credentials:{
47
+ type: String
48
+ },
46
49
  initial: {
47
50
  type: Boolean
48
51
  }
@@ -74,6 +77,13 @@ export default class FxControl extends XfAbstractControl {
74
77
  }
75
78
  `;
76
79
 
80
+ this.credentials = this.hasAttribute('credentials')
81
+ ? this.getAttribute('credentials')
82
+ : 'same-origin';
83
+ if (!['same-origin', 'include', 'omit'].includes(this.credentials)) {
84
+ console.error(`fx-submission: the value of credentials is not valid. Expected 'same-origin', 'include' or 'omit' but got '${this.credentials}'`, this);
85
+ }
86
+
77
87
  this.shadowRoot.innerHTML = `
78
88
  <style>
79
89
  ${style}
@@ -369,7 +379,7 @@ export default class FxControl extends XfAbstractControl {
369
379
  }
370
380
 
371
381
  // ### when there's a url Fore is used as widget and will be loaded from external file
372
- if (this.url && !this.loaded) {
382
+ if (this.url && !this.loaded && this.modelItem.relevant) {
373
383
  // ### evaluate initial data if necessary
374
384
 
375
385
  if (this.initial) {
@@ -417,10 +427,8 @@ export default class FxControl extends XfAbstractControl {
417
427
  try {
418
428
  const response = await fetch(this.url, {
419
429
  method: 'GET',
420
- /*
421
- mode: 'cors',
422
- credentials: 'include',
423
- */
430
+ credentials: this.credentials,
431
+ mode: 'cors',
424
432
  headers: {
425
433
  'Content-Type': 'text/html',
426
434
  },
@@ -446,9 +454,12 @@ export default class FxControl extends XfAbstractControl {
446
454
  'model-construct-done',
447
455
  e => {
448
456
  const defaultInst = imported.querySelector('fx-instance');
449
- if (this.initialNode) {
457
+ if (this.initial) {
450
458
  const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
451
459
  // Note: Clone the input to prevent the inner fore from editing the outer node
460
+ // Also update the `initialNode` to make sure we have an up-to-date version
461
+ this.initialNode = evaluateXPathToFirstNode(this.initial, this.nodeset, this);
462
+
452
463
  doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
453
464
  defaultInst.setInstanceData(doc);
454
465
  }
@@ -39,6 +39,8 @@ export class FxDialog extends HTMLElement {
39
39
  this.classList.remove('show');
40
40
  });
41
41
  }
42
+ this.setAttribute('role','dialog');
43
+ this.setAttribute('aria-modal','false');
42
44
 
43
45
  /*
44
46
  this.addEventListener('transitionend', () => {
@@ -33,7 +33,10 @@ class FxGroup extends FxContainer {
33
33
  super();
34
34
  this.collapse = false;
35
35
  }
36
-
36
+ connectedCallback() {
37
+ super.connectedCallback();
38
+ this.setAttribute('role','group');
39
+ }
37
40
  render() {
38
41
  return `
39
42
  <slot></slot>
@@ -98,6 +98,7 @@ export class FxRepeatAttributes extends foreElementMixin(HTMLElement) {
98
98
  const refd = this.querySelector('[data-ref]');
99
99
  return refd.children;
100
100
  }
101
+
101
102
  async connectedCallback() {
102
103
  // console.log('connectedCallback',this);
103
104
  // this.display = window.getComputedStyle(this, null).getPropertyValue("display");
@@ -134,7 +135,9 @@ export class FxRepeatAttributes extends foreElementMixin(HTMLElement) {
134
135
  if (mutations[0].type === 'childList') {
135
136
  const added = mutations[0].addedNodes[0];
136
137
  if (added) {
137
- const path = XPathUtil.getPath(added);
138
+ const instance = XPathUtil.resolveInstance(this);
139
+
140
+ const path = XPathUtil.getPath(added, instance);
138
141
  // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
139
142
  // this.index = index;
140
143
  // const prev = mutations[0].previousSibling.previousElementSibling;
@@ -5,6 +5,7 @@ import { foreElementMixin } from '../ForeElementMixin.js';
5
5
  import { evaluateXPath } from '../xpath-evaluation.js';
6
6
  import getInScopeContext from '../getInScopeContext.js';
7
7
  import { XPathUtil } from '../xpath-util.js';
8
+ import {DependencyNotifyingDomFacade} from '../DependencyNotifyingDomFacade';
8
9
 
9
10
  /**
10
11
  * `fx-repeat`
@@ -131,7 +132,8 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
131
132
  if (mutations[0].type === 'childList') {
132
133
  const added = mutations[0].addedNodes[0];
133
134
  if (added) {
134
- const path = XPathUtil.getPath(added);
135
+ const instance = XPathUtil.resolveInstance(this, this.ref);
136
+ const path = XPathUtil.getPath(added, instance);
135
137
  // console.log('path mutated', path);
136
138
  // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
137
139
  // this.index = index;
@@ -205,7 +207,13 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
205
207
  });
206
208
  }
207
209
 
208
- const rawNodeset = evaluateXPath(this.ref, inscope, this);
210
+ this.touchedPaths = new Set();
211
+ const instance = XPathUtil.resolveInstance(this, this.ref);
212
+ const depTrackDomfacade = new DependencyNotifyingDomFacade((node) => {
213
+ this.touchedPaths.add(XPathUtil.getPath(node, instance));
214
+ });
215
+ const rawNodeset = evaluateXPath(this.ref, inscope, this, {}, {}, depTrackDomfacade );
216
+ console.log('Touched!', this.ref, [...this.touchedPaths].join(', '));
209
217
  if (rawNodeset.length === 1 && Array.isArray(rawNodeset[0])) {
210
218
  // This XPath likely returned an XPath array. Just collapse to that array
211
219
  this.nodeset = rawNodeset[0];
@@ -220,6 +228,7 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
220
228
  if (!this.inited) this.init();
221
229
  // console.time('repeat-refresh', this);
222
230
  this._evalNodeset();
231
+
223
232
  // console.log('repeat refresh nodeset ', this.nodeset);
224
233
  // console.log('repeatCount', this.repeatCount);
225
234
 
@@ -58,7 +58,9 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
58
58
  this.shadowRoot.innerHTML = `
59
59
  ${html}
60
60
  `;
61
- this.getOwnerForm().registerLazyElement(this);
61
+ this.getOwnerForm().registerLazyElement(this);
62
+
63
+ this.ref = `${this.parentNode.ref}`;
62
64
  }
63
65
 
64
66
  disconnectedCallback() {
@@ -91,13 +93,13 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
91
93
  if (this.modelItem && !this.modelItem.relevant) {
92
94
  // await Fore.fadeOutElement(this)
93
95
  // this.style.display = 'none';
94
- this.classList.add('nonrelevant');
96
+ this.style.display = 'none';
95
97
  } else {
96
98
  // if(this.hasAttribute('repeat-index')){
97
99
  // Fore.fadeInElement(this);
98
100
  // }
99
101
  // this.style.display = this.display;
100
- this.classList.remove('nonrelevant');
102
+ this.style.display = 'block';
101
103
  }
102
104
 
103
105
  /*
@@ -25,7 +25,9 @@ export class FxTrigger extends XfAbstractControl {
25
25
  slot.addEventListener('slotchange', () => {
26
26
  const elements = slot.assignedElements({ flatten: true });
27
27
  elements[0].setAttribute('tabindex', '0');
28
- elements[0].setAttribute('role', 'button');
28
+ if(elements[0].nodeName !== 'BUTTON'){
29
+ elements[0].setAttribute('role', 'button');
30
+ }
29
31
 
30
32
  const element = elements[0];
31
33
 
@@ -341,7 +341,18 @@ function functionNameResolver({prefix, localName}, _arity) {
341
341
  case 'instance':
342
342
  case 'log':
343
343
  case 'parse':
344
+ case 'local-date':
345
+ case 'local-dateTime':
344
346
  case 'logtree':
347
+ case 'uri':
348
+ case 'uri-fragment':
349
+ case 'uri-host':
350
+ case 'uri-param':
351
+ case 'uri-path':
352
+ case 'uri-port':
353
+ case 'uri-query':
354
+ case 'uri-scheme':
355
+ case 'uri-scheme-specific-part':
345
356
  return {namespaceURI: XFORMS_NAMESPACE_URI, localName};
346
357
  default:
347
358
  if (prefix === '' && globallyDeclaredFunctionLocalNames.includes(localName)) {
@@ -407,14 +418,14 @@ function getVariablesInScope(formElement) {
407
418
  * @param {Node} contextNode The start of the XPath
408
419
  * @param {{parentNode}|ForeElementMixin} formElement The form element associated to the XPath
409
420
  */
410
- export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options={}) {
421
+ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options={}, domFacade = null) {
411
422
  const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
412
423
  const variablesInScope = getVariablesInScope(formElement);
413
424
 
414
425
  return fxEvaluateXPath(
415
426
  xpath,
416
427
  contextNode,
417
- null,
428
+ domFacade,
418
429
  {...variablesInScope, ...variables},
419
430
  fxEvaluateXPath.ALL_RESULTS_TYPE,
420
431
  {
@@ -834,8 +845,8 @@ const instance = (dynamicContext, string) => {
834
845
  );
835
846
 
836
847
  const inst = string
837
- ? formElement.querySelector(`#${string}`)
838
- : formElement.querySelector(`fx-instance`);
848
+ ? formElement.getModel().getInstance(string)
849
+ : formElement.getModel().getDefaultInstance();
839
850
 
840
851
  /*
841
852
  const inst = string
@@ -980,3 +991,89 @@ registerCustomXPathFunction(
980
991
  'xs:string?',
981
992
  (dynamicContext, string) => btoa(string),
982
993
  );
994
+
995
+ registerCustomXPathFunction(
996
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-date'},
997
+ [],
998
+ 'xs:string?',
999
+ (dynamicContext, string) => new Date().toLocaleDateString(),
1000
+ );
1001
+ registerCustomXPathFunction(
1002
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-dateTime'},
1003
+ [],
1004
+ 'xs:string?',
1005
+ (dynamicContext, string) => {
1006
+ return new Date().toLocaleString();
1007
+ },
1008
+ );
1009
+ registerCustomXPathFunction(
1010
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri'},
1011
+ [],
1012
+ 'xs:string?',
1013
+ (dynamicContext, string) => window.location.href,
1014
+ );
1015
+ registerCustomXPathFunction(
1016
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-fragment'},
1017
+ [],
1018
+ 'xs:string?',
1019
+ (dynamicContext, arg) => window.location.hash,
1020
+ );
1021
+ registerCustomXPathFunction(
1022
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-host'},
1023
+ [],
1024
+ 'xs:string?',
1025
+ (dynamicContext, arg) => window.location.host,
1026
+ );
1027
+ registerCustomXPathFunction(
1028
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-query'},
1029
+ [],
1030
+ 'xs:string?',
1031
+ (dynamicContext, arg) => window.location.search,
1032
+ );
1033
+ registerCustomXPathFunction(
1034
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-path'},
1035
+ [],
1036
+ 'xs:string?',
1037
+ (dynamicContext, arg) => {
1038
+ return new URL(window.location.href).pathname;
1039
+ },
1040
+ );
1041
+ registerCustomXPathFunction(
1042
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-port'},
1043
+ [],
1044
+ 'xs:string?',
1045
+ (dynamicContext, arg) => {
1046
+ return window.location.port;
1047
+ },
1048
+ );
1049
+ registerCustomXPathFunction(
1050
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-param'},
1051
+ ['xs:string?'],
1052
+ 'xs:string?',
1053
+ (dynamicContext, arg) => {
1054
+ if (!arg) return null;
1055
+
1056
+ const search = window.location.search;
1057
+ const urlparams = new URLSearchParams(search);
1058
+ const param = urlparams.get(arg);
1059
+ return param ? param : '';
1060
+
1061
+ },
1062
+ );
1063
+ registerCustomXPathFunction(
1064
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-scheme'},
1065
+ [],
1066
+ 'xs:string?',
1067
+ (dynamicContext, arg) => {
1068
+ return new URL(window.location.href).protocol;
1069
+ },
1070
+ );
1071
+ registerCustomXPathFunction(
1072
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-scheme-specific-part'},
1073
+ [],
1074
+ 'xs:string?',
1075
+ (dynamicContext, arg) => {
1076
+ const uri = window.location.href;
1077
+ return uri.substring(uri.indexOf(':') + 1, uri.length);
1078
+ },
1079
+ );
package/src/xpath-util.js CHANGED
@@ -115,29 +115,26 @@ export class XPathUtil {
115
115
  return null;
116
116
  }
117
117
 
118
- static resolveInstance(boundElement){
119
- const instanceId = XPathUtil.getInstanceId(boundElement.getAttribute('ref'));
118
+ static resolveInstance(boundElement, path){
119
+ let instanceId = XPathUtil.getInstanceId(path);
120
+ if (!instanceId) {
121
+ instanceId = XPathUtil.getInstanceId(boundElement.getAttribute('ref'));
122
+ }
120
123
  if(instanceId !== null){
121
124
  return instanceId;
122
125
  }
123
126
 
124
127
  const parentBinding = XPathUtil.getParentBindingElement(boundElement);
125
128
  if(parentBinding){
126
- return this.resolveInstance(parentBinding);
129
+ return this.resolveInstance(parentBinding, path);
127
130
  }
128
131
  return 'default';
129
132
  }
130
133
 
131
- // todo: certainly not ideal to rely on duplicating instance id on instance document - better way later ;)
132
- static getPath(node) {
134
+ static getPath(node, instanceId) {
133
135
  const path = fx.evaluateXPathToString('path()', node);
134
- /*
135
- const instanceId = node.ownerDocument.firstElementChild.getAttribute('id');
136
- if (instanceId !== null && instanceId !== 'default') {
137
- return `#${instanceId}${XPathUtil.shortenPath(path)}`;
138
- }
139
- */
140
- return XPathUtil.shortenPath(path);
136
+ // Path is like `$default/x/y`
137
+ return `$${instanceId}${XPathUtil.shortenPath(path)}`;
141
138
  }
142
139
 
143
140
  static shortenPath(path) {