@jinntec/fore 1.7.0 → 1.7.2

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.
package/index.js CHANGED
@@ -30,7 +30,7 @@ import './src/tools/fx-devtools.js';
30
30
  import './src/tools/fx-dom-inspector.js';
31
31
  import './src/lab/fore-component.js';
32
32
  import './src/tools/fx-json-instance.js';
33
- import './src/tools/fx-minimap.js';
33
+ // import './src/tools/fx-minimap.js';
34
34
 
35
35
 
36
36
  // import './src/ui/fx-checkbox-group.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinntec/fore",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Fore - declarative user interfaces in plain HTML",
5
5
  "module": "./index.js",
6
6
  "publishConfig": {
@@ -60,6 +60,7 @@
60
60
  "rollup": "^2.75.4",
61
61
  "rollup-plugin-babel": "^4.4.0",
62
62
  "rollup-plugin-minify-html-literals": "^1.2.6",
63
+ "rollup-plugin-version-injector": "^1.3.3",
63
64
  "typescript": "^4.7.2",
64
65
  "web-component-analyzer": "^1.1.6",
65
66
  "xmlserializer": "^0.6.1"
@@ -187,7 +187,7 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
187
187
  // console.log('execute', this.event);
188
188
 
189
189
 
190
- if (e && e.target.nodeType !== Node.DOCUMENT_NODE ){
190
+ if (e && e.target.nodeType !== Node.DOCUMENT_NODE && e.target !== window ){
191
191
  /*
192
192
  ### ignore event if there's a parent fore and the current element is NOT part of it. This avoids
193
193
  ### an event to fire twice on an inner one and the surrounding one(s).
@@ -122,7 +122,7 @@ class FxLoad extends AbstractAction {
122
122
  );
123
123
  return;
124
124
  }
125
- this.url = this.evaluateAttributeTemplateExpression(this.url,this);
125
+ const resolvedUrl = this.evaluateAttributeTemplateExpression(this.url,this);
126
126
  if (this.attachTo === '_blank') {
127
127
  window.open(this.url);
128
128
  }
@@ -132,7 +132,7 @@ class FxLoad extends AbstractAction {
132
132
  }
133
133
 
134
134
  try {
135
- const response = await fetch(this.url, {
135
+ const response = await fetch(resolvedUrl, {
136
136
  method: 'GET',
137
137
  mode: 'cors',
138
138
  credentials: 'same-origin',
package/src/fore.js CHANGED
@@ -195,6 +195,7 @@ export class Fore {
195
195
  Array.from(children).forEach(element => {
196
196
  if (element.nodeName.toUpperCase() === 'FX-FORE') {
197
197
  resolve('done');
198
+ return;
198
199
  }
199
200
  if (Fore.isUiElement(element.nodeName) && typeof element.refresh === 'function') {
200
201
  // console.log('refreshing', element, element?.ref);
@@ -1,4 +1,4 @@
1
- import { registerCustomXPathFunction } from 'fontoxpath';
1
+ import { registerCustomXPathFunction, createTypedValueFactory } from 'fontoxpath';
2
2
  import { foreElementMixin } from '../ForeElementMixin.js';
3
3
  import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation.js';
4
4
 
@@ -86,6 +86,7 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
86
86
  case 'text/xquf':
87
87
  case 'text/xquery':
88
88
  case 'text/xpath': {
89
+ const typedValueFactories = paramParts.map(param => createTypedValueFactory(param.variableType));
89
90
  const language = type === 'text/xpath' ?
90
91
  'XPath3.1' : type === 'text/xquery' ?
91
92
  'XQuery3.1' : 'XQueryUpdate3.1';
@@ -94,11 +95,12 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
94
95
  this.functionBody,
95
96
  this.getInScopeContext(),
96
97
  this.getOwnerForm(),
97
- paramParts.reduce((variablesByName, paramPart, i) => {
98
- variablesByName[paramPart.variableName.replace('$', '')] = args[i];
98
+ paramParts.reduce((variablesByName, paramPart, i) => {
99
+ // Because we know the XPath type here (from the function declaration) we do not have to depend on the implicit typings
100
+ variablesByName[paramPart.variableName.replace('$', '')] = typedValueFactories[i](args[i]);
99
101
  return variablesByName;
100
102
  }, {}),
101
- {language}
103
+ {language}
102
104
  );
103
105
  registerCustomXPathFunction(
104
106
  functionIdentifier,
package/src/fx-fore.js CHANGED
@@ -29,6 +29,12 @@ export class FxFore extends HTMLElement {
29
29
 
30
30
  static get properties() {
31
31
  return {
32
+ /**
33
+ * merge-partial
34
+ */
35
+ mergePartial:{
36
+ type: Boolean
37
+ },
32
38
  /**
33
39
  * Setting this marker attribute will refresh the UI in a lazy fashion just updating elements being
34
40
  * in viewport.
@@ -49,6 +55,9 @@ export class FxFore extends HTMLElement {
49
55
  */
50
56
  validateOn: {
51
57
  type: String
58
+ },
59
+ version: {
60
+ type: String
52
61
  }
53
62
  };
54
63
  }
@@ -63,6 +72,8 @@ export class FxFore extends HTMLElement {
63
72
  */
64
73
  constructor() {
65
74
  super();
75
+ this.version = '[VI]Version: {version} - built on {date}[/VI]';
76
+
66
77
  this.model = {};
67
78
  this.inited=false;
68
79
  // this.addEventListener('model-construct-done', this._handleModelConstructDone);
@@ -189,6 +200,8 @@ export class FxFore extends HTMLElement {
189
200
  this.someInstanceDataStructureChanged = false;
190
201
  this.repeatsFromAttributesCreated = false;
191
202
  this.validateOn = this.hasAttribute('validate-on') ? this.getAttribute('validate-on'):'update';
203
+ // this.mergePartial = this.hasAttribute('merge-partial')? true:false;
204
+ this.mergePartial = false;
192
205
  }
193
206
 
194
207
  connectedCallback() {
@@ -520,6 +533,13 @@ export class FxFore extends HTMLElement {
520
533
  storing expressions and their nodes for re-evaluation
521
534
  */
522
535
  Array.from(tmplExpressions).forEach(node => {
536
+ const ele = node.nodeType === Node.ATTRIBUTE_NODE ?
537
+ node.ownerElement :
538
+ node.parentNode;
539
+ if (ele.closest('fx-fore') !== this) {
540
+ // We found something in a sub-fore. Act like it's not there
541
+ return;
542
+ }
523
543
  if (this.storedTemplateExpressionByNode.has(node)) {
524
544
  // If the node is already known, do not process it twice
525
545
  return;
@@ -52,6 +52,7 @@ export class FxInstance extends HTMLElement {
52
52
  this.model = this.parentNode;
53
53
  this.attachShadow({ mode: 'open' });
54
54
  this.originalInstance = null;
55
+ this.partialInstance = null;
55
56
  }
56
57
 
57
58
  connectedCallback() {
@@ -92,6 +93,7 @@ export class FxInstance extends HTMLElement {
92
93
  </style>
93
94
  ${html}
94
95
  `;
96
+ this.partialInstance = {};
95
97
  }
96
98
 
97
99
  /**
@@ -149,11 +151,13 @@ export class FxInstance extends HTMLElement {
149
151
  * @returns {Document|T|any|Element}
150
152
  */
151
153
  getDefaultContext() {
152
- // console.log('getDefaultContext ', this.instanceData.firstElementChild);
154
+ // Note: use the getter here: it might provide us with stubbed data if anything async is racing,
155
+ // such as an @src attribute
156
+ const instanceData = this.getInstanceData();
153
157
  if (this.type === 'xml') {
154
- return this.instanceData.firstElementChild;
158
+ return instanceData.firstElementChild;
155
159
  }
156
- return this.instanceData;
160
+ return instanceData;
157
161
  }
158
162
 
159
163
  /**
package/src/fx-model.js CHANGED
@@ -435,12 +435,11 @@ export class FxModel extends HTMLElement {
435
435
  }
436
436
 
437
437
  getDefaultInstance() {
438
- return this?.instances[0];
438
+ return this.instances[0];
439
439
  }
440
440
 
441
441
  getDefaultInstanceData() {
442
- console.log('default instance data ', this.instances[0].instanceData);
443
- return this.instances[0].instanceData;
442
+ return this.instances[0].getInstanceData();
444
443
  }
445
444
 
446
445
  getInstance(id) {
@@ -323,8 +323,35 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
323
323
  }
324
324
  */
325
325
 
326
+ const targetInstance = this._getTargetInstance();
327
+
328
+ /*
329
+ if(this.replace === 'merge'){
330
+ if (targetInstance && targetInstance.type === 'xml') {
331
+ targetInstance.partialInstance = data;
332
+
333
+ const merged = this._mergeXML(targetInstance.instanceData,targetInstance.partialInstance);
334
+ console.log('merged', merged);
335
+
336
+ targetInstance.instanceData = merged;
337
+ console.log('merging partial instance',targetInstance.partialInstance)
338
+ /!*
339
+ targetInstance.instanceData not touched here as we want to keep the default instance unmodified as the full template for the UI.
340
+ *!/
341
+
342
+ // Skip any refreshes if the model is not yet inited#
343
+ // duplicate from replace='instance'
344
+ // if (this.model.inited) {
345
+ this.model.updateModel(); // force update
346
+ const owner = this.getOwnerForm();
347
+ // owner.mergePartial = true;
348
+ owner.refresh(true);
349
+ // }
350
+ }
351
+ }
352
+ */
353
+
326
354
  if (this.replace === 'instance') {
327
- const targetInstance = this._getTargetInstance();
328
355
  if (targetInstance) {
329
356
  if (this.targetref) {
330
357
  const [theTarget] = evaluateXPath(
@@ -388,6 +415,81 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
388
415
  }
389
416
  }
390
417
 
418
+
419
+ /*
420
+ _mergeXML(xml1, xml2) {
421
+ const parser = new DOMParser();
422
+ const serializer = new XMLSerializer();
423
+
424
+ // const doc1 = parser.parseFromString(xml1, 'text/xml');
425
+ // const doc2 = parser.parseFromString(xml2, 'text/xml');
426
+
427
+ this.mergeNodes(xml1.documentElement, xml2.documentElement);
428
+
429
+ // return serializer.serializeToString(xml1);
430
+ return xml1;
431
+ }
432
+ */
433
+
434
+ /*
435
+ _mergeNodes(node1, node2) {
436
+ const childNodes1 = node1.childNodes;
437
+ const childNodes2 = node2.childNodes;
438
+
439
+ for (let i = 0; i < childNodes2.length; i++) {
440
+ const child2 = childNodes2[i];
441
+ let nodeMerged = false;
442
+
443
+ if (child2.nodeType === 1) { // Element Node
444
+ for (let j = 0; j < childNodes1.length; j++) {
445
+ const child1 = childNodes1[j];
446
+ if (child1.nodeType === 1 && child1.tagName === child2.tagName) {
447
+ this._mergeNodes(child1, child2);
448
+ nodeMerged = true;
449
+ break;
450
+ }
451
+ }
452
+ }
453
+
454
+ if (!nodeMerged) {
455
+ const clonedNode = child2.cloneNode(true);
456
+ node1.appendChild(clonedNode);
457
+ }
458
+ }
459
+ }
460
+ */
461
+
462
+ /*
463
+ mergeNodes(node1, node2) {
464
+ // Overwrite attributes in node1 with values from node2
465
+ for (const { name, value } of node2.attributes) {
466
+ node1.setAttribute(name, value);
467
+ }
468
+
469
+ const childNodes1 = Array.from(node1.childNodes);
470
+ const childNodes2 = Array.from(node2.childNodes);
471
+
472
+ // Append all child nodes from node2 to node1
473
+ childNodes2.forEach(child2 => {
474
+ if (child2.nodeType === 1) {
475
+ // If it's an element node, check if a matching element exists in node1
476
+ const matchingElement = childNodes1.find(
477
+ child1 => child1.nodeType === 1 && child1.tagName === child2.tagName
478
+ );
479
+ if (matchingElement) {
480
+ this.mergeNodes(matchingElement, child2); // Recursively merge matching elements
481
+ } else {
482
+ const clonedNode = child2.cloneNode(true);
483
+ node1.appendChild(clonedNode);
484
+ }
485
+ } else {
486
+ // For text nodes, simply append them to node1
487
+ const clonedNode = child2.cloneNode(true);
488
+ node1.appendChild(clonedNode);
489
+ }
490
+ });
491
+ }
492
+ */
391
493
  /**
392
494
  * select relevant nodes
393
495
  *
@@ -55,7 +55,15 @@ function _getInitialContext(node, ref) {
55
55
  }
56
56
 
57
57
  export default function getInScopeContext(node, ref) {
58
+ // console.log('getInScopeContext', ref, node);
59
+
60
+
58
61
  const parentElement = _getElement(node);
62
+ // console.log('getInScopeContext parent', parentElement);
63
+
64
+ if(parentElement.closest('fx-fore').mergePartial){
65
+ console.log('mergePartial mode')
66
+ }
59
67
 
60
68
  if(parentElement.nodeName === 'FX-FORE'){
61
69
  return parentElement.getModel().getDefaultInstance().getDefaultContext();
@@ -459,10 +459,9 @@ export class FxActionLog extends HTMLElement {
459
459
  */
460
460
  _logDetails(e) {
461
461
  const eventType = e.type;
462
- // console.log('>>>> event type', type)
463
462
  const path = XPathUtil.getPath(e.target);
463
+ // console.log('>>>> _logDetails', path);
464
464
  const cut = path.substring(path.indexOf('/fx-fore'), path.length);
465
- ;
466
465
  const xpath = `/${cut}`;
467
466
  const short = cut.replaceAll('fx-', '');
468
467
 
@@ -400,7 +400,7 @@ export default class FxControl extends XfAbstractControl {
400
400
  }
401
401
 
402
402
  /**
403
- * loads an external Fore from an HTML file given by `url` attribute.
403
+ * loads an external Fore from an HTML file given by `url` attribute and embed it as child of this control.
404
404
  *
405
405
  * Will look for the `<fx-fore>` element within the returned HTML file and return that element.
406
406
  *
@@ -459,6 +459,10 @@ export default class FxControl extends XfAbstractControl {
459
459
  );
460
460
 
461
461
  const dummy = this.querySelector('input');
462
+ /*
463
+ todo: the mechanism to import constructed stylesheets as in fore-component is still missing here.
464
+ There no way yet to specify CSS for a embedded fx-fore in shadowDOM.
465
+ */
462
466
  if (this.hasAttribute('shadow')) {
463
467
  dummy.parentNode.removeChild(dummy);
464
468
  this.shadowRoot.appendChild(imported);
@@ -418,6 +418,7 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, o
418
418
  {...variablesInScope, ...variables},
419
419
  fxEvaluateXPath.ALL_RESULTS_TYPE,
420
420
  {
421
+ debug: true,
421
422
  currentContext: {formElement, variables},
422
423
  moduleImports: {
423
424
  xf: XFORMS_NAMESPACE_URI,
@@ -595,26 +596,25 @@ export function evaluateXPathToNumber(
595
596
 
596
597
  const contextFunction = (dynamicContext, string) => {
597
598
  const caller = dynamicContext.currentContext.formElement;
599
+ let instance = null;
598
600
  if (string) {
599
- const instance = resolveId(string, caller);
600
- if (instance) {
601
- if (instance.nodeName === 'FX-REPEAT') {
602
- const {nodeset} = instance;
603
- for (let parent = caller; parent; parent = parent.parentNode) {
604
- if (parent.parentNode === instance) {
605
- const offset = Array.from(parent.parentNode.children).indexOf(parent);
606
- return nodeset[offset];
607
- }
601
+ instance = resolveId(string, caller);
602
+ } else {
603
+ instance = XPathUtil.getParentBindingElement(caller);
604
+ }
605
+ if (instance) {
606
+ if (instance.nodeName === 'FX-REPEAT') {
607
+ const {nodeset} = instance;
608
+ for (let parent = caller; parent; parent = parent.parentNode) {
609
+ if (parent.parentNode === instance) {
610
+ const offset = Array.from(parent.parentNode.children).indexOf(parent);
611
+ return nodeset[offset];
608
612
  }
609
613
  }
610
- return instance.nodeset;
611
614
  }
615
+ return instance.nodeset;
612
616
  }
613
- const parent = XPathUtil.getParentBindingElement(caller);
614
- // const p = caller.nodeName;
615
- // const p = dynamicContext.domFacade.getParentElement();
616
617
 
617
- if (parent) return parent.nodeset;
618
618
  return caller.getInScopeContext();
619
619
  };
620
620
 
@@ -844,7 +844,12 @@ const instance = (dynamicContext, string) => {
844
844
  */
845
845
 
846
846
  if (inst) {
847
- return inst.getDefaultContext();
847
+ const context = inst.getDefaultContext();
848
+ if (!context) {
849
+ debugger;
850
+ return null;
851
+ }
852
+ return context;
848
853
  }
849
854
  return null;
850
855
  };