@jinntec/fore 2.4.1 → 2.5.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 +9 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +9 -2
- package/dist/fore.js.map +1 -1
- package/index.js +2 -0
- package/package.json +2 -2
- package/resources/fore.css +1 -1
- package/src/ForeElementMixin.js +12 -6
- package/src/actions/abstract-action.js +33 -0
- package/src/actions/fx-delete.js +2 -2
- package/src/actions/fx-insert.js +2 -0
- package/src/actions/fx-send.js +0 -2
- package/src/actions/fx-setvalue.js +5 -1
- package/src/fore.js +2 -1
- package/src/fx-bind.js +3 -2
- package/src/fx-fore.js +280 -123
- package/src/fx-instance.js +3 -2
- package/src/fx-model.js +64 -35
- package/src/fx-submission.js +48 -19
- package/src/getInScopeContext.js +1 -1
- package/src/modelitem.js +7 -2
- package/src/ui/abstract-control.js +6 -4
- package/src/ui/fx-container.js +3 -2
- package/src/ui/fx-control.js +3 -24
- package/src/ui/fx-repeat.js +9 -0
- package/src/ui/fx-repeatitem.js +3 -0
- package/src/ui/fx-select.js +89 -0
- package/src/ui/fx-switch.js +4 -0
- package/src/ui/fx-upload.js +304 -0
- package/src/xpath-util.js +119 -5
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import XfAbstractControl from './abstract-control.js';
|
|
2
|
+
import {
|
|
3
|
+
evaluateXPath,
|
|
4
|
+
evaluateXPathToString,
|
|
5
|
+
evaluateXPathToFirstNode,
|
|
6
|
+
} from '../xpath-evaluation.js';
|
|
7
|
+
import getInScopeContext from '../getInScopeContext.js';
|
|
8
|
+
import { Fore } from '../fore.js';
|
|
9
|
+
import { ModelItem } from '../modelitem.js';
|
|
10
|
+
import { debounce } from '../events.js';
|
|
11
|
+
import { FxModel } from '../fx-model.js';
|
|
12
|
+
|
|
13
|
+
const WIDGETCLASS = 'widget';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `fx-upload` allows to embed uploaded content into XML.
|
|
17
|
+
*
|
|
18
|
+
* @customElement
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export default class FxUpload extends XfAbstractControl {
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
this.inited = false;
|
|
26
|
+
this.attachShadow({ mode: 'open' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static get properties() {
|
|
30
|
+
return {
|
|
31
|
+
...XfAbstractControl.properties,
|
|
32
|
+
accept: {
|
|
33
|
+
type: String,
|
|
34
|
+
},
|
|
35
|
+
fileNameExpr: {
|
|
36
|
+
type: String,
|
|
37
|
+
},
|
|
38
|
+
mimetypeExpr: {
|
|
39
|
+
type: String,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
connectedCallback() {
|
|
44
|
+
|
|
45
|
+
this.updateEvent = 'change';
|
|
46
|
+
this.accept = this.hasAttribute('accept') ? this.getAttribute('accept'):'';
|
|
47
|
+
this.label = this.hasAttribute('label') ? this.getAttribute('label') : null;
|
|
48
|
+
const style = `
|
|
49
|
+
:host{
|
|
50
|
+
display:inline-block;
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
this.shadowRoot.innerHTML = `
|
|
55
|
+
<style>
|
|
56
|
+
${style}
|
|
57
|
+
</style>
|
|
58
|
+
${this.renderHTML(this.ref)}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
this.widget = this.getWidget();
|
|
62
|
+
|
|
63
|
+
this.addEventListener('mousedown', e => {
|
|
64
|
+
// ### prevent mousedown events on all control content that is not the widget or within the widget
|
|
65
|
+
if (!Fore.isWidget(e.target) && !e.target?.classList.contains('fx-hint')) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
// e.stopImmediatePropagation();
|
|
68
|
+
}
|
|
69
|
+
this.widget.focus();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// console.log('widget ', this.widget);
|
|
73
|
+
let listenOn = this.widget; // default: usually listening on widget
|
|
74
|
+
|
|
75
|
+
// ### convenience marker event
|
|
76
|
+
if (this.debounceDelay) {
|
|
77
|
+
listenOn.addEventListener(
|
|
78
|
+
this.updateEvent,
|
|
79
|
+
debounce(
|
|
80
|
+
this,
|
|
81
|
+
() => {
|
|
82
|
+
// console.log('eventlistener ', this.updateEvent);
|
|
83
|
+
// console.info('handling Event:', event.type, listenOn);
|
|
84
|
+
this._importUploadedContent();
|
|
85
|
+
},
|
|
86
|
+
this.debounceDelay,
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
listenOn.addEventListener(this.updateEvent, async event => {
|
|
91
|
+
this._importUploadedContent(event);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
this.boundInitialized = false;
|
|
95
|
+
this.fileNameExpr = this.getAttribute('filename');
|
|
96
|
+
this.mimetypeExpr = this.getAttribute('mimetype');
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async _importUploadedContent(event) {
|
|
101
|
+
console.log('_importUploadedContent',event);
|
|
102
|
+
const file = event.target.files[0];
|
|
103
|
+
|
|
104
|
+
this.evalInContext();
|
|
105
|
+
// update file ref
|
|
106
|
+
const fileNode = evaluateXPathToFirstNode(this.fileNameExpr,this.nodeset,this.getOwnerForm());
|
|
107
|
+
if(fileNode){
|
|
108
|
+
this.fileName = fileNode.nodeValue = file.name;
|
|
109
|
+
}
|
|
110
|
+
this.mimetype = file.type;
|
|
111
|
+
|
|
112
|
+
// update mediatype
|
|
113
|
+
const mimetypeNode = evaluateXPathToFirstNode(this.mimetypeExpr,this.nodeset, this.getOwnerForm());
|
|
114
|
+
if(mimetypeNode){
|
|
115
|
+
mimetypeNode.nodeValue = this.mimetype;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let content = await this._readFile(file);
|
|
119
|
+
// const setval = this.shadowRoot.getElementById('setvalue');
|
|
120
|
+
console.log('content',content);
|
|
121
|
+
if(file.type.endsWith('xml')){
|
|
122
|
+
const uploadedXML = new DOMParser().parseFromString(content, 'application/xml');
|
|
123
|
+
content = uploadedXML.firstElementChild;
|
|
124
|
+
}
|
|
125
|
+
this.setValue(content);
|
|
126
|
+
// // this.value=content;
|
|
127
|
+
// return content;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async _readFile(file) {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
const reader = new FileReader();
|
|
133
|
+
|
|
134
|
+
// Determine how to read the file based on MIME type
|
|
135
|
+
const isTextFile = file.type.startsWith("text/");
|
|
136
|
+
const readMethod = isTextFile ? "readAsText" : "readAsDataURL";
|
|
137
|
+
|
|
138
|
+
reader.onload = () => {
|
|
139
|
+
const result = reader.result;
|
|
140
|
+
// If it's a binary file, return only the Base64 content without the MIME prefix
|
|
141
|
+
if (!isTextFile) {
|
|
142
|
+
const base64Content = result.split(",")[1]; // Remove MIME prefix
|
|
143
|
+
resolve(base64Content);
|
|
144
|
+
} else {
|
|
145
|
+
resolve(result); // Return plain text
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
reader.onerror = () => reject(new Error("Error reading file"));
|
|
150
|
+
|
|
151
|
+
// Start reading the file
|
|
152
|
+
reader[readMethod](file);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* updates the model with a new value by executing it's `<fx-setvalue>` action.
|
|
161
|
+
*
|
|
162
|
+
* In case the `as='node'` is given the bound node is replaced with the widgets' value with is
|
|
163
|
+
* expected to be a node again.
|
|
164
|
+
*
|
|
165
|
+
* @param val the new value to be set
|
|
166
|
+
*/
|
|
167
|
+
setValue(val) {
|
|
168
|
+
this.value=val;
|
|
169
|
+
const modelitem = this.getModelItem();
|
|
170
|
+
|
|
171
|
+
if(this.mimetype.endsWith('xml')){
|
|
172
|
+
this.nodeset.textContent = '';
|
|
173
|
+
this.nodeset.append(document.importNode(val,true));
|
|
174
|
+
}else{
|
|
175
|
+
modelitem.value = val;
|
|
176
|
+
if(this.mimetype.startsWith('text/')){
|
|
177
|
+
const cdata = this.nodeset.ownerDocument.createCDATASection(val);
|
|
178
|
+
this.nodeset.textContent = '';
|
|
179
|
+
this.nodeset.append(cdata);
|
|
180
|
+
} else {
|
|
181
|
+
this.nodeset.textContent = '';
|
|
182
|
+
this.nodeset.append(val);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if (this.getAttribute('class')) {
|
|
188
|
+
this.classList.add('visited');
|
|
189
|
+
} else {
|
|
190
|
+
this.setAttribute('class', 'visited');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (modelitem?.readonly) {
|
|
194
|
+
console.warn('attempt to change readonly node', modelitem);
|
|
195
|
+
return; // do nothing when modelItem is readonly
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// const setval = this.shadowRoot.getElementById('setvalue');
|
|
199
|
+
// setval.setValue(modelitem, val);
|
|
200
|
+
|
|
201
|
+
if (this.modelItem instanceof ModelItem && !this.modelItem?.boundControls.includes(this)) {
|
|
202
|
+
this.modelItem.boundControls.push(this);
|
|
203
|
+
}
|
|
204
|
+
const model = this.getModel();
|
|
205
|
+
Fore.dispatch(this, 'value-changed', {
|
|
206
|
+
path: this.modelItem.path,
|
|
207
|
+
value: this.modelItem.value,
|
|
208
|
+
oldvalue: "",
|
|
209
|
+
instanceId:this.modelItem.instanceId,
|
|
210
|
+
foreId:this.getOwnerForm().id
|
|
211
|
+
});
|
|
212
|
+
this.getModel().updateModel();
|
|
213
|
+
this.getOwnerForm().refresh(true);
|
|
214
|
+
|
|
215
|
+
// console.log('data', this.getOwnerForm().getModel().getDefaultInstanceData());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
renderHTML(ref) {
|
|
220
|
+
return `
|
|
221
|
+
${this.label ? `${this.label}` : ''}
|
|
222
|
+
<slot></slot>
|
|
223
|
+
<input type="file" ${this.accept !== '' ? `accept="${this.accept}"` : ''}>
|
|
224
|
+
<fx-setvalue id="setvalue" ref="${ref}"></fx-setvalue>
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The widget is the actual control being used in the UI e.g. a native input control or any
|
|
230
|
+
* other component that presents a control that can be interacted with.
|
|
231
|
+
*
|
|
232
|
+
* This function returns the widget by querying the children of this control for an element
|
|
233
|
+
* with `class="widget"`. If that cannot be found it searches for an native `input` of any type.
|
|
234
|
+
* If either cannot be found a `<input type="text">` is created.
|
|
235
|
+
*
|
|
236
|
+
* @returns {HTMLElement|*}
|
|
237
|
+
*/
|
|
238
|
+
getWidget() {
|
|
239
|
+
return this.shadowRoot.querySelector('input');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* updates the widget from the modelItem value. During refresh the a control
|
|
244
|
+
* evaluates it's binding expression to determine the bound node. The bound node corresponds
|
|
245
|
+
* to a modelItem which acts a the state object of a node. The modelItem determines the value
|
|
246
|
+
* and the state of the node and set the `value` property of this class.
|
|
247
|
+
*
|
|
248
|
+
* @returns {Promise<void>}
|
|
249
|
+
*/
|
|
250
|
+
async updateWidgetValue() {
|
|
251
|
+
// this._getValueFromHtmlDom() = this.value;
|
|
252
|
+
|
|
253
|
+
let { widget } = this;
|
|
254
|
+
if (!widget) {
|
|
255
|
+
widget = this;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (widget.value !== this.value) {
|
|
259
|
+
// widget.value = this.value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async refresh(force) {
|
|
264
|
+
// console.log('fx-control refresh', this);
|
|
265
|
+
super.refresh(force);
|
|
266
|
+
// ### if we find a ref on control we have a 'select' control of some kind
|
|
267
|
+
Fore.refreshChildren(this, force);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
evalLabel(optionLabel, node, newEntry) {
|
|
273
|
+
const labelExpr = optionLabel.substring(1, optionLabel.length - 1);
|
|
274
|
+
if (!labelExpr) return;
|
|
275
|
+
|
|
276
|
+
const label = evaluateXPathToString(labelExpr, node, this);
|
|
277
|
+
newEntry.textContent = label;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
createEntry() {
|
|
281
|
+
return this.template.content.firstElementChild.cloneNode(true);
|
|
282
|
+
// const content = this.template.content.firstElementChild.cloneNode(true);
|
|
283
|
+
// return content;
|
|
284
|
+
// const newEntry = document.importNode(content, true);
|
|
285
|
+
// this.template.parentNode.appendChild(newEntry);
|
|
286
|
+
// return newEntry;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// eslint-disable-next-line class-methods-use-this
|
|
290
|
+
_getValueAttribute(element) {
|
|
291
|
+
let result;
|
|
292
|
+
Array.from(element.attributes).forEach(attribute => {
|
|
293
|
+
const attrVal = attribute.value;
|
|
294
|
+
if (attrVal.indexOf('{') !== -1) {
|
|
295
|
+
// console.log('avt found ', attribute);
|
|
296
|
+
result = attribute;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (!customElements.get('fx-upload')) {
|
|
303
|
+
window.customElements.define('fx-upload', FxUpload);
|
|
304
|
+
}
|
package/src/xpath-util.js
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
1
|
import * as fx from 'fontoxpath';
|
|
2
2
|
|
|
3
3
|
export class XPathUtil {
|
|
4
|
+
/**
|
|
5
|
+
* creates DOM Nodes from an XPath locationpath expression. Support namespaced and un-namespaced
|
|
6
|
+
* nodes.
|
|
7
|
+
* E.g. 'foo/bar' creates an element 'foo' with an child element 'bar'
|
|
8
|
+
* 'foo/@bar' creates a 'foo' element with an 'bar' attribute
|
|
9
|
+
*
|
|
10
|
+
* supports multiple steps
|
|
11
|
+
*
|
|
12
|
+
* @param xpath
|
|
13
|
+
* @param doc
|
|
14
|
+
* @param fore
|
|
15
|
+
* @return {*}
|
|
16
|
+
*/
|
|
17
|
+
static createElementFromXPath(xpath, doc, fore) {
|
|
18
|
+
if (!doc) {
|
|
19
|
+
doc = document.implementation.createDocument(null, null, null); // Create a new XML document if not provided
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const parts = xpath.split('/');
|
|
23
|
+
let rootNode = null;
|
|
24
|
+
let currentNode = null;
|
|
25
|
+
|
|
26
|
+
for (const part of parts) {
|
|
27
|
+
if (!part) continue; // Skip empty parts (e.g., leading slashes)
|
|
28
|
+
|
|
29
|
+
// Handle attributes
|
|
30
|
+
if (part.startsWith('@')) {
|
|
31
|
+
const attrName = part.slice(1); // Strip '@'
|
|
32
|
+
if (!currentNode) {
|
|
33
|
+
throw new Error('Cannot create an attribute without a parent element.');
|
|
34
|
+
}
|
|
35
|
+
currentNode.setAttribute(attrName, '');
|
|
36
|
+
} else {
|
|
37
|
+
// Handle namespaces if present
|
|
38
|
+
const [prefix, localName] = part.includes(':') ? part.split(':') : [null, part];
|
|
39
|
+
const namespace = prefix ? XPathUtil.lookupNamespace(fore, prefix) : null;
|
|
40
|
+
|
|
41
|
+
const newElement = namespace
|
|
42
|
+
? doc.createElementNS(namespace, part)
|
|
43
|
+
: doc.createElement(localName);
|
|
44
|
+
|
|
45
|
+
if (!rootNode) {
|
|
46
|
+
rootNode = newElement; // Set as the root node
|
|
47
|
+
} else {
|
|
48
|
+
currentNode.appendChild(newElement);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
currentNode = newElement;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!rootNode) {
|
|
56
|
+
throw new Error('Invalid XPath; no root element could be created.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return rootNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* looks up namespace on ownerForm. Though not strictly in the sense of resolving namespaces in XML, the
|
|
64
|
+
* fx-fore element is a convenient place to put namespace declarations for 2 reasons:
|
|
65
|
+
* - this way namespaces are scoped to a Fore element
|
|
66
|
+
* - as fx-fore is a web component we can add our xmlns attributes as we got no restrictions to attributes
|
|
67
|
+
* though strictly speaking they are no xmlns declarations and just serve the purpose of namespace lookup.
|
|
68
|
+
*
|
|
69
|
+
* @param boundElement
|
|
70
|
+
* @param prefix
|
|
71
|
+
* @return {string}
|
|
72
|
+
*/
|
|
73
|
+
static lookupNamespace(ownerForm, prefix) {
|
|
74
|
+
return ownerForm.getAttribute(`xmlns:${prefix}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static querySelectorAll(querySelector, start) {
|
|
78
|
+
const queue = [start];
|
|
79
|
+
const found = [];
|
|
80
|
+
while (queue.length) {
|
|
81
|
+
const item = queue.shift();
|
|
82
|
+
for (const child of Array.from(item.children).reverse()) {
|
|
83
|
+
queue.unshift(child);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (item.matches && item.matches('template')) {
|
|
87
|
+
queue.unshift(item.content);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (item.matches && item.matches(querySelector)) {
|
|
91
|
+
found.push(item);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return found;
|
|
96
|
+
}
|
|
97
|
+
|
|
4
98
|
/**
|
|
5
99
|
* Alternative to `contains` that respects shadowroots
|
|
6
100
|
* @param {Node} ancestor
|
|
@@ -74,7 +168,7 @@ export class XPathUtil {
|
|
|
74
168
|
(start.parentNode.nodeType !== Node.DOCUMENT_NODE ||
|
|
75
169
|
start.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE)
|
|
76
170
|
) {
|
|
77
|
-
return
|
|
171
|
+
return this.getClosest('[ref],fx-repeatitem', start.parentNode);
|
|
78
172
|
}
|
|
79
173
|
return null;
|
|
80
174
|
}
|
|
@@ -87,7 +181,9 @@ export class XPathUtil {
|
|
|
87
181
|
* path, otherwise <code>false</code>.
|
|
88
182
|
*/
|
|
89
183
|
static isAbsolutePath(path) {
|
|
90
|
-
return
|
|
184
|
+
return (
|
|
185
|
+
path != null && (path.startsWith('/') || path.startsWith('instance(') || path.startsWith('$'))
|
|
186
|
+
);
|
|
91
187
|
}
|
|
92
188
|
|
|
93
189
|
/**
|
|
@@ -104,9 +200,10 @@ export class XPathUtil {
|
|
|
104
200
|
*
|
|
105
201
|
* Otherwise instance id is extracted from function and returned. If all fails null is returned.
|
|
106
202
|
* @param {string} ref
|
|
203
|
+
* @param {HTMLElement} boundElement The element related to this ref. Used to resolve variables
|
|
107
204
|
* @returns {string}
|
|
108
205
|
*/
|
|
109
|
-
static getInstanceId(ref) {
|
|
206
|
+
static getInstanceId(ref, boundElement) {
|
|
110
207
|
if (!ref) {
|
|
111
208
|
return 'default';
|
|
112
209
|
}
|
|
@@ -117,6 +214,23 @@ export class XPathUtil {
|
|
|
117
214
|
const result = ref.substring(ref.indexOf('(') + 1);
|
|
118
215
|
return result.substring(1, result.indexOf(')') - 1);
|
|
119
216
|
}
|
|
217
|
+
if (ref.startsWith('$')) {
|
|
218
|
+
// this variable might actually point to an instance
|
|
219
|
+
const variableName = ref.match(/\$(?<variableName>[a-zA-Z0-9\-\_]+).*/)?.groups?.variableName;
|
|
220
|
+
let closestActualFormElement = boundElement;
|
|
221
|
+
while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
|
|
222
|
+
closestActualFormElement =
|
|
223
|
+
closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
|
|
224
|
+
? closestActualFormElement.ownerElement
|
|
225
|
+
: closestActualFormElement.parentNode;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const correspondingVariable = closestActualFormElement?.inScopeVariables?.get(variableName);
|
|
229
|
+
if (!correspondingVariable) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
return this.getInstanceId(correspondingVariable.valueQuery, correspondingVariable);
|
|
233
|
+
}
|
|
120
234
|
return null;
|
|
121
235
|
}
|
|
122
236
|
|
|
@@ -126,9 +240,9 @@ export class XPathUtil {
|
|
|
126
240
|
* @returns {string}
|
|
127
241
|
*/
|
|
128
242
|
static resolveInstance(boundElement, path) {
|
|
129
|
-
let instanceId = XPathUtil.getInstanceId(path);
|
|
243
|
+
let instanceId = XPathUtil.getInstanceId(path, boundElement);
|
|
130
244
|
if (!instanceId) {
|
|
131
|
-
instanceId = XPathUtil.getInstanceId(boundElement.getAttribute('ref'));
|
|
245
|
+
instanceId = XPathUtil.getInstanceId(boundElement.getAttribute('ref'), boundElement);
|
|
132
246
|
}
|
|
133
247
|
if (instanceId !== null) {
|
|
134
248
|
return instanceId;
|