@jinntec/fore 2.5.0 → 2.6.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 +34261 -9
- package/dist/fore.js +34123 -9
- package/index.js +1 -1
- package/package.json +1 -1
- package/resources/fore.css +29 -0
- package/src/DataObserver.js +181 -0
- package/src/DependentXPathQueries.js +32 -0
- package/src/ForeElementMixin.js +289 -260
- package/src/actions/abstract-action.js +24 -24
- package/src/actions/fx-append.js +2 -0
- package/src/actions/fx-delete.js +14 -2
- package/src/actions/fx-hide.js +1 -1
- package/src/actions/fx-insert.js +47 -41
- package/src/actions/fx-load.js +7 -2
- package/src/actions/fx-setvalue.js +104 -86
- package/src/actions/fx-show.js +4 -2
- package/src/fore.js +40 -23
- package/src/fx-fore.js +92 -93
- package/src/fx-submission.js +28 -23
- package/src/getInScopeContext.js +1 -0
- package/src/modelitem.js +107 -96
- package/src/ui/UIElement.js +91 -0
- package/src/ui/abstract-control.js +10 -7
- package/src/ui/fx-container.js +5 -3
- package/src/ui/fx-control-menu.js +198 -0
- package/src/ui/fx-control.js +61 -17
- package/src/ui/fx-dialog.js +2 -2
- package/src/ui/fx-group.js +14 -0
- package/src/ui/fx-repeat.js +33 -3
- package/src/xpath-util.js +284 -256
- package/dist/fore-dev.js.map +0 -1
- package/dist/fore.js.map +0 -1
- package/src/ui/fx-select.js +0 -89
package/src/xpath-util.js
CHANGED
|
@@ -1,301 +1,329 @@
|
|
|
1
1
|
import * as fx from 'fontoxpath';
|
|
2
2
|
|
|
3
3
|
export class XPathUtil {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 createNodesFromXPath(xpath, doc, fore) {
|
|
18
|
+
if (!doc) {
|
|
19
|
+
doc = document.implementation.createDocument(null, null, null); // Create a new XML document if not provided
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const parts = xpath.split('/');
|
|
23
|
+
let rootNode = null;
|
|
24
|
+
let currentNode = null;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
for (const part of parts) {
|
|
27
|
+
if (!part) continue; // Skip empty parts (e.g., leading slashes)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
// Handle attributes
|
|
30
|
+
if (part.startsWith('@')) {
|
|
31
|
+
const attrName = part.slice(1); // Strip '@'
|
|
32
|
+
if (!currentNode) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
'Cannot create an attribute without a parent element.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
currentNode.setAttribute(attrName, '');
|
|
38
|
+
} else {
|
|
39
|
+
// Handle namespaces if present
|
|
40
|
+
const [prefix, localName] = part.includes(':')
|
|
41
|
+
? part.split(':')
|
|
42
|
+
: [null, part];
|
|
43
|
+
const namespace = prefix
|
|
44
|
+
? XPathUtil.lookupNamespace(fore, prefix)
|
|
45
|
+
: null;
|
|
46
|
+
|
|
47
|
+
const newElement = namespace
|
|
48
|
+
? doc.createElementNS(namespace, part)
|
|
49
|
+
: doc.createElement(localName);
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
if (!rootNode) {
|
|
52
|
+
rootNode = newElement; // Set as the root node
|
|
53
|
+
} else {
|
|
54
|
+
currentNode.appendChild(newElement);
|
|
55
|
+
}
|
|
56
|
+
currentNode = newElement;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
44
59
|
|
|
45
60
|
if (!rootNode) {
|
|
46
|
-
|
|
47
|
-
} else {
|
|
48
|
-
currentNode.appendChild(newElement);
|
|
61
|
+
throw new Error('Invalid XPath; no root element could be created.');
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
}
|
|
64
|
+
return rootNode;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
/**
|
|
68
|
+
* looks up namespace on ownerForm. Though not strictly in the sense of resolving namespaces in XML, the
|
|
69
|
+
* fx-fore element is a convenient place to put namespace declarations for 2 reasons:
|
|
70
|
+
* - this way namespaces are scoped to a Fore element
|
|
71
|
+
* - as fx-fore is a web component we can add our xmlns attributes as we got no restrictions to attributes
|
|
72
|
+
* though strictly speaking they are no xmlns declarations and just serve the purpose of namespace lookup.
|
|
73
|
+
*
|
|
74
|
+
* @param boundElement
|
|
75
|
+
* @param prefix
|
|
76
|
+
* @return {string}
|
|
77
|
+
*/
|
|
78
|
+
static lookupNamespace(ownerForm, prefix) {
|
|
79
|
+
return ownerForm.getAttribute(`xmlns:${prefix}`);
|
|
57
80
|
}
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
}
|
|
82
|
+
static querySelectorAll(querySelector, start) {
|
|
83
|
+
const queue = [start];
|
|
84
|
+
const found = [];
|
|
85
|
+
while (queue.length) {
|
|
86
|
+
const item = queue.shift();
|
|
87
|
+
for (const child of Array.from(item.children).reverse()) {
|
|
88
|
+
queue.unshift(child);
|
|
89
|
+
}
|
|
76
90
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
while (queue.length) {
|
|
81
|
-
const item = queue.shift();
|
|
82
|
-
for (const child of Array.from(item.children).reverse()) {
|
|
83
|
-
queue.unshift(child);
|
|
84
|
-
}
|
|
91
|
+
if (item.matches && item.matches('template')) {
|
|
92
|
+
queue.unshift(item.content);
|
|
93
|
+
}
|
|
85
94
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
if (item.matches && item.matches(querySelector)) {
|
|
96
|
+
found.push(item);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
found.push(item);
|
|
92
|
-
}
|
|
100
|
+
return found;
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (descendant === ancestor) {
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
103
|
+
/**
|
|
104
|
+
* Alternative to `contains` that respects shadowroots
|
|
105
|
+
* @param {Node} ancestor
|
|
106
|
+
* @param {Node} descendant
|
|
107
|
+
* @returns {boolean}
|
|
108
|
+
*/
|
|
109
|
+
static contains(ancestor, descendant) {
|
|
110
|
+
while (descendant) {
|
|
111
|
+
if (descendant === ancestor) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
if (descendant.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
116
|
+
// We are passing a shadow root boundary
|
|
117
|
+
descendant = descendant.host;
|
|
118
|
+
} else {
|
|
119
|
+
descendant = descendant.parentNode;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
116
123
|
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
124
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Alternative to `closest` that respects subcontrol boundaries
|
|
127
|
+
*
|
|
128
|
+
* @param {string} querySelector
|
|
129
|
+
* @param {Node} start
|
|
130
|
+
* @returns {HTMLElement}
|
|
131
|
+
*/
|
|
132
|
+
static getClosest(querySelector, start) {
|
|
133
|
+
while ((start && !start.matches) || !start.matches(querySelector)) {
|
|
134
|
+
if (start.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
135
|
+
// We are passing a shadow root boundary
|
|
136
|
+
start = start.host;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (start.nodeType === Node.ATTRIBUTE_NODE) {
|
|
140
|
+
// We are passing an attribute
|
|
141
|
+
start = start.ownerElement;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (start.nodeType === Node.TEXT_NODE) {
|
|
145
|
+
start = start.parentNode;
|
|
146
|
+
}
|
|
147
|
+
if (start.matches('fx-fore')) {
|
|
148
|
+
// Subform reached. Bail out
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
start = start.parentNode;
|
|
152
|
+
if (!start) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return start;
|
|
150
157
|
}
|
|
151
|
-
return start;
|
|
152
|
-
}
|
|
153
158
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
/**
|
|
160
|
+
* returns next bound element upwards in tree
|
|
161
|
+
* @param {Node} start where to start the search
|
|
162
|
+
* @returns {*|null}
|
|
163
|
+
*/
|
|
164
|
+
static getParentBindingElement(start) {
|
|
165
|
+
/* if (start.parentNode.host) {
|
|
161
166
|
const { host } = start.parentNode;
|
|
162
167
|
if (host.hasAttribute('ref')) {
|
|
163
168
|
return host;
|
|
164
169
|
}
|
|
165
170
|
} else */
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
if (
|
|
172
|
+
start.parentNode &&
|
|
173
|
+
(start.parentNode.nodeType !== Node.DOCUMENT_NODE ||
|
|
174
|
+
start.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE)
|
|
175
|
+
) {
|
|
176
|
+
return this.getClosest('[ref],fx-repeatitem', start.parentNode);
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
172
179
|
}
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Checks whether the specified path expression is an absolute path.
|
|
178
|
-
*
|
|
179
|
-
* @param {string} path the path expression.
|
|
180
|
-
* @returns {boolean} <code>true</code> if specified path expression is an absolute
|
|
181
|
-
* path, otherwise <code>false</code>.
|
|
182
|
-
*/
|
|
183
|
-
static isAbsolutePath(path) {
|
|
184
|
-
return (
|
|
185
|
-
path != null && (path.startsWith('/') || path.startsWith('instance(') || path.startsWith('$'))
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* @param {string} ref
|
|
191
|
-
*/
|
|
192
|
-
static isSelfReference(ref) {
|
|
193
|
-
return ref === '.' || ref === './text()' || ref === 'text()' || ref === '' || ref === null;
|
|
194
|
-
}
|
|
195
180
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Checks whether the specified path expression is an absolute path.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} path the path expression.
|
|
185
|
+
* @returns {boolean} <code>true</code> if specified path expression is an absolute
|
|
186
|
+
* path, otherwise <code>false</code>.
|
|
187
|
+
*/
|
|
188
|
+
static isAbsolutePath(path) {
|
|
189
|
+
return (
|
|
190
|
+
path != null &&
|
|
191
|
+
(path.startsWith('/') ||
|
|
192
|
+
path.startsWith('instance(') ||
|
|
193
|
+
path.startsWith('$'))
|
|
194
|
+
);
|
|
209
195
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @param {string} ref
|
|
199
|
+
*/
|
|
200
|
+
static isSelfReference(ref) {
|
|
201
|
+
return (
|
|
202
|
+
ref === '.' ||
|
|
203
|
+
ref === './text()' ||
|
|
204
|
+
ref === 'text()' ||
|
|
205
|
+
ref === '' ||
|
|
206
|
+
ref === null
|
|
207
|
+
);
|
|
216
208
|
}
|
|
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
209
|
|
|
228
|
-
|
|
229
|
-
|
|
210
|
+
/**
|
|
211
|
+
* returns the instance id from a complete XPath using `instance()` function.
|
|
212
|
+
*
|
|
213
|
+
* Will return 'default' in case no ref is given at all or the `instance()` function is called without arg.
|
|
214
|
+
*
|
|
215
|
+
* Otherwise instance id is extracted from function and returned. If all fails null is returned.
|
|
216
|
+
* @param {string} ref
|
|
217
|
+
* @param {HTMLElement} boundElement The element related to this ref. Used to resolve variables
|
|
218
|
+
* @returns {string}
|
|
219
|
+
*/
|
|
220
|
+
static getInstanceId(ref, boundElement) {
|
|
221
|
+
if (!ref) {
|
|
222
|
+
return 'default';
|
|
223
|
+
}
|
|
224
|
+
if (ref.startsWith('instance()')) {
|
|
225
|
+
return 'default';
|
|
226
|
+
}
|
|
227
|
+
if (ref.startsWith('instance(')) {
|
|
228
|
+
const result = ref.substring(ref.indexOf('(') + 1);
|
|
229
|
+
return result.substring(1, result.indexOf(')') - 1);
|
|
230
|
+
}
|
|
231
|
+
if (ref.startsWith('$')) {
|
|
232
|
+
// this variable might actually point to an instance
|
|
233
|
+
const variableName = ref.match(
|
|
234
|
+
/\$(?<variableName>[a-zA-Z0-9\-\_]+).*/,
|
|
235
|
+
)?.groups?.variableName;
|
|
236
|
+
let closestActualFormElement = boundElement;
|
|
237
|
+
while (
|
|
238
|
+
closestActualFormElement &&
|
|
239
|
+
!('inScopeVariables' in closestActualFormElement)
|
|
240
|
+
) {
|
|
241
|
+
closestActualFormElement =
|
|
242
|
+
closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
|
|
243
|
+
? closestActualFormElement.ownerElement
|
|
244
|
+
: closestActualFormElement.parentNode;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const correspondingVariable =
|
|
248
|
+
closestActualFormElement?.inScopeVariables?.get(variableName);
|
|
249
|
+
if (!correspondingVariable) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return this.getInstanceId(
|
|
253
|
+
correspondingVariable.valueQuery,
|
|
254
|
+
correspondingVariable,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
230
257
|
return null;
|
|
231
|
-
}
|
|
232
|
-
return this.getInstanceId(correspondingVariable.valueQuery, correspondingVariable);
|
|
233
258
|
}
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
259
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
260
|
+
/**
|
|
261
|
+
* @param {HTMLElement} boundElement
|
|
262
|
+
* @param {string} path
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
265
|
+
static resolveInstance(boundElement, path) {
|
|
266
|
+
let instanceId = XPathUtil.getInstanceId(path, boundElement);
|
|
267
|
+
if (!instanceId) {
|
|
268
|
+
instanceId = XPathUtil.getInstanceId(
|
|
269
|
+
boundElement.getAttribute('ref'),
|
|
270
|
+
boundElement,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (instanceId !== null) {
|
|
274
|
+
return instanceId;
|
|
275
|
+
}
|
|
250
276
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
277
|
+
const parentBinding = XPathUtil.getParentBindingElement(boundElement);
|
|
278
|
+
if (parentBinding) {
|
|
279
|
+
return this.resolveInstance(parentBinding, path);
|
|
280
|
+
}
|
|
281
|
+
return 'default';
|
|
254
282
|
}
|
|
255
|
-
return 'default';
|
|
256
|
-
}
|
|
257
283
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
/**
|
|
285
|
+
* @param {Node} node
|
|
286
|
+
* @returns string
|
|
287
|
+
*/
|
|
288
|
+
static getDocPath(node) {
|
|
289
|
+
const path = fx.evaluateXPathToString('path()', node);
|
|
290
|
+
// Path is like `$default/x[1]/y[1]`
|
|
291
|
+
const shortened = XPathUtil.shortenPath(path);
|
|
292
|
+
return shortened.startsWith('/') ? `${shortened}` : `/${shortened}`;
|
|
293
|
+
}
|
|
268
294
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
295
|
+
/**
|
|
296
|
+
* @param {Node} node
|
|
297
|
+
* @param {string} instanceId
|
|
298
|
+
* @returns string
|
|
299
|
+
*/
|
|
300
|
+
static getPath(node, instanceId) {
|
|
301
|
+
const path = fx.evaluateXPathToString('path()', node);
|
|
302
|
+
// Path is like `$default/x[1]/y[1]`
|
|
303
|
+
const shortened = XPathUtil.shortenPath(path);
|
|
304
|
+
return shortened.startsWith('/')
|
|
305
|
+
? `$${instanceId}${shortened}`
|
|
306
|
+
: `$${instanceId}/${shortened}`;
|
|
307
|
+
}
|
|
280
308
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
/**
|
|
310
|
+
* @param {string} path
|
|
311
|
+
* @returns string
|
|
312
|
+
*/
|
|
313
|
+
static shortenPath(path) {
|
|
314
|
+
const tmp = path.replaceAll(/(Q{(.*?)\})/g, '');
|
|
315
|
+
// cut off leading slash
|
|
316
|
+
const tmp1 = tmp.substring(1, tmp.length);
|
|
317
|
+
// ### cut-off root node ref
|
|
318
|
+
return tmp1.substring(tmp1.indexOf('/'), tmp.length);
|
|
319
|
+
}
|
|
292
320
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
321
|
+
/**
|
|
322
|
+
* @param {string} dep
|
|
323
|
+
* @returns {string}
|
|
324
|
+
*/
|
|
325
|
+
static getBasePath(dep) {
|
|
326
|
+
const split = dep.split(':');
|
|
327
|
+
return split[0];
|
|
328
|
+
}
|
|
301
329
|
}
|