@jinntec/fore 2.7.2 → 2.9.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.
- package/dist/fore-dev.js +2240 -1936
- package/dist/fore.js +10949 -14540
- package/package.json +1 -1
- package/resources/fore.css +3 -0
- package/src/functions/fx-functionlib.js +138 -12
- package/src/functions/mylib.js +13 -0
- package/src/functions/registerFunction.js +15 -2
- package/src/fx-bind.js +1 -1
- package/src/fx-fore.js +309 -143
- package/src/fx-instance.js +2 -2
- package/src/fx-submission.js +18 -0
- package/src/modelitem.js +5 -5
- package/src/tools/deprecation.md +1 -0
- package/src/tools/fx-action-log.js +2 -2
- package/src/ui/abstract-control.js +49 -16
- package/src/xpath-util.js +3 -3
- package/src/DataObserver.js +0 -181
package/package.json
CHANGED
package/resources/fore.css
CHANGED
|
@@ -1,10 +1,45 @@
|
|
|
1
1
|
import ForeElementMixin from '../ForeElementMixin.js';
|
|
2
2
|
import registerFunction from './registerFunction.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const LOCAL_FUNCTIONS_NS = 'http://www.w3.org/2005/xquery-local-functions';
|
|
5
|
+
|
|
6
|
+
// Global per-page cache to prevent registering the same library multiple times.
|
|
7
|
+
// Keyed by resolved URL + prefix + mode.
|
|
8
|
+
const _functionLibLoadCache = new Map();
|
|
9
|
+
|
|
10
|
+
function looksLikeModuleSrc(src) {
|
|
11
|
+
return /\.m?js($|\?)/i.test(src);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function applyPrefixToSignature(signature, prefix) {
|
|
15
|
+
if (!signature || !prefix) return signature;
|
|
16
|
+
|
|
17
|
+
const s = signature.trim();
|
|
18
|
+
const paren = s.indexOf('(');
|
|
19
|
+
if (paren < 0) return s;
|
|
20
|
+
|
|
21
|
+
const namePart = s.slice(0, paren).trim();
|
|
22
|
+
const rest = s.slice(paren);
|
|
23
|
+
|
|
24
|
+
const localName = namePart.includes(':') ? namePart.split(':').pop().trim() : namePart;
|
|
25
|
+
if (!localName) return s;
|
|
26
|
+
|
|
27
|
+
return `${prefix}:${localName}${rest}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeModuleExportToList(mod, src) {
|
|
31
|
+
const lib = mod.functions ?? mod.fxFunctions;
|
|
32
|
+
if (!lib) {
|
|
33
|
+
console.error(
|
|
34
|
+
`fx-functionlib: Module ${src} must export a named \`functions\` (or \`fxFunctions\`).`,
|
|
35
|
+
);
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(lib)) return lib;
|
|
39
|
+
if (typeof lib === 'object') return Object.values(lib);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
8
43
|
export class FxFunctionlib extends ForeElementMixin {
|
|
9
44
|
constructor() {
|
|
10
45
|
super();
|
|
@@ -24,20 +59,112 @@ export class FxFunctionlib extends ForeElementMixin {
|
|
|
24
59
|
this.style.display = 'none';
|
|
25
60
|
|
|
26
61
|
const src = this.getAttribute('src');
|
|
62
|
+
if (!src) {
|
|
63
|
+
console.error('fx-functionlib: Missing required @src.');
|
|
64
|
+
this._resolveLoading(undefined);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const prefix = (this.getAttribute('prefix') || '').trim();
|
|
69
|
+
|
|
70
|
+
const typeAttr = (this.getAttribute('type') || '').trim().toLowerCase();
|
|
71
|
+
const isModule = typeAttr === 'module' || (!typeAttr && looksLikeModuleSrc(src));
|
|
72
|
+
|
|
73
|
+
const resolvedUrl = new URL(src, this.baseURI).href;
|
|
74
|
+
|
|
75
|
+
if (prefix) this._ensurePrefixDeclared(prefix);
|
|
76
|
+
|
|
77
|
+
const mode = isModule ? 'module' : 'html';
|
|
78
|
+
const cacheKey = `${mode}|${resolvedUrl}|${prefix}`;
|
|
79
|
+
|
|
80
|
+
const existing = _functionLibLoadCache.get(cacheKey);
|
|
81
|
+
if (existing) {
|
|
82
|
+
try {
|
|
83
|
+
await existing;
|
|
84
|
+
} finally {
|
|
85
|
+
this._resolveLoading(undefined);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const loadPromise = (async () => {
|
|
91
|
+
if (isModule) {
|
|
92
|
+
await this._loadModuleLibrary(resolvedUrl, src, prefix);
|
|
93
|
+
} else {
|
|
94
|
+
await this._loadHtmlLibrary(resolvedUrl, src, prefix);
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
27
97
|
|
|
28
|
-
|
|
98
|
+
_functionLibLoadCache.set(cacheKey, loadPromise);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
await loadPromise;
|
|
102
|
+
} catch (e) {
|
|
103
|
+
_functionLibLoadCache.delete(cacheKey);
|
|
104
|
+
console.error(`fx-functionlib: Loading function library at ${src} failed.`, e);
|
|
105
|
+
} finally {
|
|
106
|
+
this._resolveLoading(undefined);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_ensurePrefixDeclared(prefix) {
|
|
111
|
+
const ownerForm =
|
|
112
|
+
(typeof this.getOwnerForm === 'function' && this.getOwnerForm()) || this.closest('fx-fore');
|
|
113
|
+
|
|
114
|
+
if (!ownerForm) return;
|
|
115
|
+
|
|
116
|
+
const attrName = `xmlns:${prefix}`;
|
|
117
|
+
if (!ownerForm.getAttribute(attrName)) {
|
|
118
|
+
ownerForm.setAttribute(attrName, LOCAL_FUNCTIONS_NS);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_register(functionObject, prefix) {
|
|
123
|
+
if (!functionObject || typeof functionObject.signature !== 'string') return;
|
|
124
|
+
|
|
125
|
+
// If prefix is given: register ONLY the prefixed signature (no unprefixed alias).
|
|
126
|
+
const sig = prefix
|
|
127
|
+
? applyPrefixToSignature(functionObject.signature, prefix)
|
|
128
|
+
: functionObject.signature;
|
|
129
|
+
|
|
130
|
+
registerFunction({ ...functionObject, signature: sig }, this);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async _loadModuleLibrary(resolvedUrl, src, prefix) {
|
|
134
|
+
const mod = await import(/* @vite-ignore */ resolvedUrl);
|
|
135
|
+
|
|
136
|
+
const items = normalizeModuleExportToList(mod, src);
|
|
137
|
+
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
if (typeof item === 'function') {
|
|
140
|
+
const { signature } = item;
|
|
141
|
+
if (typeof signature !== 'string' || !signature.trim()) continue;
|
|
142
|
+
|
|
143
|
+
this._register(
|
|
144
|
+
{
|
|
145
|
+
type: 'text/javascript',
|
|
146
|
+
signature: signature.trim(),
|
|
147
|
+
implementation: item,
|
|
148
|
+
},
|
|
149
|
+
prefix,
|
|
150
|
+
);
|
|
151
|
+
} else if (item && typeof item === 'object' && typeof item.signature === 'string') {
|
|
152
|
+
this._register(item, prefix);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async _loadHtmlLibrary(resolvedUrl, src, prefix) {
|
|
158
|
+
const result = await fetch(resolvedUrl);
|
|
29
159
|
if (!result.ok) {
|
|
30
160
|
console.error(`Loading function library at ${src} failed.`);
|
|
161
|
+
return;
|
|
31
162
|
}
|
|
32
163
|
|
|
33
164
|
const body = await result.text();
|
|
34
165
|
const document = new DOMParser().parseFromString(body, 'text/html');
|
|
35
166
|
|
|
36
|
-
/**
|
|
37
|
-
* @type {HTMLElement[]}
|
|
38
|
-
*/
|
|
39
167
|
const functions = Array.from(document.querySelectorAll('fx-function'));
|
|
40
|
-
// TODO: also recurse into new function libraries here?
|
|
41
168
|
for (const func of functions) {
|
|
42
169
|
const functionObject = {
|
|
43
170
|
type: func.getAttribute('type'),
|
|
@@ -45,12 +172,11 @@ export class FxFunctionlib extends ForeElementMixin {
|
|
|
45
172
|
functionBody: func.innerText,
|
|
46
173
|
};
|
|
47
174
|
|
|
48
|
-
|
|
175
|
+
this._register(functionObject, prefix);
|
|
49
176
|
}
|
|
50
|
-
this._resolveLoading(undefined);
|
|
51
177
|
}
|
|
52
178
|
}
|
|
53
179
|
|
|
54
180
|
if (!customElements.get('fx-functionlib')) {
|
|
55
181
|
customElements.define('fx-functionlib', FxFunctionlib);
|
|
56
|
-
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function myfunc(node) {
|
|
2
|
+
// simple: true if node is present
|
|
3
|
+
return !!node;
|
|
4
|
+
}
|
|
5
|
+
myfunc.signature = 'myfunc($node as item()?) as xs:boolean';
|
|
6
|
+
|
|
7
|
+
export function hasText(s) {
|
|
8
|
+
return s != null && String(s).trim() !== '';
|
|
9
|
+
}
|
|
10
|
+
hasText.signature = 'hasText($s as xs:string?) as xs:boolean';
|
|
11
|
+
|
|
12
|
+
// Named export is the default convention for fx-functionlib
|
|
13
|
+
export const functions = [myfunc, hasText];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createTypedValueFactory, registerCustomXPathFunction } from 'fontoxpath';
|
|
2
|
-
import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation';
|
|
2
|
+
import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @param functionObject {{signature: string, type: string|null, functionBody: string}}
|
|
@@ -53,6 +53,20 @@ export default function registerFunction(functionObject, formElement) {
|
|
|
53
53
|
|
|
54
54
|
switch (type) {
|
|
55
55
|
case 'text/javascript': {
|
|
56
|
+
// NEW: if a real JS function is provided (module libs), register it directly.
|
|
57
|
+
if (typeof functionObject.implementation === 'function') {
|
|
58
|
+
const impl = functionObject.implementation;
|
|
59
|
+
registerCustomXPathFunction(
|
|
60
|
+
functionIdentifier,
|
|
61
|
+
paramParts.map(paramPart => paramPart.variableType),
|
|
62
|
+
returnType || 'item()*',
|
|
63
|
+
(domFacade, ...values) =>
|
|
64
|
+
impl.apply(formElement.getInScopeContext(), [...values, formElement.getOwnerForm()]),
|
|
65
|
+
);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Existing behavior: compile from functionBody
|
|
56
70
|
// eslint-disable-next-line no-new-func
|
|
57
71
|
const fun = new Function(
|
|
58
72
|
'_domFacade',
|
|
@@ -69,7 +83,6 @@ export default function registerFunction(functionObject, formElement) {
|
|
|
69
83
|
);
|
|
70
84
|
break;
|
|
71
85
|
}
|
|
72
|
-
|
|
73
86
|
case 'text/xquf':
|
|
74
87
|
case 'text/xquery':
|
|
75
88
|
case 'text/xpath': {
|
package/src/fx-bind.js
CHANGED
|
@@ -94,7 +94,7 @@ export class FxBind extends ForeElementMixin {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// ✅ only the repeat item gets the _<opNum> suffix; children do not.
|
|
97
|
-
const basePath =
|
|
97
|
+
const basePath = getPath(node, instanceId);
|
|
98
98
|
const path = opNum ? `${basePath}_${opNum}` : basePath;
|
|
99
99
|
|
|
100
100
|
// const path = XPathUtil.getPath(node, instanceId);
|