@sap-ux/jest-environment-ui5 5.3.1
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/LICENSE +201 -0
- package/README.md +81 -0
- package/package.json +51 -0
- package/src/env/mockXHR.js +137 -0
- package/src/env/ui5Environment.js +97 -0
- package/src/env/ui5loader.js +3323 -0
- package/src/index.js +170 -0
- package/src/utils/automaticSetup.js +4 -0
- package/src/utils/tsMappingStrategy.js +47 -0
- package/src/utils/ui5MappingStrategy.js +80 -0
|
@@ -0,0 +1,3323 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ${copyright}
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// This file is a copy from the ui5 ui5loader.js file with some modifications to make it work in the jest enviorment
|
|
6
|
+
/*
|
|
7
|
+
* IMPORTANT NOTICE
|
|
8
|
+
* With 1.54, ui5loader.js and its new features are not yet a public API.
|
|
9
|
+
* The loader must only be used via the well-known and documented UI5 APIs
|
|
10
|
+
* such as sap.ui.define, sap.ui.require, etc.
|
|
11
|
+
* Any direct usage of ui5loader.js or its features is not supported and
|
|
12
|
+
* might break in future releases.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/*global sap:true, Blob, console, document, Promise, URL, XMLHttpRequest */
|
|
16
|
+
|
|
17
|
+
(function(__global) {
|
|
18
|
+
"use strict";
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Helper function that removes any query and/or hash parts from the given URL.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} href URL to remove query and hash from
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function pathOnly(href) {
|
|
27
|
+
const p = href.search(/[?#]/);
|
|
28
|
+
return p < 0 ? href : href.slice(0, p);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a given URL, either against the base URL of the current document or against a given base URL.
|
|
33
|
+
*
|
|
34
|
+
* If no base URL is given, the URL will be resolved relative to the baseURI of the current document.
|
|
35
|
+
* If a base URL is given, that base will first be resolved relative to the document's baseURI,
|
|
36
|
+
* then the URL will be resolved relative to the resolved base.
|
|
37
|
+
*
|
|
38
|
+
* Search parameters or a hash of the chosen base will be ignored.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} sURI Relative or absolute URL that should be resolved
|
|
41
|
+
* @param {string} [sBase=document.baseURI] Base URL relative to which the URL should be resolved
|
|
42
|
+
* @returns {string} Resolved URL
|
|
43
|
+
*/
|
|
44
|
+
function resolveURL(sURI, sBase) {
|
|
45
|
+
sBase = pathOnly(sBase ? resolveURL(sBase) : document.baseURI);
|
|
46
|
+
return new URL(sURI, sBase).href;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---- helpers -------------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
function noop() {}
|
|
52
|
+
|
|
53
|
+
function forEach(obj, callback) {
|
|
54
|
+
Object.keys(obj).forEach((key) => callback(key, obj[key]));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function executeInSeparateTask(fn) {
|
|
58
|
+
setTimeout(fn, 0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function executeInMicroTask(fn) {
|
|
62
|
+
Promise.resolve().then(fn);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---- hooks & configuration -----------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
const aEarlyLogs = [];
|
|
68
|
+
|
|
69
|
+
function earlyLog(level, message) {
|
|
70
|
+
aEarlyLogs.push({
|
|
71
|
+
level,
|
|
72
|
+
message
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Log functionality.
|
|
78
|
+
*
|
|
79
|
+
* Can be set to an object with the methods shown below (subset of sap/base/Log).
|
|
80
|
+
* Logging methods never must fail. Should they ever throw errors, then the internal state
|
|
81
|
+
* of the loader will be broken.
|
|
82
|
+
*
|
|
83
|
+
* By default, all methods are implemented as NOOPs.
|
|
84
|
+
*
|
|
85
|
+
* @type {{debug:function(),info:function(),warning:function(),error:function(),isLoggable:function():boolean}}
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
let log = {
|
|
90
|
+
debug: earlyLog.bind(this, 'debug'),
|
|
91
|
+
info: earlyLog.bind(this, 'info'),
|
|
92
|
+
warning: earlyLog.bind(this, 'warning'),
|
|
93
|
+
error: earlyLog.bind(this, 'error'),
|
|
94
|
+
isLoggable: noop
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Basic assert functionality.
|
|
99
|
+
*
|
|
100
|
+
* Can be set to a function that gets a value (the expression to be asserted) as first
|
|
101
|
+
* parameter and a message as second parameter. When the expression coerces to false,
|
|
102
|
+
* the assertion is violated and the message should be emitted (logged, thrown, whatever).
|
|
103
|
+
*
|
|
104
|
+
* By default, this is implemented as a NOOP.
|
|
105
|
+
* @type {function(any,string)}
|
|
106
|
+
* @private
|
|
107
|
+
*/
|
|
108
|
+
let assert = noop; // Null Object pattern: dummy assert which is used as long as no assert is injected
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Callback for performance measurement.
|
|
112
|
+
*
|
|
113
|
+
* When set, it must be an object with methods <code>start</code> and <code>end</code>.
|
|
114
|
+
* @type {{start:function(string,any),end:function(string)}}
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
let measure;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Source code transformation hook.
|
|
121
|
+
*
|
|
122
|
+
* To be used by code coverage, only supported in sync mode.
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
let translate;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Method used by sap.ui.require to simulate asynchronous behavior.
|
|
129
|
+
*
|
|
130
|
+
* The default executes the given function in a separate browser task.
|
|
131
|
+
* Can be changed to execute in a micro task to save idle time in case of
|
|
132
|
+
* many nested sap.ui.require calls.
|
|
133
|
+
*/
|
|
134
|
+
let simulateAsyncCallback = executeInSeparateTask;
|
|
135
|
+
|
|
136
|
+
/*
|
|
137
|
+
* Activates strictest possible compliance with AMD spec
|
|
138
|
+
* - no multiple executions of the same module
|
|
139
|
+
* - at most one anonymous module definition per file, zero for adhoc definitions
|
|
140
|
+
*/
|
|
141
|
+
const strictModuleDefinitions = true;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Whether asynchronous loading can be used at all.
|
|
145
|
+
* When activated, require will load asynchronously, else synchronously.
|
|
146
|
+
* @type {boolean}
|
|
147
|
+
* @private
|
|
148
|
+
*/
|
|
149
|
+
let bGlobalAsyncMode = false;
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Whether ui5loader currently exposes its AMD implementation as global properties
|
|
154
|
+
* <code>define</code> and <code>require</code>. Defaults to <code>false</code>.
|
|
155
|
+
* @type {boolean}
|
|
156
|
+
* @private
|
|
157
|
+
*/
|
|
158
|
+
let bExposeAsAMDLoader = false;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* How the loader should react to calls of sync APIs or when global names are accessed:
|
|
162
|
+
* 0: tolerate
|
|
163
|
+
* 1: warn
|
|
164
|
+
* 2: reject
|
|
165
|
+
* @type {int}
|
|
166
|
+
* @private
|
|
167
|
+
*/
|
|
168
|
+
let syncCallBehavior = 0;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Default base URL for modules, used when no other configuration is provided.
|
|
172
|
+
* In case the base url is removed via <code>registerResourcePath("", null)</code>
|
|
173
|
+
* it will be reset to this URL instead.
|
|
174
|
+
* @const
|
|
175
|
+
* @type {string}
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
const DEFAULT_BASE_URL = "";
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Temporarily saved reference to the original value of the global define variable.
|
|
182
|
+
*
|
|
183
|
+
* @type {any}
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
let vOriginalDefine;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Temporarily saved reference to the original value of the global require variable.
|
|
190
|
+
*
|
|
191
|
+
* @type {any}
|
|
192
|
+
* @private
|
|
193
|
+
*/
|
|
194
|
+
let vOriginalRequire;
|
|
195
|
+
|
|
196
|
+
let currentModuleName;
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A map of URL prefixes keyed by the corresponding module name prefix.
|
|
201
|
+
*
|
|
202
|
+
* Note that the empty prefix ('') will always match and thus serves as a fallback.
|
|
203
|
+
* See {@link sap.ui.loader.config}, option <code>paths</code>.
|
|
204
|
+
* @type {Object<string,{url:string,absoluteUrl:string}>}
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
const mUrlPrefixes = Object.create(null);
|
|
208
|
+
mUrlPrefixes[''] = {
|
|
209
|
+
url: DEFAULT_BASE_URL,
|
|
210
|
+
absoluteUrl: resolveURL(DEFAULT_BASE_URL)
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Mapping of module IDs.
|
|
215
|
+
*
|
|
216
|
+
* Each entry is a map of its own, keyed by the module ID prefix for which it should be
|
|
217
|
+
* applied. Each contained map maps module ID prefixes to module ID prefixes.
|
|
218
|
+
*
|
|
219
|
+
* All module ID prefixes must not have extensions.
|
|
220
|
+
* @type {Object.<string,Object.<string,string>>}
|
|
221
|
+
* @private
|
|
222
|
+
*/
|
|
223
|
+
const mMaps = Object.create(null);
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Information about third party modules, keyed by the module's resource name (including extension '.js').
|
|
227
|
+
*
|
|
228
|
+
* Each module shim object can have the following properties:
|
|
229
|
+
* <ul>
|
|
230
|
+
* <li><i>boolean</i>: [amd=false] Whether the module uses an AMD loader if present. If set to <code>true</code>,
|
|
231
|
+
* UI5 will disable an AMD loader while loading such a module to force the module to expose its content
|
|
232
|
+
* via global names.</li>
|
|
233
|
+
* <li><i>string[]|string</i>: [exports=undefined] Global name (or names) that are exported by the module.
|
|
234
|
+
* If one ore multiple names are defined, the first one will be read from the global object and will be
|
|
235
|
+
* used as value of the module. Each name can be a dot separated hierarchical name (will be resolved with
|
|
236
|
+
* <code>getGlobalProperty</code>)</li>
|
|
237
|
+
* <li><i>string[]</i>: [deps=undefined] List of modules that the module depends on. The modules will be loaded
|
|
238
|
+
* first before loading the module itself. Note that the stored dependencies also include the extension '.js'
|
|
239
|
+
* for easier evaluation, but <code>config({shim:...})</code> expects them without the extension for
|
|
240
|
+
* compatibility with the AMD-JS specification.</li>
|
|
241
|
+
* </ul>
|
|
242
|
+
*
|
|
243
|
+
* @see config method
|
|
244
|
+
* @type {Object.<string,{amd:boolean,exports:(string|string[]),deps:string[]}>}
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
const mShims = Object.create(null);
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Dependency Cache information.
|
|
251
|
+
* Maps the name of a module to a list of its known dependencies.
|
|
252
|
+
* @type {Object.<string,string[]>}
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
const mDepCache = Object.create(null);
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Whether the loader should try to load debug sources.
|
|
259
|
+
* @type {boolean}
|
|
260
|
+
* @private
|
|
261
|
+
*/
|
|
262
|
+
let bDebugSources = false;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Indicates partial or total debug mode.
|
|
266
|
+
*
|
|
267
|
+
* Can be set to a function which checks whether preloads should be ignored for the given module.
|
|
268
|
+
* If undefined, all preloads will be used.
|
|
269
|
+
* @type {function(string):boolean|undefined}
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
let fnIgnorePreload;
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
// ---- internal state ------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Map of modules that have been loaded or required so far, keyed by their name.
|
|
279
|
+
*
|
|
280
|
+
* @type {Object<string,Module>}
|
|
281
|
+
* @private
|
|
282
|
+
*/
|
|
283
|
+
const mModules = Object.create(null);
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Whether (sap.ui.)define calls must be executed synchronously in the current context.
|
|
287
|
+
*
|
|
288
|
+
* The initial value is <code>null</code>. During the execution of a module loading operation
|
|
289
|
+
* ((sap.ui.)require or (sap.ui.)define etc.), it is set to true or false depending on the
|
|
290
|
+
* legacy synchronicity behavior of the operation.
|
|
291
|
+
*
|
|
292
|
+
* Problem: when AMD modules are loaded with hard coded script tags and when some later inline
|
|
293
|
+
* script expects the module export synchronously, then the (sap.ui.)define must be executed
|
|
294
|
+
* synchronously.
|
|
295
|
+
* Most prominent example: unit tests that include QUnitUtils as a script tag and use qutils
|
|
296
|
+
* in one of their inline scripts.
|
|
297
|
+
* @type {boolean|null}
|
|
298
|
+
* @private
|
|
299
|
+
*/
|
|
300
|
+
let bForceSyncDefines = null;
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Stack of modules that are currently being executed in case of synchronous processing.
|
|
304
|
+
*
|
|
305
|
+
* Allows to identify the executing module (e.g. when resolving dependencies or in case of
|
|
306
|
+
* bundles like sap-ui-core).
|
|
307
|
+
*
|
|
308
|
+
* @type {Array.<{name:string,used:boolean}>}
|
|
309
|
+
* @private
|
|
310
|
+
*/
|
|
311
|
+
const _execStack = [ ];
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* A prefix that will be added to module loading log statements and which reflects the nesting of module executions.
|
|
315
|
+
* @type {string}
|
|
316
|
+
* @private
|
|
317
|
+
*/
|
|
318
|
+
let sLogPrefix = "";
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Counter used to give anonymous modules a unique module ID.
|
|
322
|
+
* @type {int}
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
let iAnonymousModuleCount = 0;
|
|
326
|
+
|
|
327
|
+
// ---- break preload execution into tasks ----------------------------------------------------
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Default value for `iMaxTaskDuration`.
|
|
331
|
+
*
|
|
332
|
+
* A value of -1 switched the scheduling off, a value of zero postpones each execution
|
|
333
|
+
*/
|
|
334
|
+
const DEFAULT_MAX_TASK_DURATION = -1; // off
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Maximum accumulated task execution time (threshold)
|
|
338
|
+
* Can be configured via the private API property `maxTaskDuration`.
|
|
339
|
+
*/
|
|
340
|
+
let iMaxTaskDuration = DEFAULT_MAX_TASK_DURATION;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* The earliest elapsed time at which a new browser task will be enforced.
|
|
344
|
+
* Will be updated when a new task starts.
|
|
345
|
+
*/
|
|
346
|
+
let iMaxTaskTime = Date.now() + iMaxTaskDuration;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* A promise that fulfills when the new browser task has been reached.
|
|
350
|
+
* All postponed callback executions will be executed after this promise.
|
|
351
|
+
* `null` as long as the elapsed time threshold is not reached.
|
|
352
|
+
*/
|
|
353
|
+
let pWaitForNextTask;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Message channel which will be used to create a new browser task
|
|
357
|
+
* without being subject to timer throttling.
|
|
358
|
+
* Will be created lazily on first usage.
|
|
359
|
+
*/
|
|
360
|
+
let oNextTaskMessageChannel;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Update elapsed time threshold.
|
|
364
|
+
*
|
|
365
|
+
* The threshold will be updated only if executions currently are not postponed.
|
|
366
|
+
* Otherwise, the next task will anyhow update the threshold.
|
|
367
|
+
*/
|
|
368
|
+
function updateMaxTaskTime() {
|
|
369
|
+
if ( pWaitForNextTask == null ) {
|
|
370
|
+
iMaxTaskTime = Date.now() + iMaxTaskDuration;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Update duration limit and elapsed time threshold.
|
|
376
|
+
*/
|
|
377
|
+
function updateMaxTaskDuration(v) {
|
|
378
|
+
v = Number(v);
|
|
379
|
+
|
|
380
|
+
const iBeginOfCurrentTask = iMaxTaskTime - iMaxTaskDuration;
|
|
381
|
+
|
|
382
|
+
// limit to range [-1 ... Infinity], any other value incl. NaN restores the default
|
|
383
|
+
iMaxTaskDuration = v >= -1 ? v : DEFAULT_MAX_TASK_DURATION;
|
|
384
|
+
|
|
385
|
+
// Update the elapsed time threshold only if executions currently are not postponed.
|
|
386
|
+
// Otherwise, the next task will be the first to honor the new maximum duration.
|
|
387
|
+
if ( pWaitForNextTask == null ) {
|
|
388
|
+
iMaxTaskTime = iBeginOfCurrentTask + iMaxTaskDuration;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function waitForNextTask() {
|
|
393
|
+
if ( pWaitForNextTask == null ) {
|
|
394
|
+
/**
|
|
395
|
+
* Post a message to a MessageChannel to create a new task, without suffering from timer throttling
|
|
396
|
+
* In the new task, use a setTimeout(,0) to allow for better queuing of other events (like CSS loading)
|
|
397
|
+
*/
|
|
398
|
+
pWaitForNextTask = new Promise(function(resolve) {
|
|
399
|
+
if ( oNextTaskMessageChannel == null ) {
|
|
400
|
+
oNextTaskMessageChannel = new MessageChannel();
|
|
401
|
+
oNextTaskMessageChannel.port2.start();
|
|
402
|
+
}
|
|
403
|
+
oNextTaskMessageChannel.port2.addEventListener("message", function() {
|
|
404
|
+
setTimeout(function() {
|
|
405
|
+
pWaitForNextTask = null;
|
|
406
|
+
iMaxTaskTime = Date.now() + iMaxTaskDuration;
|
|
407
|
+
resolve();
|
|
408
|
+
}, 0);
|
|
409
|
+
}, {
|
|
410
|
+
once: true
|
|
411
|
+
});
|
|
412
|
+
oNextTaskMessageChannel.port1.postMessage(null);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return pWaitForNextTask;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Creates a function which schedules the execution of the given callback.
|
|
420
|
+
*
|
|
421
|
+
* The scheduling tries to limit the duration of browser tasks. When the configurable
|
|
422
|
+
* limit is reached, the creation of a new browser task is triggered and all subsequently
|
|
423
|
+
* scheduled callbacks will be postponed until the new browser task starts executing.
|
|
424
|
+
* In the new browser task, scheduling starts anew.
|
|
425
|
+
*
|
|
426
|
+
* The limit for the duration of browser tasks is configured via `iMaxTaskDuration`.
|
|
427
|
+
* By setting `iMaxTaskDuration` to a negative value, the whole scheduling mechanism is
|
|
428
|
+
* switched off. In that case, the returned function will execute the callback immediately.
|
|
429
|
+
*
|
|
430
|
+
* If a value of zero is set, each callback will be executed in a separate browser task.
|
|
431
|
+
* For preloaded modules, this essentially mimics the browser behavior of single file loading,
|
|
432
|
+
* but without the network and server delays.
|
|
433
|
+
*
|
|
434
|
+
* For larger values, at least one callback will be executed in each new browser task. When,
|
|
435
|
+
* after the execution of the callback, the configured threshold has been reached, all further
|
|
436
|
+
* callbacks will be postponed.
|
|
437
|
+
*
|
|
438
|
+
* Note: This is a heuristic only. Neither is the measurement of the task duration accurate,
|
|
439
|
+
* nor is there a way to know in advance the execution time of a callback.
|
|
440
|
+
*
|
|
441
|
+
* @param {function(any):void} fnCallback
|
|
442
|
+
* Function to schedule
|
|
443
|
+
* @returns {function(any):void}
|
|
444
|
+
* A function to call instead of the original callback; it takes care of scheduling
|
|
445
|
+
* and executing the original callback.
|
|
446
|
+
* @private
|
|
447
|
+
*/
|
|
448
|
+
function scheduleExecution(fnCallback) {
|
|
449
|
+
if ( iMaxTaskDuration < 0 ) {
|
|
450
|
+
return fnCallback;
|
|
451
|
+
}
|
|
452
|
+
return function() {
|
|
453
|
+
if ( pWaitForNextTask == null ) {
|
|
454
|
+
fnCallback.call(undefined, arguments[0]);
|
|
455
|
+
|
|
456
|
+
// if time limit is reached now, postpone future task
|
|
457
|
+
if ( Date.now() >= iMaxTaskTime ) {
|
|
458
|
+
waitForNextTask();
|
|
459
|
+
}
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
pWaitForNextTask.then(scheduleExecution(fnCallback).bind(undefined, arguments[0]));
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// ---- Names and Paths -----------------------------------------------------------------------
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Name conversion function that converts a name in unified resource name syntax to a name in UI5 module name syntax.
|
|
470
|
+
* If the name cannot be converted (e.g. doesn't end with '.js'), then <code>undefined</code> is returned.
|
|
471
|
+
*
|
|
472
|
+
* @param {string} sName Name in unified resource name syntax
|
|
473
|
+
* @returns {string|undefined} Name in UI5 (legacy) module name syntax (dot separated)
|
|
474
|
+
* or <code>undefined</code> when the name can't be converted
|
|
475
|
+
* @private
|
|
476
|
+
*/
|
|
477
|
+
function urnToUI5(sName) {
|
|
478
|
+
// UI5 module name syntax is only defined for JS resources
|
|
479
|
+
if ( !/\.js$/.test(sName) ) {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
sName = sName.slice(0, -3);
|
|
484
|
+
if ( /^jquery\.sap\./.test(sName) ) {
|
|
485
|
+
return sName; // do nothing
|
|
486
|
+
}
|
|
487
|
+
return sName.replace(/\//g, ".");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function urnToIDAndType(sResourceName) {
|
|
491
|
+
const basenamePos = sResourceName.lastIndexOf('/');
|
|
492
|
+
const dotPos = sResourceName.lastIndexOf('.');
|
|
493
|
+
|
|
494
|
+
if ( dotPos > basenamePos ) {
|
|
495
|
+
return {
|
|
496
|
+
id: sResourceName.slice(0, dotPos),
|
|
497
|
+
type: sResourceName.slice(dotPos)
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
id: sResourceName,
|
|
502
|
+
type: ''
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const rJSSubTypes = /(\.controller|\.fragment|\.view|\.designtime|\.support)?.js$/;
|
|
507
|
+
|
|
508
|
+
function urnToBaseIDAndSubType(sResourceName) {
|
|
509
|
+
const m = rJSSubTypes.exec(sResourceName);
|
|
510
|
+
if ( m ) {
|
|
511
|
+
return {
|
|
512
|
+
baseID: sResourceName.slice(0, m.index),
|
|
513
|
+
subType: m[0]
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const rDotSegmentAnywhere = /(?:^|\/)\.+(?=\/|$)/;
|
|
519
|
+
const rDotSegment = /^\.*$/;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Normalizes a resource name by resolving any relative name segments.
|
|
523
|
+
*
|
|
524
|
+
* A segment consisting of a single dot <code>./</code>, when used at the beginning of a name refers
|
|
525
|
+
* to the containing package of the <code>sBaseName</code>. When used inside a name, it is ignored.
|
|
526
|
+
*
|
|
527
|
+
* A segment consisting of two dots <code>../</code> refers to the parent package. It can be used
|
|
528
|
+
* anywhere in a name, but the resolved name prefix up to that point must not be empty.
|
|
529
|
+
*
|
|
530
|
+
* Example: A name <code>../common/validation.js</code> defined in <code>sap/myapp/controller/mycontroller.controller.js</code>
|
|
531
|
+
* will resolve to <code>sap/myapp/common/validation.js</code>.
|
|
532
|
+
*
|
|
533
|
+
* When <code>sBaseName</code> is <code>null</code> (e.g. for a <code>sap.ui.require</code> call),
|
|
534
|
+
* the resource name must not start with a relative name segment or an error will be thrown.
|
|
535
|
+
*
|
|
536
|
+
* @param {string} sResourceName Name to resolve
|
|
537
|
+
* @param {string|null} sBaseName Name of a reference module relative to which the name will be resolved
|
|
538
|
+
* @returns {string} Resolved name
|
|
539
|
+
* @throws {Error} When a relative name should be resolved but not basename is given;
|
|
540
|
+
* or when upward navigation (../) is requested on the root level
|
|
541
|
+
* or when a name segment consists of 3 or more dots only
|
|
542
|
+
* @private
|
|
543
|
+
*/
|
|
544
|
+
function normalize(sResourceName, sBaseName) {
|
|
545
|
+
|
|
546
|
+
const p = sResourceName.search(rDotSegmentAnywhere);
|
|
547
|
+
|
|
548
|
+
// check whether the name needs to be resolved at all - if not, just return the sModuleName as it is.
|
|
549
|
+
if ( p < 0 ) {
|
|
550
|
+
return sResourceName;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// if the name starts with a relative segment then there must be a base name (a global sap.ui.require doesn't support relative names)
|
|
554
|
+
if ( p === 0 ) {
|
|
555
|
+
if ( sBaseName == null ) {
|
|
556
|
+
throw new Error("relative name not supported ('" + sResourceName + "'");
|
|
557
|
+
}
|
|
558
|
+
// prefix module name with the parent package
|
|
559
|
+
sResourceName = sBaseName.slice(0, sBaseName.lastIndexOf('/') + 1) + sResourceName;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const aSegments = sResourceName.split('/');
|
|
563
|
+
|
|
564
|
+
// process path segments
|
|
565
|
+
let j = 0;
|
|
566
|
+
const l = aSegments.length;
|
|
567
|
+
for (let i = 0; i < l; i++) {
|
|
568
|
+
|
|
569
|
+
const sSegment = aSegments[i];
|
|
570
|
+
|
|
571
|
+
if ( rDotSegment.test(sSegment) ) {
|
|
572
|
+
if (sSegment === '.' || sSegment === '') {
|
|
573
|
+
// ignore '.' as it's just a pointer to current package. ignore '' as it results from double slashes (ignored by browsers as well)
|
|
574
|
+
continue;
|
|
575
|
+
} else if (sSegment === '..') {
|
|
576
|
+
// move to parent directory
|
|
577
|
+
if ( j === 0 ) {
|
|
578
|
+
throw new Error("Can't navigate to parent of root ('" + sResourceName + "')");
|
|
579
|
+
}
|
|
580
|
+
j--;
|
|
581
|
+
} else {
|
|
582
|
+
throw new Error("Illegal path segment '" + sSegment + "' ('" + sResourceName + "')");
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
aSegments[j++] = sSegment;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
aSegments.length = j;
|
|
591
|
+
|
|
592
|
+
return aSegments.join('/');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Adds a resource path to the resources map.
|
|
597
|
+
*
|
|
598
|
+
* @param {string} sResourceNamePrefix prefix is used as map key
|
|
599
|
+
* @param {string} sUrlPrefix path to the resource
|
|
600
|
+
*/
|
|
601
|
+
function registerResourcePath(sResourceNamePrefix, sUrlPrefix) {
|
|
602
|
+
sResourceNamePrefix = String(sResourceNamePrefix || "");
|
|
603
|
+
|
|
604
|
+
if ( sUrlPrefix == null ) {
|
|
605
|
+
|
|
606
|
+
// remove a registered URL prefix, if it wasn't for the empty resource name prefix
|
|
607
|
+
if ( sResourceNamePrefix ) {
|
|
608
|
+
if ( mUrlPrefixes[sResourceNamePrefix] ) {
|
|
609
|
+
delete mUrlPrefixes[sResourceNamePrefix];
|
|
610
|
+
log.info(`registerResourcePath ('${sResourceNamePrefix}') (registration removed)`);
|
|
611
|
+
}
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// otherwise restore the default
|
|
616
|
+
sUrlPrefix = DEFAULT_BASE_URL;
|
|
617
|
+
log.info(`registerResourcePath ('${sResourceNamePrefix}') (default registration restored)`);
|
|
618
|
+
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// cast to string and remove query parameters and/or hash
|
|
622
|
+
sUrlPrefix = pathOnly(String(sUrlPrefix));
|
|
623
|
+
|
|
624
|
+
// ensure that the prefix ends with a '/'
|
|
625
|
+
if ( sUrlPrefix.slice(-1) !== '/' ) {
|
|
626
|
+
sUrlPrefix += '/';
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
mUrlPrefixes[sResourceNamePrefix] = {
|
|
630
|
+
url: sUrlPrefix,
|
|
631
|
+
// calculate absolute URL, only to be used by 'guessResourceName'
|
|
632
|
+
absoluteUrl: resolveURL(sUrlPrefix)
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Retrieves path to a given resource by finding the longest matching prefix for the resource name
|
|
638
|
+
*
|
|
639
|
+
* @param {string} sResourceName name of the resource stored in the resources map
|
|
640
|
+
* @param {string} sSuffix url suffix
|
|
641
|
+
*
|
|
642
|
+
* @returns {string} resource path
|
|
643
|
+
*/
|
|
644
|
+
function getResourcePath(sResourceName, sSuffix) {
|
|
645
|
+
const startsWithSlash = sResourceName.startsWith("/")
|
|
646
|
+
let sNamePrefix = sResourceName;
|
|
647
|
+
let p = sResourceName.length;
|
|
648
|
+
|
|
649
|
+
// search for a registered name prefix, starting with the full name and successively removing one segment
|
|
650
|
+
while ( p > 0 && !mUrlPrefixes[sNamePrefix] ) {
|
|
651
|
+
p = sNamePrefix.lastIndexOf('/');
|
|
652
|
+
// Note: an empty segment at p = 0 (leading slash) will be ignored
|
|
653
|
+
sNamePrefix = p > 0 ? sNamePrefix.slice(0, p) : '';
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
assert((p > 0 || sNamePrefix === '') && mUrlPrefixes[sNamePrefix], "there always must be a mapping");
|
|
657
|
+
|
|
658
|
+
let sPath = mUrlPrefixes[sNamePrefix].url + sResourceName.slice(p + 1); // also skips a leading slash!
|
|
659
|
+
|
|
660
|
+
//remove trailing slash
|
|
661
|
+
if ( sPath.slice(-1) === '/' ) {
|
|
662
|
+
sPath = sPath.slice(0, -1);
|
|
663
|
+
}
|
|
664
|
+
if(startsWithSlash) {
|
|
665
|
+
sPath = `/${sPath}`
|
|
666
|
+
}
|
|
667
|
+
return sPath + (sSuffix || '');
|
|
668
|
+
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Returns the reporting mode for synchronous calls
|
|
673
|
+
*
|
|
674
|
+
* @returns {int} sync call behavior
|
|
675
|
+
*/
|
|
676
|
+
function getSyncCallBehavior() {
|
|
677
|
+
return syncCallBehavior;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Try to find a resource name that would be mapped to the given URL.
|
|
682
|
+
*
|
|
683
|
+
* If multiple path mappings would create a match, the returned name is not necessarily
|
|
684
|
+
* the best (longest) match. The first match which is found, will be returned.
|
|
685
|
+
*
|
|
686
|
+
* When <code>bLoadedResourcesOnly</code> is set, only those resources will be taken
|
|
687
|
+
* into account for which content has been loaded already.
|
|
688
|
+
*
|
|
689
|
+
* @param {string} sURL URL to guess the resource name for
|
|
690
|
+
* @param {boolean} [bLoadedResourcesOnly=false] Whether the guess should be limited to already loaded resources
|
|
691
|
+
* @returns {string|undefined} Resource name or <code>undefined</code> if no matching name could be found
|
|
692
|
+
* @private
|
|
693
|
+
*/
|
|
694
|
+
function guessResourceName(sURL, bLoadedResourcesOnly) {
|
|
695
|
+
// Make sure to have an absolute URL without query parameters or hash
|
|
696
|
+
// to check against absolute prefix URLs
|
|
697
|
+
sURL = pathOnly(resolveURL(sURL));
|
|
698
|
+
|
|
699
|
+
for (const sNamePrefix in mUrlPrefixes) {
|
|
700
|
+
|
|
701
|
+
// Note: configured URL prefixes are guaranteed to end with a '/'
|
|
702
|
+
// But to support the legacy scenario promoted by the application tools ( "registerModulePath('Application','Application')" )
|
|
703
|
+
// the prefix check here has to be done without the slash
|
|
704
|
+
const sUrlPrefix = mUrlPrefixes[sNamePrefix].absoluteUrl.slice(0, -1);
|
|
705
|
+
|
|
706
|
+
if ( sURL.startsWith(sUrlPrefix) ) {
|
|
707
|
+
|
|
708
|
+
// calc resource name
|
|
709
|
+
let sResourceName = sNamePrefix + sURL.slice(sUrlPrefix.length);
|
|
710
|
+
// remove a leading '/' (occurs if name prefix is empty and if match was a full segment match
|
|
711
|
+
if ( sResourceName.charAt(0) === '/' ) {
|
|
712
|
+
sResourceName = sResourceName.slice(1);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if ( !bLoadedResourcesOnly || mModules[sResourceName]?.data != undefined ) {
|
|
716
|
+
return sResourceName;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Find the most specific map config that matches the given context resource
|
|
724
|
+
* @param {string} sContext Resource name to be used as context
|
|
725
|
+
* @returns {Object<string,string>|undefined} Most specific map or <code>undefined</code>
|
|
726
|
+
*/
|
|
727
|
+
function findMapForContext(sContext) {
|
|
728
|
+
let p, mMap;
|
|
729
|
+
if ( sContext != null ) {
|
|
730
|
+
// maps are defined on module IDs, reduce URN to module ID
|
|
731
|
+
sContext = urnToIDAndType(sContext).id;
|
|
732
|
+
p = sContext.length;
|
|
733
|
+
mMap = mMaps[sContext];
|
|
734
|
+
while ( p > 0 && mMap == null ) {
|
|
735
|
+
p = sContext.lastIndexOf('/');
|
|
736
|
+
if ( p > 0 ) { // Note: an empty segment at p = 0 (leading slash) will be ignored
|
|
737
|
+
sContext = sContext.slice(0, p);
|
|
738
|
+
mMap = mMaps[sContext];
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// if none is found, fallback to '*' map
|
|
743
|
+
return mMap || mMaps['*'];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function getMappedName(sResourceName, sRequestingResourceName) {
|
|
747
|
+
var addSlash = false
|
|
748
|
+
if(sRequestingResourceName?.startsWith("/") && sResourceName.startsWith(".")) {
|
|
749
|
+
addSlash = true
|
|
750
|
+
}
|
|
751
|
+
const mMap = findMapForContext(sRequestingResourceName);
|
|
752
|
+
|
|
753
|
+
// resolve relative names
|
|
754
|
+
sResourceName = normalize(sResourceName, sRequestingResourceName);
|
|
755
|
+
if(addSlash) {
|
|
756
|
+
sResourceName = `/${sResourceName}`
|
|
757
|
+
}
|
|
758
|
+
// if there's a map, search for the most specific matching entry
|
|
759
|
+
if ( mMap != null ) {
|
|
760
|
+
// start with the full ID and successively remove one segment
|
|
761
|
+
let sPrefix = urnToIDAndType(sResourceName).id;
|
|
762
|
+
let p = sPrefix.length;
|
|
763
|
+
while ( p > 0 && mMap[sPrefix] == null ) {
|
|
764
|
+
p = sPrefix.lastIndexOf('/');
|
|
765
|
+
// Note: an empty segment at p = 0 (leading slash) will be ignored
|
|
766
|
+
sPrefix = p > 0 ? sPrefix.slice(0, p) : '';
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if ( p > 0 ) {
|
|
770
|
+
const sMappedResourceName = mMap[sPrefix] + sResourceName.slice(p);
|
|
771
|
+
if ( log.isLoggable() ) {
|
|
772
|
+
log.debug(`module ID ${sResourceName} mapped to ${sMappedResourceName}`);
|
|
773
|
+
}
|
|
774
|
+
return sMappedResourceName; // also skips a leading slash!
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return sResourceName;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function getGlobalObject(oObject, aNames, l, bCreate) {
|
|
782
|
+
for (let i = 0; oObject && i < l; i++) {
|
|
783
|
+
if (!oObject[aNames[i]] && bCreate ) {
|
|
784
|
+
oObject[aNames[i]] = {};
|
|
785
|
+
}
|
|
786
|
+
oObject = oObject[aNames[i]];
|
|
787
|
+
}
|
|
788
|
+
return oObject;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function getGlobalProperty(sName) {
|
|
792
|
+
const aNames = sName ? sName.split(".") : [];
|
|
793
|
+
|
|
794
|
+
if ( syncCallBehavior && aNames.length > 1 ) {
|
|
795
|
+
log.error("[nosync] getGlobalProperty called to retrieve global name '" + sName + "'");
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return getGlobalObject(__global, aNames, aNames.length);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function setGlobalProperty(sName, vValue) {
|
|
802
|
+
const aNames = sName ? sName.split(".") : [];
|
|
803
|
+
|
|
804
|
+
if ( aNames.length > 0 ) {
|
|
805
|
+
const oObject = getGlobalObject(__global, aNames, aNames.length - 1, true);
|
|
806
|
+
oObject[aNames[aNames.length - 1]] = vValue;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ---- Modules -------------------------------------------------------------------------------
|
|
811
|
+
|
|
812
|
+
function wrapExport(value) {
|
|
813
|
+
return { moduleExport: value };
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function unwrapExport(wrapper) {
|
|
817
|
+
return wrapper.moduleExport;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Module neither has been required nor preloaded nor declared, but someone asked for it.
|
|
822
|
+
*/
|
|
823
|
+
const INITIAL = 0,
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Module has been preloaded, but not required or declared.
|
|
827
|
+
*/
|
|
828
|
+
PRELOADED = -1,
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Module has been declared.
|
|
832
|
+
*/
|
|
833
|
+
LOADING = 1,
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Module has been loaded, but not yet executed.
|
|
837
|
+
*/
|
|
838
|
+
LOADED = 2,
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Module is currently being executed
|
|
842
|
+
*/
|
|
843
|
+
EXECUTING = 3,
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Module has been loaded and executed without errors.
|
|
847
|
+
*/
|
|
848
|
+
READY = 4,
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Module either could not be loaded or execution threw an error
|
|
852
|
+
*/
|
|
853
|
+
FAILED = 5,
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Special content value used internally until the content of a module has been determined
|
|
857
|
+
*/
|
|
858
|
+
NOT_YET_DETERMINED = {};
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* A module/resource as managed by the module system.
|
|
862
|
+
*
|
|
863
|
+
* Each module has the following properties
|
|
864
|
+
* <ul>
|
|
865
|
+
* <li>{int} state one of the module states defined in this function</li>
|
|
866
|
+
* <li>{string} url URL where the module has been loaded from</li>
|
|
867
|
+
* <li>{any} data temp. raw content of the module (between loaded and ready or when preloaded)</li>
|
|
868
|
+
* <li>{string} group the bundle with which a resource was loaded or null</li>
|
|
869
|
+
* <li>{string} error an error description for state <code>FAILED</code></li>
|
|
870
|
+
* <li>{any} content the content of the module as exported via define()<(li>
|
|
871
|
+
* </ul>
|
|
872
|
+
*/
|
|
873
|
+
class Module {
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Creates a new Module.
|
|
877
|
+
*
|
|
878
|
+
* @param {string} name Name of the module, including extension
|
|
879
|
+
*/
|
|
880
|
+
constructor(name) {
|
|
881
|
+
this.name = name;
|
|
882
|
+
this.state = INITIAL;
|
|
883
|
+
/*
|
|
884
|
+
* Whether processing of the module is complete.
|
|
885
|
+
* This is very similar to, but not the same as state >= READY because declareModule() sets state=READY very early.
|
|
886
|
+
* That state transition is 'legacy' from the library-all files; it needs to be checked whether it can be removed.
|
|
887
|
+
*/
|
|
888
|
+
this.settled = false;
|
|
889
|
+
this.url =
|
|
890
|
+
this._deferred =
|
|
891
|
+
this.data =
|
|
892
|
+
this.group =
|
|
893
|
+
this.error =
|
|
894
|
+
this.pending = null;
|
|
895
|
+
this.content = NOT_YET_DETERMINED;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
deferred() {
|
|
899
|
+
if ( this._deferred == null ) {
|
|
900
|
+
const deferred = this._deferred = {};
|
|
901
|
+
deferred.promise = new Promise(function(resolve,reject) {
|
|
902
|
+
deferred.resolve = resolve;
|
|
903
|
+
deferred.reject = reject;
|
|
904
|
+
});
|
|
905
|
+
// avoid 'Uncaught (in promise)' log entries
|
|
906
|
+
deferred.promise.catch(noop);
|
|
907
|
+
}
|
|
908
|
+
return this._deferred;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
api() {
|
|
912
|
+
this._api ??= {
|
|
913
|
+
id: this.name.slice(0,-3),
|
|
914
|
+
exports: this._exports = {},
|
|
915
|
+
url: this.url,
|
|
916
|
+
config: noop
|
|
917
|
+
};
|
|
918
|
+
return this._api;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Sets the module state to READY and either determines the value or sets
|
|
923
|
+
* it from the given parameter.
|
|
924
|
+
* @param {any} value Module value
|
|
925
|
+
*/
|
|
926
|
+
ready(value) {
|
|
927
|
+
// should throw, but some tests and apps would fail
|
|
928
|
+
assert(!this.settled, `Module ${this.name} is already settled`);
|
|
929
|
+
this.state = READY;
|
|
930
|
+
this.settled = true;
|
|
931
|
+
if ( arguments.length > 0 ) {
|
|
932
|
+
// check arguments.length to allow a value of undefined
|
|
933
|
+
this.content = value;
|
|
934
|
+
}
|
|
935
|
+
this.deferred().resolve(wrapExport(this.value()));
|
|
936
|
+
if ( this.aliases ) {
|
|
937
|
+
value = this.value();
|
|
938
|
+
this.aliases.forEach((alias) => Module.get(alias).ready(value));
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
failWith(msg, cause) {
|
|
943
|
+
const err = makeModuleError(msg, this, cause);
|
|
944
|
+
this.fail(err);
|
|
945
|
+
return err;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
fail(err) {
|
|
949
|
+
// should throw, but some tests and apps would fail
|
|
950
|
+
assert(!this.settled, `Module ${this.name} is already settled`);
|
|
951
|
+
this.settled = true;
|
|
952
|
+
if ( this.state !== FAILED ) {
|
|
953
|
+
this.state = FAILED;
|
|
954
|
+
this.error = err;
|
|
955
|
+
this.deferred().reject(err);
|
|
956
|
+
this.aliases?.forEach((alias) => Module.get(alias).fail(err));
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
addPending(sDependency) {
|
|
961
|
+
(this.pending ??= []).push(sDependency);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
addAlias(sAliasName) {
|
|
965
|
+
(this.aliases ??= []).push(sAliasName);
|
|
966
|
+
// add this module as pending dependency to the original
|
|
967
|
+
Module.get(sAliasName).addPending(this.name);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
preload(url, data, bundle) {
|
|
971
|
+
if ( this.state === INITIAL && !fnIgnorePreload?.(this.name) ) {
|
|
972
|
+
this.state = PRELOADED;
|
|
973
|
+
this.url = url;
|
|
974
|
+
this.data = data;
|
|
975
|
+
this.group = bundle;
|
|
976
|
+
}
|
|
977
|
+
return this;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* Determines the value of this module.
|
|
982
|
+
*
|
|
983
|
+
* If the module hasn't been loaded or executed yet, <code>undefined</code> will be returned.
|
|
984
|
+
*
|
|
985
|
+
* @returns {any} Export of the module or <code>undefined</code>
|
|
986
|
+
* @private
|
|
987
|
+
*/
|
|
988
|
+
value() {
|
|
989
|
+
if ( this.state === READY ) {
|
|
990
|
+
if ( this.content === NOT_YET_DETERMINED ) {
|
|
991
|
+
// Determine the module value lazily.
|
|
992
|
+
// For AMD modules this has already been done on execution of the factory function.
|
|
993
|
+
// For other modules that are required synchronously, it has been done after execution.
|
|
994
|
+
// For the few remaining scenarios (like global scripts), it is done here
|
|
995
|
+
const oShim = mShims[this.name],
|
|
996
|
+
sExport = oShim && (Array.isArray(oShim.exports) ? oShim.exports[0] : oShim.exports);
|
|
997
|
+
// best guess for thirdparty modules or legacy modules that don't use sap.ui.define
|
|
998
|
+
if(this.name === "sap/ui/thirdparty/crossroads.js") {
|
|
999
|
+
this.content = window.crossroads;
|
|
1000
|
+
} else {
|
|
1001
|
+
this.content = getGlobalProperty( sExport || urnToUI5(this.name) );
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
}
|
|
1005
|
+
return this.content;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
return undefined;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Checks whether this module depends on the given module.
|
|
1013
|
+
*
|
|
1014
|
+
* When a module definition (define) is executed, the requested dependencies are added
|
|
1015
|
+
* as 'pending' to the Module instance. This function checks if the oDependantModule is
|
|
1016
|
+
* reachable from this module when following the pending dependency information.
|
|
1017
|
+
*
|
|
1018
|
+
* Note: when module aliases are introduced (all module definitions in a file use an ID that differs
|
|
1019
|
+
* from the request module ID), then the alias module is also added as a "pending" dependency.
|
|
1020
|
+
*
|
|
1021
|
+
* @param {Module} oDependantModule Module which has a dependency to <code>oModule</code>
|
|
1022
|
+
* @returns {boolean} Whether this module depends on the given one.
|
|
1023
|
+
* @private
|
|
1024
|
+
*/
|
|
1025
|
+
dependsOn(oDependantModule) {
|
|
1026
|
+
const dependant = oDependantModule.name,
|
|
1027
|
+
visited = Object.create(null);
|
|
1028
|
+
|
|
1029
|
+
// log.debug("checking for a cycle between", this.name, "and", dependant);
|
|
1030
|
+
function visit(mod) {
|
|
1031
|
+
if ( !visited[mod] ) {
|
|
1032
|
+
// log.debug(" ", mod);
|
|
1033
|
+
visited[mod] = true;
|
|
1034
|
+
const pending = mModules[mod]?.pending;
|
|
1035
|
+
return Array.isArray(pending) &&
|
|
1036
|
+
(pending.indexOf(dependant) >= 0 || pending.some(visit));
|
|
1037
|
+
}
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
return this.name === dependant || visit(this.name);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Find or create a module by its unified resource name.
|
|
1046
|
+
*
|
|
1047
|
+
* If the module doesn't exist yet, a new one is created in state INITIAL.
|
|
1048
|
+
*
|
|
1049
|
+
* @param {string} sModuleName Name of the module in URN syntax
|
|
1050
|
+
* @returns {Module} Module with that name, newly created if it didn't exist yet
|
|
1051
|
+
*/
|
|
1052
|
+
static get(sModuleName) {
|
|
1053
|
+
const oModule = mModules[sModuleName] ??= new Module(sModuleName);
|
|
1054
|
+
return oModule;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/*
|
|
1060
|
+
* Determines the currently executing module.
|
|
1061
|
+
*/
|
|
1062
|
+
function getExecutingModule() {
|
|
1063
|
+
if ( _execStack.length > 0 ) {
|
|
1064
|
+
return _execStack[_execStack.length - 1].name;
|
|
1065
|
+
}
|
|
1066
|
+
return document.currentScript?.getAttribute("data-sap-ui-module");
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// --------------------------------------------------------------------------------------------
|
|
1070
|
+
|
|
1071
|
+
let _globalDefine = () => {
|
|
1072
|
+
},
|
|
1073
|
+
_globalDefineAMD;
|
|
1074
|
+
|
|
1075
|
+
function updateDefineAndInterceptAMDFlag(newDefine) {
|
|
1076
|
+
|
|
1077
|
+
// no change, do nothing
|
|
1078
|
+
if ( _globalDefine === newDefine ) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// first cleanup on an old loader
|
|
1083
|
+
if ( _globalDefine ) {
|
|
1084
|
+
_globalDefine.amd = _globalDefineAMD;
|
|
1085
|
+
_globalDefine =
|
|
1086
|
+
_globalDefineAMD = undefined;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// remember the new define
|
|
1090
|
+
_globalDefine = newDefine;
|
|
1091
|
+
|
|
1092
|
+
// intercept access to the 'amd' property of the new define, if it's not our own define
|
|
1093
|
+
if ( newDefine && !newDefine.ui5 ) {
|
|
1094
|
+
_globalDefineAMD = _globalDefine.amd;
|
|
1095
|
+
|
|
1096
|
+
Object.defineProperty(_globalDefine, "amd", {
|
|
1097
|
+
get: function() {
|
|
1098
|
+
const sCurrentModule = getExecutingModule();
|
|
1099
|
+
if ( sCurrentModule && mShims[sCurrentModule]?.amd ) {
|
|
1100
|
+
log.debug(`suppressing define.amd for ${sCurrentModule}`);
|
|
1101
|
+
return undefined;
|
|
1102
|
+
}
|
|
1103
|
+
return _globalDefineAMD;
|
|
1104
|
+
},
|
|
1105
|
+
set: function(newDefineAMD) {
|
|
1106
|
+
_globalDefineAMD = newDefineAMD;
|
|
1107
|
+
log.debug(`define.amd became ${newDefineAMD ? "active" : "unset"}`);
|
|
1108
|
+
},
|
|
1109
|
+
configurable: true // we have to allow a redefine for debug mode or restart from CDN etc.
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
try {
|
|
1115
|
+
Object.defineProperty(__global, "define", {
|
|
1116
|
+
get: function() {
|
|
1117
|
+
|
|
1118
|
+
return global.__globalDefine;
|
|
1119
|
+
//return _globalDefine;
|
|
1120
|
+
},
|
|
1121
|
+
set: function(newDefine) {
|
|
1122
|
+
global.__globalDefine = newDefine;
|
|
1123
|
+
this.__globalDefine = newDefine;
|
|
1124
|
+
updateDefineAndInterceptAMDFlag(newDefine);
|
|
1125
|
+
log.debug(`define became ${newDefine ? "active" : "unset"}`);
|
|
1126
|
+
},
|
|
1127
|
+
configurable: true // we have to allow a redefine for debug mode or restart from CDN etc.
|
|
1128
|
+
});
|
|
1129
|
+
} catch (e) {
|
|
1130
|
+
log.warning("could not intercept changes to window.define, ui5loader won't be able to a change of the AMD loader");
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
updateDefineAndInterceptAMDFlag(__global.define);
|
|
1134
|
+
|
|
1135
|
+
// --------------------------------------------------------------------------------------------
|
|
1136
|
+
|
|
1137
|
+
function isModuleError(err) {
|
|
1138
|
+
return err?.name === "ModuleError";
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Wraps the given 'cause' in a new error with the given message and with name 'ModuleError'.
|
|
1143
|
+
*
|
|
1144
|
+
* The new message and the message of the cause are combined. The stacktrace of the
|
|
1145
|
+
* new error and of the cause are combined (with a separating 'Caused by').
|
|
1146
|
+
*
|
|
1147
|
+
* Instead of the final message string, a template is provided which can contain placeholders
|
|
1148
|
+
* for the module ID ({id}) and module URL ({url}). Providing a template without concrete
|
|
1149
|
+
* values allows to detect the repeated nesting of the same error. In such a case, only
|
|
1150
|
+
* the innermost cause will be kept (affects both, stack trace as well as the cause property).
|
|
1151
|
+
* The message, however, will contain the full chain of module IDs.
|
|
1152
|
+
*
|
|
1153
|
+
* @param {string} template Message string template with placeholders
|
|
1154
|
+
* @param {Module} module Module for which the error occurred
|
|
1155
|
+
* @param {Error} cause original error
|
|
1156
|
+
* @returns {Error} New module error
|
|
1157
|
+
*/
|
|
1158
|
+
function makeModuleError(template, module, cause) {
|
|
1159
|
+
let modules = `'${module.name}'`;
|
|
1160
|
+
|
|
1161
|
+
if (isModuleError(cause)) {
|
|
1162
|
+
// update the chain of modules (increasing the indent)
|
|
1163
|
+
modules += `\n -> ${cause._modules.replace(/ -> /g, " -> ")}`;
|
|
1164
|
+
// omit repeated occurrences of the same kind of error
|
|
1165
|
+
if ( template === cause._template ) {
|
|
1166
|
+
cause = cause.cause;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// create the message string from the template and the cause's message
|
|
1171
|
+
const message =
|
|
1172
|
+
template.replace(/\{id\}/, modules).replace(/\{url\}/, module.url)
|
|
1173
|
+
+ (cause ? ": " + cause.message : "");
|
|
1174
|
+
//console.log(cause.stack);
|
|
1175
|
+
const error = new Error(message);
|
|
1176
|
+
error.name = "ModuleError";
|
|
1177
|
+
error.cause = cause;
|
|
1178
|
+
if ( cause?.stack ) {
|
|
1179
|
+
error.stack = error.stack + "\nCaused by: " + cause.stack;
|
|
1180
|
+
}
|
|
1181
|
+
// the following properties are only for internal usage
|
|
1182
|
+
error._template = template;
|
|
1183
|
+
error._modules = modules;
|
|
1184
|
+
return error;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
function declareModule(sModuleName) {
|
|
1188
|
+
// sModuleName must be a unified resource name of type .js
|
|
1189
|
+
assert(/\.js$/.test(sModuleName), "must be a Javascript module");
|
|
1190
|
+
|
|
1191
|
+
const oModule = Module.get(sModuleName);
|
|
1192
|
+
|
|
1193
|
+
if ( oModule.state > INITIAL ) {
|
|
1194
|
+
return oModule;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
if ( log.isLoggable() ) {
|
|
1198
|
+
log.debug(`${sLogPrefix}declare module '${sModuleName}'`);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// avoid cycles
|
|
1202
|
+
oModule.state = READY;
|
|
1203
|
+
|
|
1204
|
+
return oModule;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* Define an already loaded module synchronously.
|
|
1209
|
+
* Finds or creates a module by its unified resource name and resolves it with the given value.
|
|
1210
|
+
*
|
|
1211
|
+
* @param {string} sResourceName Name of the module in URN syntax
|
|
1212
|
+
* @param {any} vValue Content of the module
|
|
1213
|
+
*/
|
|
1214
|
+
function defineModuleSync(sResourceName, vValue) {
|
|
1215
|
+
Module.get(sResourceName).ready(vValue);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Queue of modules for which sap.ui.define has been called (in async mode), but which have not been executed yet.
|
|
1220
|
+
* When loading modules via script tag, only the onload handler knows the relationship between executed sap.ui.define calls and
|
|
1221
|
+
* module name. It then resolves the pending modules in the queue. Only one entry can get the name of the module
|
|
1222
|
+
* if there are more entries, then this is an error
|
|
1223
|
+
*
|
|
1224
|
+
* @param {boolean} [nested] Whether this is a nested queue used during sync execution of a module
|
|
1225
|
+
*/
|
|
1226
|
+
function ModuleDefinitionQueue(nested) {
|
|
1227
|
+
let aQueue = [],
|
|
1228
|
+
iRun = 0,
|
|
1229
|
+
vTimer;
|
|
1230
|
+
|
|
1231
|
+
this.push = function(name, deps, factory, _export) {
|
|
1232
|
+
if ( log.isLoggable() ) {
|
|
1233
|
+
log.debug(sLogPrefix + "pushing define() call"
|
|
1234
|
+
+ (document.currentScript ? " from " + document.currentScript.src : "")
|
|
1235
|
+
+ " to define queue #" + iRun);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
const sModule = document.currentScript?.getAttribute('data-sap-ui-module');
|
|
1239
|
+
aQueue.push({
|
|
1240
|
+
name: name,
|
|
1241
|
+
deps: deps,
|
|
1242
|
+
factory: factory,
|
|
1243
|
+
_export: _export,
|
|
1244
|
+
guess: sModule
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
// trigger queue processing via a timer in case the currently executing script is not managed by the loader
|
|
1248
|
+
if ( !vTimer && !nested && sModule == null ) {
|
|
1249
|
+
vTimer = setTimeout(this.process.bind(this, null, "timer"));
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
this.clear = function() {
|
|
1254
|
+
aQueue = [];
|
|
1255
|
+
if ( vTimer ) {
|
|
1256
|
+
clearTimeout(vTimer);
|
|
1257
|
+
vTimer = null;
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Process the queue of module definitions, assuming that the original request was for
|
|
1263
|
+
* <code>oRequestedModule</code>. If there is an unnamed module definition, it is assumed to be
|
|
1264
|
+
* the one for the requested module.
|
|
1265
|
+
*
|
|
1266
|
+
* When called via timer, <code>oRequestedModule</code> will be undefined.
|
|
1267
|
+
*
|
|
1268
|
+
* @param {Module} [oRequestedModule] Module for which the current script was loaded.
|
|
1269
|
+
* @param {string} [sInitiator] A string describing the caller of <code>process</code>
|
|
1270
|
+
*/
|
|
1271
|
+
this.process = function(oRequestedModule, sInitiator) {
|
|
1272
|
+
const bLoggable = log.isLoggable();
|
|
1273
|
+
const aQueueCopy = aQueue;
|
|
1274
|
+
const iCurrentRun = iRun++;
|
|
1275
|
+
let sModuleName = null;
|
|
1276
|
+
|
|
1277
|
+
// clear the queue and timer early, we've already taken a copy of the queue
|
|
1278
|
+
this.clear();
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
// if a module execution error was detected, stop processing the queue
|
|
1282
|
+
if ( oRequestedModule?.execError ) {
|
|
1283
|
+
if ( bLoggable ) {
|
|
1284
|
+
log.debug(`module execution error detected, ignoring queued define calls (${aQueueCopy.length})`);
|
|
1285
|
+
}
|
|
1286
|
+
oRequestedModule.fail(oRequestedModule.execError);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/*
|
|
1291
|
+
* Name of the requested module, null when unknown or already consumed.
|
|
1292
|
+
*
|
|
1293
|
+
* - when no module request is known (e.g. script was embedded in the page as an unmanaged script tag),
|
|
1294
|
+
* then no name is known and unnamed module definitions will be reported as an error
|
|
1295
|
+
* - multiple unnamed module definitions also are reported as an error
|
|
1296
|
+
* - when the name of a named module definition matches the name of requested module, the name is 'consumed'.
|
|
1297
|
+
* Any later unnamed module definition will be reported as an error, too
|
|
1298
|
+
*/
|
|
1299
|
+
sModuleName = oRequestedModule?.name;
|
|
1300
|
+
|
|
1301
|
+
// check whether there's a module definition for the requested module
|
|
1302
|
+
aQueueCopy.forEach((oEntry) => {
|
|
1303
|
+
if ( oEntry.name == null ) {
|
|
1304
|
+
if ( sModuleName != null ) {
|
|
1305
|
+
oEntry.name = sModuleName;
|
|
1306
|
+
sModuleName = null;
|
|
1307
|
+
} else {
|
|
1308
|
+
// multiple modules have been queued, but only one module can inherit the name from the require call
|
|
1309
|
+
if ( strictModuleDefinitions ) {
|
|
1310
|
+
const oError = new Error(
|
|
1311
|
+
"Modules that use an anonymous define() call must be loaded with a require() call; " +
|
|
1312
|
+
"they must not be executed via script tag or nested into other modules. ");
|
|
1313
|
+
if ( oRequestedModule ) {
|
|
1314
|
+
oRequestedModule.fail(oError);
|
|
1315
|
+
} else {
|
|
1316
|
+
throw oError;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
// give anonymous modules a unique pseudo ID
|
|
1320
|
+
oEntry.name = `~anonymous~${++iAnonymousModuleCount}.js`;
|
|
1321
|
+
log.error(
|
|
1322
|
+
"Modules that use an anonymous define() call must be loaded with a require() call; " +
|
|
1323
|
+
"they must not be executed via script tag or nested into other modules. " +
|
|
1324
|
+
"All other usages will fail in future releases or when standard AMD loaders are used. " +
|
|
1325
|
+
"Now using substitute name " + oEntry.name);
|
|
1326
|
+
}
|
|
1327
|
+
} else if ( oRequestedModule && oEntry.name === oRequestedModule.name ) {
|
|
1328
|
+
if ( sModuleName == null && !strictModuleDefinitions ) {
|
|
1329
|
+
// if 'strictModuleDefinitions' is active, double execution will be reported anyhow
|
|
1330
|
+
log.error(
|
|
1331
|
+
"Duplicate module definition: both, an unnamed module and a module with the expected name exist." +
|
|
1332
|
+
"This use case will fail in future releases or when standard AMD loaders are used. ");
|
|
1333
|
+
}
|
|
1334
|
+
sModuleName = null;
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
// if not, assign an alias if there's at least one queued module definition
|
|
1339
|
+
if ( sModuleName && aQueueCopy.length > 0 ) {
|
|
1340
|
+
if ( bLoggable ) {
|
|
1341
|
+
log.debug(
|
|
1342
|
+
"No queued module definition matches the ID of the request. " +
|
|
1343
|
+
`Now assuming that the first definition '${aQueueCopy[0].name}' is an alias of '${sModuleName}'`);
|
|
1344
|
+
}
|
|
1345
|
+
Module.get(aQueueCopy[0].name).addAlias(sModuleName);
|
|
1346
|
+
sModuleName = null;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if ( bLoggable ) {
|
|
1350
|
+
log.debug(sLogPrefix + "[" + sInitiator + "] "
|
|
1351
|
+
+ "processing define queue #" + iCurrentRun
|
|
1352
|
+
+ (oRequestedModule ? " for '" + oRequestedModule.name + "'" : "")
|
|
1353
|
+
+ ` with entries [${aQueueCopy.map((entry) => `'${entry.name}'`)}]`);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
aQueueCopy.forEach((oEntry) => {
|
|
1357
|
+
// start to resolve the dependencies
|
|
1358
|
+
executeModuleDefinition(oEntry.name, oEntry.deps, oEntry.factory, oEntry._export, /* bAsync = */ true);
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
if ( sModuleName != null && !oRequestedModule.settled ) {
|
|
1362
|
+
// module name still not consumed, might be a non-UI5 module (e.g. in 'global' format)
|
|
1363
|
+
if ( bLoggable ) {
|
|
1364
|
+
log.debug(sLogPrefix + "no queued module definition for the requested module found, assume the module to be ready");
|
|
1365
|
+
}
|
|
1366
|
+
oRequestedModule.data = undefined; // allow GC
|
|
1367
|
+
oRequestedModule.ready(); // no export known, has to be retrieved via global name
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
if ( bLoggable ) {
|
|
1371
|
+
log.debug(sLogPrefix + `processing define queue #${iCurrentRun} done`);
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
let queue = new ModuleDefinitionQueue();
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* Loads the source for the given module with a sync XHR.
|
|
1380
|
+
* @param {Module} oModule Module to load the source for
|
|
1381
|
+
* @throws {Error} When loading failed for some reason.
|
|
1382
|
+
*/
|
|
1383
|
+
function loadSyncXHR(oModule) {
|
|
1384
|
+
const xhr = new XMLHttpRequest();
|
|
1385
|
+
|
|
1386
|
+
function createXHRLoadError(error) {
|
|
1387
|
+
error = new Error(xhr.statusText ? xhr.status + " - " + xhr.statusText : xhr.status);
|
|
1388
|
+
error.name = "XHRLoadError";
|
|
1389
|
+
error.status = xhr.status;
|
|
1390
|
+
error.statusText = xhr.statusText;
|
|
1391
|
+
return error;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
xhr.addEventListener('load', function(e) {
|
|
1395
|
+
// File protocol (file://) always has status code 0
|
|
1396
|
+
if ( xhr.status === 200 || xhr.status === 0 ) {
|
|
1397
|
+
oModule.state = LOADED;
|
|
1398
|
+
oModule.data = xhr.responseText;
|
|
1399
|
+
} else {
|
|
1400
|
+
oModule.error = createXHRLoadError();
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
// Note: according to whatwg spec, error event doesn't fire for sync send(), instead an error is thrown
|
|
1404
|
+
// we register a handler, in case a browser doesn't follow the spec
|
|
1405
|
+
xhr.addEventListener('error', function(e) {
|
|
1406
|
+
oModule.error = createXHRLoadError();
|
|
1407
|
+
});
|
|
1408
|
+
xhr.open('GET', oModule.url, false);
|
|
1409
|
+
try {
|
|
1410
|
+
xhr.send();
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
oModule.error = error;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Global event handler to detect script execution errors.
|
|
1418
|
+
* @private
|
|
1419
|
+
*/
|
|
1420
|
+
window.addEventListener('error', function onUncaughtError(errorEvent) {
|
|
1421
|
+
var sModuleName = document.currentScript?.getAttribute('data-sap-ui-module');
|
|
1422
|
+
var oModule = sModuleName && Module.get(sModuleName);
|
|
1423
|
+
if ( oModule && oModule.execError == null ) {
|
|
1424
|
+
// if a currently executing module can be identified, attach the error to it and suppress reporting
|
|
1425
|
+
if ( log.isLoggable() ) {
|
|
1426
|
+
log.debug(`unhandled exception occurred while executing ${sModuleName}: ${errorEvent.message}`);
|
|
1427
|
+
}
|
|
1428
|
+
oModule.execError = errorEvent.error || {
|
|
1429
|
+
name: 'Error',
|
|
1430
|
+
message: errorEvent.message
|
|
1431
|
+
};
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
function loadScript(oModule, sAlternativeURL) {
|
|
1437
|
+
|
|
1438
|
+
const oScript = document.createElement('SCRIPT');
|
|
1439
|
+
// Accessing the 'src' property of the script in this strange way prevents Safari 12 (or WebKit) from
|
|
1440
|
+
// wrongly optimizing access. SF12 seems to check at optimization time whether there's a setter for the
|
|
1441
|
+
// property and optimize accordingly. When a setter is defined or changed at a later point in time (e.g.
|
|
1442
|
+
// by the AppCacheBuster), then the optimization seems not to be updated and the new setter is ignored
|
|
1443
|
+
// BCP 1970035485
|
|
1444
|
+
oScript["s" + "rc"] = oModule.url;
|
|
1445
|
+
//oScript.src = oModule.url;
|
|
1446
|
+
oScript.setAttribute("data-sap-ui-module", oModule.name);
|
|
1447
|
+
|
|
1448
|
+
function onload(e) {
|
|
1449
|
+
updateMaxTaskTime();
|
|
1450
|
+
if ( log.isLoggable() ) {
|
|
1451
|
+
log.debug(`JavaScript resource loaded: ${oModule.name}`);
|
|
1452
|
+
}
|
|
1453
|
+
oScript.removeEventListener('load', onload);
|
|
1454
|
+
oScript.removeEventListener('error', onerror);
|
|
1455
|
+
queue.process(oModule, "onload");
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function onerror(e) {
|
|
1459
|
+
updateMaxTaskTime();
|
|
1460
|
+
oScript.removeEventListener('load', onload);
|
|
1461
|
+
oScript.removeEventListener('error', onerror);
|
|
1462
|
+
if (sAlternativeURL) {
|
|
1463
|
+
log.warning(`retry loading JavaScript resource: ${oModule.name}`);
|
|
1464
|
+
oScript?.parentNode?.removeChild(oScript);
|
|
1465
|
+
oModule.url = sAlternativeURL;
|
|
1466
|
+
loadScript(oModule, /* sAlternativeURL= */ null);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
log.error(`failed to load JavaScript resource: ${oModule.name}`);
|
|
1471
|
+
oModule.failWith("failed to load {id} from {url}", new Error("script load error"));
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
if ( sAlternativeURL !== undefined ) {
|
|
1475
|
+
if ( mShims[oModule.name]?.amd ) {
|
|
1476
|
+
oScript.setAttribute("data-sap-ui-module-amd", "true");
|
|
1477
|
+
}
|
|
1478
|
+
oScript.addEventListener('load', onload);
|
|
1479
|
+
oScript.addEventListener('error', onerror);
|
|
1480
|
+
}
|
|
1481
|
+
document.head.appendChild(oScript);
|
|
1482
|
+
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
function preloadDependencies(sModuleName) {
|
|
1486
|
+
const knownDependencies = mDepCache[sModuleName];
|
|
1487
|
+
if ( Array.isArray(knownDependencies) ) {
|
|
1488
|
+
log.debug(`preload dependencies for ${sModuleName}: ${knownDependencies}`);
|
|
1489
|
+
knownDependencies.forEach((dep) => {
|
|
1490
|
+
dep = getMappedName(dep, sModuleName);
|
|
1491
|
+
if ( /\.js$/.test(dep) ) {
|
|
1492
|
+
requireModule(null, dep, /* always async */ true);
|
|
1493
|
+
} // else: TODO handle non-JS resources, e.g. link rel=prefetch
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Loads the given module if needed and returns the module export or a promise on it.
|
|
1500
|
+
*
|
|
1501
|
+
* If loading is still ongoing for the requested module and if there is a cycle detected between
|
|
1502
|
+
* the requesting module and the module to be loaded, then <code>undefined</code> (or a promise on
|
|
1503
|
+
* <code>undefined</code>) will be returned as intermediate module export to resolve the cycle.
|
|
1504
|
+
*
|
|
1505
|
+
* @param {Module} oRequestingModule The module in whose context the new module has to be loaded;
|
|
1506
|
+
* this is needed to detect cycles
|
|
1507
|
+
* @param {string} sModuleName Name of the module to be loaded, in URN form and with '.js' extension
|
|
1508
|
+
* @param {boolean} bAsync Whether the operation can be executed asynchronously
|
|
1509
|
+
* @param {boolean} [bSkipShimDeps=false] Whether shim dependencies should be ignored (used by recursive calls)
|
|
1510
|
+
* @param {boolean} [bSkipBundle=false] Whether bundle information should be ignored (used by recursive calls)
|
|
1511
|
+
* @returns {any|Promise} Returns the module export in sync mode or a promise on it in async mode
|
|
1512
|
+
* @throws {Error} When loading failed in sync mode
|
|
1513
|
+
*
|
|
1514
|
+
* @private
|
|
1515
|
+
*/
|
|
1516
|
+
function requireModule(oRequestingModule, sModuleName, bAsync, bSkipShimDeps, bSkipBundle) {
|
|
1517
|
+
|
|
1518
|
+
// only for robustness, should not be possible by design (all callers append '.js')
|
|
1519
|
+
const oSplitName = urnToBaseIDAndSubType(sModuleName);
|
|
1520
|
+
if ( !oSplitName ) {
|
|
1521
|
+
throw new Error(`can only require Javascript module, not ${sModuleName}`);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// Module names should not start with a "/"
|
|
1525
|
+
if (sModuleName[0] == "/") {
|
|
1526
|
+
log.debug("Module names that start with a slash should not be used, as they are reserved for future use.");
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
const bLoggable = log.isLoggable();
|
|
1530
|
+
|
|
1531
|
+
const oModule = Module.get(sModuleName);
|
|
1532
|
+
const oShim = mShims[sModuleName];
|
|
1533
|
+
|
|
1534
|
+
// when there's a shim with dependencies for the module
|
|
1535
|
+
// resolve them first before requiring the module again with bSkipShimDeps = true
|
|
1536
|
+
if ( oShim?.deps && !bSkipShimDeps ) {
|
|
1537
|
+
if ( bLoggable ) {
|
|
1538
|
+
log.debug("require dependencies of raw module " + sModuleName);
|
|
1539
|
+
}
|
|
1540
|
+
return requireAll(oModule, oShim.deps, function() {
|
|
1541
|
+
// set bSkipShimDeps to true to prevent endless recursion
|
|
1542
|
+
return requireModule(oRequestingModule, sModuleName, bAsync, /* bSkipShimDeps = */ true, bSkipBundle);
|
|
1543
|
+
}, function(oErr) {
|
|
1544
|
+
// Note: in async mode, this 'throw' will reject the promise returned by requireAll
|
|
1545
|
+
throw oModule.failWith("Failed to resolve dependencies of {id}", oErr);
|
|
1546
|
+
}, bAsync);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// when there's bundle information for the module
|
|
1550
|
+
// require the bundle first before requiring the module again with bSkipBundle = true
|
|
1551
|
+
if ( oModule.state === INITIAL && oModule.group && oModule.group !== sModuleName && !bSkipBundle ) {
|
|
1552
|
+
if ( bLoggable ) {
|
|
1553
|
+
log.debug(`${sLogPrefix}require bundle '${oModule.group}' containing '${sModuleName}'`);
|
|
1554
|
+
}
|
|
1555
|
+
if ( bAsync ) {
|
|
1556
|
+
return requireModule(null, oModule.group, bAsync).catch(noop).then(function() {
|
|
1557
|
+
// set bSkipBundle to true to prevent endless recursion
|
|
1558
|
+
return requireModule(oRequestingModule, sModuleName, bAsync, bSkipShimDeps, /* bSkipBundle = */ true);
|
|
1559
|
+
});
|
|
1560
|
+
} else {
|
|
1561
|
+
try {
|
|
1562
|
+
requireModule(null, oModule.group, bAsync);
|
|
1563
|
+
} catch (oError) {
|
|
1564
|
+
if ( bLoggable ) {
|
|
1565
|
+
log.error(sLogPrefix + "require bundle '" + oModule.group + "' failed (ignored)");
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if ( bLoggable ) {
|
|
1572
|
+
log.debug(sLogPrefix + "require '" + sModuleName + "'"
|
|
1573
|
+
+ (oRequestingModule ? " (dependency of '" + oRequestingModule.name + "')" : ""));
|
|
1574
|
+
}
|
|
1575
|
+
if(oModule.name === "sap/ui/thirdparty/hasher.js" && oModule.state === PRELOADED) {
|
|
1576
|
+
oModule.state = INITIAL
|
|
1577
|
+
}
|
|
1578
|
+
if(oModule.name === "sap/ui/fl/library.js") {
|
|
1579
|
+
bAsync = false;
|
|
1580
|
+
}
|
|
1581
|
+
// check if module has been loaded already
|
|
1582
|
+
if ( oModule.state !== INITIAL ) {
|
|
1583
|
+
|
|
1584
|
+
let bExecutedNow = false;
|
|
1585
|
+
|
|
1586
|
+
if ( oModule.state === EXECUTING && oModule.data != null && !bAsync && oModule.async ) {
|
|
1587
|
+
oModule.state = PRELOADED;
|
|
1588
|
+
oModule.async = bAsync;
|
|
1589
|
+
oModule.pending = null; // TODO or is this still needed ?
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if ( oModule.state === PRELOADED ) {
|
|
1593
|
+
oModule.state = LOADED;
|
|
1594
|
+
oModule.async = bAsync;
|
|
1595
|
+
bExecutedNow = true;
|
|
1596
|
+
measure && measure.start(sModuleName, "Require module " + sModuleName + " (preloaded)", ["require"]);
|
|
1597
|
+
execModule(sModuleName, bAsync);
|
|
1598
|
+
measure && measure.end(sModuleName);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
if ( oModule.state === READY ) {
|
|
1602
|
+
if ( !bExecutedNow && bLoggable ) {
|
|
1603
|
+
log.debug(sLogPrefix + "module '" + sModuleName + "' has already been loaded (skipped).");
|
|
1604
|
+
}
|
|
1605
|
+
// Note: this intentionally does not return oModule.promise() as the export might be temporary in case of cycles
|
|
1606
|
+
// or it might have changed after repeated module execution
|
|
1607
|
+
return bAsync ? Promise.resolve(wrapExport(oModule.value())) : wrapExport(oModule.value());
|
|
1608
|
+
} else if ( oModule.state === FAILED ) {
|
|
1609
|
+
if ( bAsync ) {
|
|
1610
|
+
return oModule.deferred().promise;
|
|
1611
|
+
} else {
|
|
1612
|
+
throw oModule.error;
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
// currently loading or executing
|
|
1616
|
+
if ( bAsync ) {
|
|
1617
|
+
// break up cyclic dependencies
|
|
1618
|
+
if ( oRequestingModule && oModule.dependsOn(oRequestingModule) ) {
|
|
1619
|
+
if ( log.isLoggable() ) {
|
|
1620
|
+
log.debug("cycle detected between '" + oRequestingModule.name + "' and '" + sModuleName + "', returning undefined for '" + sModuleName + "'");
|
|
1621
|
+
}
|
|
1622
|
+
// Note: this must be a separate promise as the fulfillment is not the final one
|
|
1623
|
+
return Promise.resolve(wrapExport(undefined));
|
|
1624
|
+
}
|
|
1625
|
+
return oModule.deferred().promise;
|
|
1626
|
+
}
|
|
1627
|
+
if ( !bAsync && !oModule.async ) {
|
|
1628
|
+
// sync pending, return undefined
|
|
1629
|
+
if ( log.isLoggable() ) {
|
|
1630
|
+
log.debug("cycle detected between '" + (oRequestingModule ? oRequestingModule.name : "unknown") + "' and '" + sModuleName + "', returning undefined for '" + sModuleName + "'");
|
|
1631
|
+
}
|
|
1632
|
+
return wrapExport(undefined);
|
|
1633
|
+
}
|
|
1634
|
+
// async pending, load sync again
|
|
1635
|
+
log.warning("Sync request triggered for '" + sModuleName + "' while async request was already pending." +
|
|
1636
|
+
" Loading a module twice might cause issues and should be avoided by fully migrating to async APIs.");
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
measure && measure.start(sModuleName, "Require module " + sModuleName, ["require"]);
|
|
1641
|
+
|
|
1642
|
+
// set marker for loading modules (to break cycles)
|
|
1643
|
+
oModule.state = LOADING;
|
|
1644
|
+
oModule.async = bAsync;
|
|
1645
|
+
|
|
1646
|
+
// if debug is enabled, try to load debug module first
|
|
1647
|
+
const aExtensions = bDebugSources ? ["-dbg", ""] : [""];
|
|
1648
|
+
if ( !bAsync ) {
|
|
1649
|
+
|
|
1650
|
+
for (let i = 0; i < aExtensions.length && oModule.state !== LOADED; i++) {
|
|
1651
|
+
// create module URL for the current extension
|
|
1652
|
+
oModule.url = getResourcePath(oSplitName.baseID, aExtensions[i] + oSplitName.subType);
|
|
1653
|
+
if ( bLoggable ) {
|
|
1654
|
+
log.debug(sLogPrefix + "loading " + (aExtensions[i] ? aExtensions[i] + " version of " : "") + "'" + sModuleName + "' from '" + oModule.url + "' (using sync XHR)");
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
if ( syncCallBehavior ) {
|
|
1658
|
+
const sMsg = "[nosync] loading module '" + oModule.url + "'";
|
|
1659
|
+
if ( syncCallBehavior === 1 ) {
|
|
1660
|
+
log.error(sMsg);
|
|
1661
|
+
} else {
|
|
1662
|
+
throw new Error(sMsg);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// call notification hook
|
|
1667
|
+
ui5Require.load({ completeLoad:noop, async: false }, oModule.url, oSplitName.baseID);
|
|
1668
|
+
|
|
1669
|
+
loadSyncXHR(oModule);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
if ( oModule.state === LOADING ) {
|
|
1673
|
+
// transition to FAILED
|
|
1674
|
+
oModule.failWith("failed to load {id} from {url}", oModule.error);
|
|
1675
|
+
} else if ( oModule.state === LOADED ) {
|
|
1676
|
+
// execute module __after__ loading it, this reduces the required stack space!
|
|
1677
|
+
execModule(sModuleName, bAsync);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
measure && measure.end(sModuleName);
|
|
1681
|
+
|
|
1682
|
+
if ( oModule.state !== READY ) {
|
|
1683
|
+
throw oModule.error;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
return wrapExport(oModule.value());
|
|
1687
|
+
|
|
1688
|
+
} else {
|
|
1689
|
+
|
|
1690
|
+
oModule.url = getResourcePath(oSplitName.baseID, aExtensions[0] + oSplitName.subType);
|
|
1691
|
+
// in debug mode, fall back to the non-dbg source, otherwise try the same source again (for SSO re-connect)
|
|
1692
|
+
const sAltUrl = bDebugSources ? getResourcePath(oSplitName.baseID, aExtensions[1] + oSplitName.subType) : oModule.url;
|
|
1693
|
+
|
|
1694
|
+
if ( log.isLoggable() ) {
|
|
1695
|
+
log.debug(sLogPrefix + "loading '" + sModuleName + "' from '" + oModule.url + "' (using <script>)");
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// call notification hook only once
|
|
1699
|
+
const filePath = global.pathMappingFn(sModuleName);
|
|
1700
|
+
|
|
1701
|
+
ui5Require.load({ completeLoad:noop, async: true }, sAltUrl, oSplitName.baseID);
|
|
1702
|
+
try {
|
|
1703
|
+
currentModuleName = sModuleName;
|
|
1704
|
+
_execStack.push({
|
|
1705
|
+
name: sModuleName,
|
|
1706
|
+
used: false
|
|
1707
|
+
});
|
|
1708
|
+
const requireOutput = global.requireFn(filePath)
|
|
1709
|
+
currentModuleName = undefined;
|
|
1710
|
+
queue.process(oModule, "onload");
|
|
1711
|
+
} catch(e) {
|
|
1712
|
+
oModule.failWith("failed to load {id} from {url}", new Error("script load error"));
|
|
1713
|
+
} finally {
|
|
1714
|
+
_execStack.pop();
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
//loadScript(oModule, /* sAlternativeURL= */ sAltUrl);
|
|
1719
|
+
|
|
1720
|
+
// process dep cache info
|
|
1721
|
+
preloadDependencies(sModuleName);
|
|
1722
|
+
|
|
1723
|
+
return oModule.deferred().promise;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// sModuleName must be a normalized resource name of type .js
|
|
1728
|
+
function execModule(sModuleName, bAsync) {
|
|
1729
|
+
|
|
1730
|
+
const oModule = mModules[sModuleName];
|
|
1731
|
+
|
|
1732
|
+
if ( oModule && oModule.state === LOADED && typeof oModule.data !== "undefined" ) {
|
|
1733
|
+
|
|
1734
|
+
const bLoggable = log.isLoggable();
|
|
1735
|
+
const bOldForceSyncDefines = bForceSyncDefines;
|
|
1736
|
+
const oOldQueue = queue;
|
|
1737
|
+
let sOldPrefix, sScript;
|
|
1738
|
+
|
|
1739
|
+
try {
|
|
1740
|
+
|
|
1741
|
+
bForceSyncDefines = !bAsync;
|
|
1742
|
+
queue = new ModuleDefinitionQueue(true);
|
|
1743
|
+
|
|
1744
|
+
if ( bLoggable ) {
|
|
1745
|
+
if ( typeof oModule.data === "string" ) {
|
|
1746
|
+
log.warning(sLogPrefix + "executing '" + sModuleName + "' (using eval)");
|
|
1747
|
+
} else {
|
|
1748
|
+
log.debug(sLogPrefix + "executing '" + sModuleName + "'");
|
|
1749
|
+
}
|
|
1750
|
+
sOldPrefix = sLogPrefix;
|
|
1751
|
+
sLogPrefix = sLogPrefix + ": ";
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// execute the script in the __global context
|
|
1755
|
+
oModule.state = EXECUTING;
|
|
1756
|
+
_execStack.push({
|
|
1757
|
+
name: sModuleName,
|
|
1758
|
+
used: false
|
|
1759
|
+
});
|
|
1760
|
+
if ( typeof oModule.data === "function" ) {
|
|
1761
|
+
const args = [];
|
|
1762
|
+
if( mShims[oModule.name] && mShims[oModule.name].deps) {
|
|
1763
|
+
mShims[oModule.name].deps.forEach((depName) => {
|
|
1764
|
+
args.push(Module.get(depName+ ".js").value())
|
|
1765
|
+
})
|
|
1766
|
+
}
|
|
1767
|
+
const output = oModule.data.call(__global);
|
|
1768
|
+
if(output && mShims[oModule.name] && mShims[oModule.name].exports && Object.keys(output).length > 0 && !global[mShims[oModule.name].exports]) {
|
|
1769
|
+
// if(global[mShims[oModule.name].exports] ) {
|
|
1770
|
+
// global[mShims[oModule.name].exports] = {...global[mShims[oModule.name].exports], ...output}
|
|
1771
|
+
// } else {
|
|
1772
|
+
global[mShims[oModule.name].exports] = output;
|
|
1773
|
+
//}
|
|
1774
|
+
|
|
1775
|
+
}
|
|
1776
|
+
else if(output && Object.keys(output).length > 0) {
|
|
1777
|
+
if( Object.keys(oModule.content).length === 0) {
|
|
1778
|
+
oModule.content = output;
|
|
1779
|
+
} else {
|
|
1780
|
+
oModule.content = {...oModule.content, ...output};
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
}
|
|
1784
|
+
} else if ( Array.isArray(oModule.data) ) {
|
|
1785
|
+
ui5Define.apply(null, oModule.data);
|
|
1786
|
+
} else {
|
|
1787
|
+
|
|
1788
|
+
sScript = oModule.data;
|
|
1789
|
+
|
|
1790
|
+
// sourceURL: Firebug, Chrome and Safari debugging help, appending the string seems to cost ZERO performance
|
|
1791
|
+
// Note: make URL absolute so Chrome displays the file tree correctly
|
|
1792
|
+
// Note: do not append if there is already a sourceURL / sourceMappingURL
|
|
1793
|
+
// Note: Safari fails, if sourceURL is the same as an existing XHR URL
|
|
1794
|
+
// Note: Chrome ignores debug files when the same URL has already been load via sourcemap of the bootstrap file (sap-ui-core)
|
|
1795
|
+
// Note: sourcemap annotations URLs in eval'ed sources are resolved relative to the page, not relative to the source
|
|
1796
|
+
if (sScript ) {
|
|
1797
|
+
const oMatch = /\/\/[#@] source(Mapping)?URL=(.*)$/.exec(sScript);
|
|
1798
|
+
if ( oMatch && oMatch[1] && /^[^/]+\.js\.map$/.test(oMatch[2]) ) {
|
|
1799
|
+
// found a sourcemap annotation with a typical UI5 generated relative URL
|
|
1800
|
+
sScript = sScript.slice(0, oMatch.index) + oMatch[0].slice(0, -oMatch[2].length) + resolveURL(oMatch[2], oModule.url);
|
|
1801
|
+
}
|
|
1802
|
+
// @evo-todo use only sourceMappingURL, sourceURL or both?
|
|
1803
|
+
if ( !oMatch || oMatch[1] ) {
|
|
1804
|
+
// write sourceURL if no annotation was there or when it was a sourceMappingURL
|
|
1805
|
+
sScript += "\n//# sourceURL=" + resolveURL(oModule.url) + "?eval";
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
// framework internal hook to intercept the loaded script and modify
|
|
1810
|
+
// it before executing the script - e.g. useful for client side coverage
|
|
1811
|
+
if (typeof translate === "function") {
|
|
1812
|
+
sScript = translate(sScript, sModuleName);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// eval the source in the global context (preventing access to the closure of this function)
|
|
1816
|
+
__global.eval(sScript);
|
|
1817
|
+
}
|
|
1818
|
+
queue.process(oModule, "after eval");
|
|
1819
|
+
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
oModule.data = undefined;
|
|
1822
|
+
if (isModuleError(err)) {
|
|
1823
|
+
// don't wrap a ModuleError again
|
|
1824
|
+
oModule.fail(err);
|
|
1825
|
+
} else {
|
|
1826
|
+
if (err instanceof SyntaxError && sScript) {
|
|
1827
|
+
// Module execution failed with a syntax error.
|
|
1828
|
+
// If in debug mode, load the script code again via script tag for better error reporting
|
|
1829
|
+
// (but without reacting to load/error events)
|
|
1830
|
+
if (fnIgnorePreload) {
|
|
1831
|
+
oModule.url = URL.createObjectURL(new Blob([sScript], {type: 'text/javascript'}));
|
|
1832
|
+
loadScript(oModule);
|
|
1833
|
+
} else {
|
|
1834
|
+
log.error("A syntax error occurred while evaluating '" + sModuleName + "'"
|
|
1835
|
+
+ ", restarting the app with sap-ui-debug=x might reveal the error location");
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
oModule.failWith("Failed to execute {id}", err);
|
|
1839
|
+
}
|
|
1840
|
+
} finally {
|
|
1841
|
+
|
|
1842
|
+
_execStack.pop();
|
|
1843
|
+
|
|
1844
|
+
if ( bLoggable ) {
|
|
1845
|
+
sLogPrefix = sOldPrefix;
|
|
1846
|
+
log.debug(sLogPrefix + "finished executing '" + sModuleName + "'");
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
queue = oOldQueue;
|
|
1850
|
+
bForceSyncDefines = bOldForceSyncDefines;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
function requireAll(oRequestingModule, aDependencies, fnCallback, fnErrCallback, bAsync) {
|
|
1856
|
+
|
|
1857
|
+
const aModules = [];
|
|
1858
|
+
let sBaseName,
|
|
1859
|
+
oError;
|
|
1860
|
+
|
|
1861
|
+
try {
|
|
1862
|
+
// calculate the base name for relative module names
|
|
1863
|
+
if ( oRequestingModule instanceof Module ) {
|
|
1864
|
+
sBaseName = oRequestingModule.name;
|
|
1865
|
+
} else {
|
|
1866
|
+
sBaseName = oRequestingModule;
|
|
1867
|
+
oRequestingModule = null;
|
|
1868
|
+
}
|
|
1869
|
+
aDependencies = aDependencies.slice();
|
|
1870
|
+
for (let i = 0; i < aDependencies.length; i++) {
|
|
1871
|
+
aDependencies[i] = getMappedName(aDependencies[i] + '.js', sBaseName);
|
|
1872
|
+
}
|
|
1873
|
+
if ( oRequestingModule ) {
|
|
1874
|
+
// remember outgoing dependencies to be able to detect cycles, but ignore pseudo-dependencies
|
|
1875
|
+
aDependencies.forEach((dep) => {
|
|
1876
|
+
if ( !/^(require|exports|module)\.js$/.test(dep) ) {
|
|
1877
|
+
oRequestingModule.addPending(dep);
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
for (let i = 0; i < aDependencies.length; i++) {
|
|
1883
|
+
const sDepModName = aDependencies[i];
|
|
1884
|
+
if ( oRequestingModule ) {
|
|
1885
|
+
switch ( sDepModName ) {
|
|
1886
|
+
case 'require.js':
|
|
1887
|
+
// the injected local require should behave like the Standard require (2nd argument = true)
|
|
1888
|
+
aModules[i] = wrapExport(createContextualRequire(sBaseName, true));
|
|
1889
|
+
break;
|
|
1890
|
+
case 'module.js':
|
|
1891
|
+
aModules[i] = wrapExport(oRequestingModule.api());
|
|
1892
|
+
break;
|
|
1893
|
+
case 'exports.js':
|
|
1894
|
+
oRequestingModule.api();
|
|
1895
|
+
aModules[i] = wrapExport(oRequestingModule._exports);
|
|
1896
|
+
break;
|
|
1897
|
+
default:
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
if ( !aModules[i] ) {
|
|
1902
|
+
aModules[i] = requireModule(oRequestingModule, sDepModName, bAsync);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
} catch (err) {
|
|
1907
|
+
oError = err;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
if ( bAsync ) {
|
|
1911
|
+
const oPromise = oError ? Promise.reject(oError) : Promise.all(aModules);
|
|
1912
|
+
return oPromise.then(fnCallback, fnErrCallback);
|
|
1913
|
+
} else {
|
|
1914
|
+
if ( oError ) {
|
|
1915
|
+
fnErrCallback(oError);
|
|
1916
|
+
} else {
|
|
1917
|
+
return fnCallback(aModules);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
function executeModuleDefinition(sResourceName, aDependencies, vFactory, bExport, bAsync) {
|
|
1923
|
+
const bLoggable = log.isLoggable();
|
|
1924
|
+
sResourceName = normalize(sResourceName);
|
|
1925
|
+
|
|
1926
|
+
if ( bLoggable ) {
|
|
1927
|
+
log.debug(sLogPrefix + "define('" + sResourceName + "', " + "['" + aDependencies.join("','") + "']" + ")");
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
const oModule = declareModule(sResourceName);
|
|
1931
|
+
|
|
1932
|
+
let repeatedExecutionReported = false;
|
|
1933
|
+
|
|
1934
|
+
function shouldSkipExecution() {
|
|
1935
|
+
if ( oModule.settled ) {
|
|
1936
|
+
// avoid double execution of the module, e.g. when async/sync conflict occurred before queue processing
|
|
1937
|
+
if ( oModule.state >= READY && bAsync && oModule.async === false ) {
|
|
1938
|
+
log.warning("Repeated module execution skipped after async/sync conflict for " + oModule.name);
|
|
1939
|
+
return true;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// when an inline module definition is executed repeatedly, this is reported but not prevented
|
|
1943
|
+
// Standard AMD loaders don't support this scenario, it needs to be fixed on caller side
|
|
1944
|
+
if ( strictModuleDefinitions && bAsync ) {
|
|
1945
|
+
log.warning("Module '" + oModule.name + "' has been defined more than once. " +
|
|
1946
|
+
"All but the first definition will be ignored, don't try to define the same module again.");
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
if ( !repeatedExecutionReported ) {
|
|
1951
|
+
log.error(
|
|
1952
|
+
"Module '" + oModule.name + "' is executed more than once. " +
|
|
1953
|
+
"This is an unsupported scenario and will fail in future versions of UI5 or " +
|
|
1954
|
+
"when a standard AMD loader is used. Don't define the same module again.");
|
|
1955
|
+
repeatedExecutionReported = true;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
if ( shouldSkipExecution() ) {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// avoid early evaluation of the module value
|
|
1965
|
+
oModule.content = undefined;
|
|
1966
|
+
|
|
1967
|
+
function onSuccess(aModules) {
|
|
1968
|
+
|
|
1969
|
+
// avoid double execution of the module, e.g. when async/sync conflict occurred while waiting for dependencies
|
|
1970
|
+
if ( shouldSkipExecution() ) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
// factory
|
|
1975
|
+
if ( bLoggable ) {
|
|
1976
|
+
log.debug(sLogPrefix + "define('" + sResourceName + "'): dependencies resolved, calling factory " + typeof vFactory);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
if ( bExport && syncCallBehavior !== 2 ) {
|
|
1980
|
+
// ensure parent namespace
|
|
1981
|
+
const aPackages = sResourceName.split('/');
|
|
1982
|
+
if ( aPackages.length > 1 ) {
|
|
1983
|
+
getGlobalObject(__global, aPackages, aPackages.length - 1, true);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
if ( typeof vFactory === 'function' ) {
|
|
1988
|
+
// from https://github.com/amdjs/amdjs-api/blob/master/AMD.md
|
|
1989
|
+
// "If the factory function returns a value (an object, function, or any value that coerces to true),
|
|
1990
|
+
// then that value should be assigned as the exported value for the module."
|
|
1991
|
+
try {
|
|
1992
|
+
aModules = aModules.map(unwrapExport);
|
|
1993
|
+
let exports = vFactory.apply(__global, aModules);
|
|
1994
|
+
if ( oModule._api?.exports !== undefined && oModule._api.exports !== oModule._exports ) {
|
|
1995
|
+
exports = oModule._api.exports;
|
|
1996
|
+
} else if ( exports === undefined && oModule._exports ) {
|
|
1997
|
+
exports = oModule._exports;
|
|
1998
|
+
}
|
|
1999
|
+
oModule.content = exports;
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
const wrappedError = oModule.failWith("failed to execute module factory for '{id}'", error);
|
|
2002
|
+
if ( bAsync ) {
|
|
2003
|
+
// Note: in async mode, the error is reported via the oModule's promise
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
throw wrappedError;
|
|
2007
|
+
}
|
|
2008
|
+
} else {
|
|
2009
|
+
oModule.content = vFactory;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
// HACK: global export
|
|
2013
|
+
if ( bExport && syncCallBehavior !== 2 ) {
|
|
2014
|
+
if ( oModule.content == null ) {
|
|
2015
|
+
log.error(`Module '${sResourceName}' returned no content, but should export to global?`);
|
|
2016
|
+
} else {
|
|
2017
|
+
if ( bLoggable ) {
|
|
2018
|
+
log.debug(`exporting content of '${sResourceName}': as global object`);
|
|
2019
|
+
}
|
|
2020
|
+
// convert module name to UI5 module name syntax (might fail!)
|
|
2021
|
+
const sModuleName = urnToUI5(sResourceName);
|
|
2022
|
+
setGlobalProperty(sModuleName, oModule.content);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
oModule.ready();
|
|
2027
|
+
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
// Note: dependencies will be resolved and converted from RJS to URN inside requireAll
|
|
2031
|
+
requireAll(oModule, aDependencies, bAsync && oModule.data ? scheduleExecution(onSuccess) : onSuccess, function(oErr) {
|
|
2032
|
+
const oWrappedError = oModule.failWith("Failed to resolve dependencies of {id}", oErr);
|
|
2033
|
+
if ( !bAsync ) {
|
|
2034
|
+
throw oWrappedError;
|
|
2035
|
+
}
|
|
2036
|
+
// Note: in async mode, the error is reported via the oModule's promise
|
|
2037
|
+
}, /* bAsync = */ bAsync);
|
|
2038
|
+
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
function ui5Define(sModuleName, aDependencies, vFactory, bExport) {
|
|
2042
|
+
let sResourceName;
|
|
2043
|
+
|
|
2044
|
+
// optional id
|
|
2045
|
+
if ( typeof sModuleName === 'string' ) {
|
|
2046
|
+
sResourceName = sModuleName + '.js';
|
|
2047
|
+
} else {
|
|
2048
|
+
// shift parameters
|
|
2049
|
+
bExport = vFactory;
|
|
2050
|
+
vFactory = aDependencies;
|
|
2051
|
+
aDependencies = sModuleName;
|
|
2052
|
+
sResourceName = null;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// optional array of dependencies
|
|
2056
|
+
if ( !Array.isArray(aDependencies) ) {
|
|
2057
|
+
// shift parameters
|
|
2058
|
+
bExport = vFactory;
|
|
2059
|
+
vFactory = aDependencies;
|
|
2060
|
+
if ( typeof vFactory === 'function' && vFactory.length > 0 ) {
|
|
2061
|
+
aDependencies = ['require', 'exports', 'module'].slice(0, vFactory.length);
|
|
2062
|
+
} else {
|
|
2063
|
+
aDependencies = [];
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// if ( bForceSyncDefines === false || (bForceSyncDefines == null && bGlobalAsyncMode) ) {
|
|
2068
|
+
// queue.push(sResourceName, aDependencies, vFactory, bExport);
|
|
2069
|
+
// if ( sResourceName != null ) {
|
|
2070
|
+
// const oModule = Module.get(sResourceName);
|
|
2071
|
+
// if ( oModule.state === INITIAL ) {
|
|
2072
|
+
// oModule.state = EXECUTING;
|
|
2073
|
+
// oModule.async = true;
|
|
2074
|
+
// }
|
|
2075
|
+
// }
|
|
2076
|
+
// return;
|
|
2077
|
+
// }
|
|
2078
|
+
|
|
2079
|
+
// immediate, synchronous execution
|
|
2080
|
+
const oCurrentExecInfo = _execStack.length > 0 ? _execStack[_execStack.length - 1] : null;
|
|
2081
|
+
if ( !sResourceName ) {
|
|
2082
|
+
|
|
2083
|
+
if ( oCurrentExecInfo && !oCurrentExecInfo.used ) {
|
|
2084
|
+
sResourceName = oCurrentExecInfo.name;
|
|
2085
|
+
oCurrentExecInfo.used = true;
|
|
2086
|
+
} else {
|
|
2087
|
+
// give anonymous modules a unique pseudo ID
|
|
2088
|
+
if(!oCurrentExecInfo) {
|
|
2089
|
+
sResourceName = global.testPath;
|
|
2090
|
+
} else if(currentModuleName) {
|
|
2091
|
+
sResourceName = currentModuleName;
|
|
2092
|
+
} else {
|
|
2093
|
+
sResourceName = `~anonymous~${++iAnonymousModuleCount}.js`;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
if ( oCurrentExecInfo ) {
|
|
2097
|
+
sResourceName = oCurrentExecInfo.name.slice(0, oCurrentExecInfo.name.lastIndexOf('/') + 1) + sResourceName;
|
|
2098
|
+
}
|
|
2099
|
+
log.error(
|
|
2100
|
+
"Modules that use an anonymous define() call must be loaded with a require() call; " +
|
|
2101
|
+
"they must not be executed via script tag or nested into other modules. " +
|
|
2102
|
+
"All other usages will fail in future releases or when standard AMD loaders are used " +
|
|
2103
|
+
"or when ui5loader runs in async mode. Now using substitute name " + sResourceName);
|
|
2104
|
+
}
|
|
2105
|
+
} else if ( oCurrentExecInfo?.used && sResourceName !== oCurrentExecInfo.name ) {
|
|
2106
|
+
log.debug(`module names don't match: requested: ${sModuleName}, defined: ${oCurrentExecInfo.name}`);
|
|
2107
|
+
Module.get(oCurrentExecInfo.name).addAlias(sModuleName);
|
|
2108
|
+
}
|
|
2109
|
+
executeModuleDefinition(sResourceName, aDependencies, vFactory, bExport, /* bAsync = */ false);
|
|
2110
|
+
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
/**
|
|
2114
|
+
* The amdDefine() function is closer to the AMD spec, as opposed to sap.ui.define.
|
|
2115
|
+
* It's later assigned as the global define() if the loader is running in amd=true
|
|
2116
|
+
* mode (has to be configured explicitly).
|
|
2117
|
+
*/
|
|
2118
|
+
function amdDefine(sModuleName, aDependencies, vFactory) {
|
|
2119
|
+
let oArgs = arguments;
|
|
2120
|
+
const bExportIsSet = typeof oArgs[oArgs.length - 1] === "boolean";
|
|
2121
|
+
|
|
2122
|
+
// bExport parameter is proprietary and should not be used for an AMD compliant define()
|
|
2123
|
+
if (bExportIsSet) {
|
|
2124
|
+
oArgs = Array.prototype.slice.call(oArgs, 0, oArgs.length - 1);
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
ui5Define.apply(this, oArgs);
|
|
2128
|
+
}
|
|
2129
|
+
amdDefine.amd = {}; // identify as AMD-spec compliant loader
|
|
2130
|
+
amdDefine.ui5 = {}; // identify as ui5loader
|
|
2131
|
+
|
|
2132
|
+
|
|
2133
|
+
/**
|
|
2134
|
+
* Create a require() function which acts in the context of the given resource.
|
|
2135
|
+
*
|
|
2136
|
+
* @param {string|null} sContextName Name of the context resource (module) in URN syntax, incl. extension
|
|
2137
|
+
* @param {boolean} bAMDCompliance If set to true, the behavior of the require() function is closer to the AMD specification.
|
|
2138
|
+
* @returns {function} Require function.
|
|
2139
|
+
*/
|
|
2140
|
+
function createContextualRequire(sContextName, bAMDCompliance) {
|
|
2141
|
+
const fnRequire = function(vDependencies, fnCallback, fnErrCallback) {
|
|
2142
|
+
assert(typeof vDependencies === 'string' || Array.isArray(vDependencies), "dependency param either must be a single string or an array of strings");
|
|
2143
|
+
assert(fnCallback == null || typeof fnCallback === 'function', "callback must be a function or null/undefined");
|
|
2144
|
+
assert(fnErrCallback == null || typeof fnErrCallback === 'function', "error callback must be a function or null/undefined");
|
|
2145
|
+
|
|
2146
|
+
// Probing for existing module
|
|
2147
|
+
if ( typeof vDependencies === 'string' ) {
|
|
2148
|
+
const sModuleName = getMappedName(vDependencies + '.js', sContextName);
|
|
2149
|
+
const oModule = Module.get(sModuleName);
|
|
2150
|
+
|
|
2151
|
+
// check the modules internal state
|
|
2152
|
+
// everything from PRELOADED to LOADED (incl. FAILED) is considered erroneous
|
|
2153
|
+
if (bAMDCompliance && oModule.state !== EXECUTING && oModule.state !== READY) {
|
|
2154
|
+
throw new Error(
|
|
2155
|
+
"Module '" + sModuleName + "' has not been loaded yet. " +
|
|
2156
|
+
"Use require(['" + sModuleName + "']) to load it."
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// Module is in state READY or EXECUTING; or require() was called from sap.ui.require().
|
|
2161
|
+
// A modules value might be undefined (no return statement) even though the state is READY.
|
|
2162
|
+
return oModule.value();
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
requireAll(sContextName, vDependencies, function(aModules) {
|
|
2166
|
+
aModules = aModules.map(unwrapExport);
|
|
2167
|
+
if ( typeof fnCallback === 'function' ) {
|
|
2168
|
+
if ( bGlobalAsyncMode ) {
|
|
2169
|
+
fnCallback.apply(__global, aModules);
|
|
2170
|
+
} else {
|
|
2171
|
+
// enforce asynchronous execution of callback even in sync mode
|
|
2172
|
+
simulateAsyncCallback(function() {
|
|
2173
|
+
fnCallback.apply(__global, aModules);
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}, function(oErr) {
|
|
2178
|
+
if ( typeof fnErrCallback === 'function' ) {
|
|
2179
|
+
if ( bGlobalAsyncMode ) {
|
|
2180
|
+
fnErrCallback.call(__global, oErr);
|
|
2181
|
+
} else {
|
|
2182
|
+
simulateAsyncCallback(function() {
|
|
2183
|
+
fnErrCallback.call(__global, oErr);
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
} else {
|
|
2187
|
+
throw oErr;
|
|
2188
|
+
}
|
|
2189
|
+
}, /* bAsync = */ bGlobalAsyncMode);
|
|
2190
|
+
|
|
2191
|
+
// return undefined;
|
|
2192
|
+
};
|
|
2193
|
+
fnRequire.toUrl = function(sName) {
|
|
2194
|
+
const sMappedName = ensureTrailingSlash(getMappedName(sName, sContextName), sName);
|
|
2195
|
+
return toUrl(sMappedName);
|
|
2196
|
+
};
|
|
2197
|
+
return fnRequire;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
function ensureTrailingSlash(sName, sInput) {
|
|
2201
|
+
//restore trailing slash
|
|
2202
|
+
if (sInput.slice(-1) === "/" && sName.slice(-1) !== "/") {
|
|
2203
|
+
return sName + "/";
|
|
2204
|
+
}
|
|
2205
|
+
return sName;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
function toUrl(sName) {
|
|
2209
|
+
if (sName.indexOf("/") === 0) {
|
|
2210
|
+
throw new Error(`The provided argument '${sName}' may not start with a slash`);
|
|
2211
|
+
}
|
|
2212
|
+
return ensureTrailingSlash(getResourcePath(sName), sName);
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
/*
|
|
2216
|
+
* UI5 version of require (sap.ui.require)
|
|
2217
|
+
*/
|
|
2218
|
+
const ui5Require = createContextualRequire(null, false);
|
|
2219
|
+
|
|
2220
|
+
/*
|
|
2221
|
+
* AMD version of require (window.require)
|
|
2222
|
+
*
|
|
2223
|
+
* Difference between require (sap.ui.require) and amdRequire (window.require):
|
|
2224
|
+
* - require("my/module"), returns undefined if the module was not loaded yet
|
|
2225
|
+
* - amdRequire("my/module"), throws an error if the module was not loaded yet
|
|
2226
|
+
*/
|
|
2227
|
+
const amdRequire = createContextualRequire(null, true);
|
|
2228
|
+
|
|
2229
|
+
function requireSync(sModuleName) {
|
|
2230
|
+
sModuleName = getMappedName(sModuleName + '.js');
|
|
2231
|
+
if ( log.isLoggable() ) {
|
|
2232
|
+
log.warning(`sync require of '${sModuleName}'`);
|
|
2233
|
+
}
|
|
2234
|
+
return unwrapExport(requireModule(null, sModuleName, /* bAsync = */ false));
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
function predefine(sModuleName, aDependencies, vFactory, bExport) {
|
|
2238
|
+
if ( typeof sModuleName !== 'string' ) {
|
|
2239
|
+
throw new Error("predefine requires a module name");
|
|
2240
|
+
}
|
|
2241
|
+
sModuleName = normalize(sModuleName);
|
|
2242
|
+
Module.get(sModuleName + '.js').preload("<unknown>/" + sModuleName, [sModuleName, aDependencies, vFactory, bExport], null);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
function preload(modules, group, url) {
|
|
2246
|
+
group = group || null;
|
|
2247
|
+
url = url || "<unknown>";
|
|
2248
|
+
for ( let name in modules ) {
|
|
2249
|
+
name = normalize(name);
|
|
2250
|
+
Module.get(name).preload(url + "/" + name, modules[name], group);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* Dumps information about the current set of modules and their state.
|
|
2256
|
+
*
|
|
2257
|
+
* @param {int} [iThreshold=-1] Earliest module state for which odules should be reported
|
|
2258
|
+
* @private
|
|
2259
|
+
*/
|
|
2260
|
+
function dumpInternals(iThreshold) {
|
|
2261
|
+
|
|
2262
|
+
const states = [PRELOADED, INITIAL, LOADED, READY, FAILED, EXECUTING, LOADING];
|
|
2263
|
+
const stateNames = {
|
|
2264
|
+
[PRELOADED]: 'PRELOADED',
|
|
2265
|
+
[INITIAL]:'INITIAL',
|
|
2266
|
+
[LOADING]: 'LOADING',
|
|
2267
|
+
[LOADED]: 'LOADED',
|
|
2268
|
+
[EXECUTING]: 'EXECUTING',
|
|
2269
|
+
[READY]: 'READY',
|
|
2270
|
+
[FAILED]: 'FAILED'
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
if ( iThreshold == null ) {
|
|
2274
|
+
iThreshold = PRELOADED;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
/*eslint-disable no-console */
|
|
2278
|
+
const info = log.isLoggable('INFO') ? log.info.bind(log) : console.info.bind(console);
|
|
2279
|
+
/*eslint-enable no-console */
|
|
2280
|
+
|
|
2281
|
+
const aModuleNames = Object.keys(mModules).sort();
|
|
2282
|
+
states.forEach((state) => {
|
|
2283
|
+
if ( state < iThreshold ) {
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
let count = 0;
|
|
2287
|
+
info(stateNames[state] + ":");
|
|
2288
|
+
aModuleNames.forEach((sModule, idx) => {
|
|
2289
|
+
const oModule = mModules[sModule];
|
|
2290
|
+
if ( oModule.state === state ) {
|
|
2291
|
+
let addtlInfo;
|
|
2292
|
+
if ( oModule.state === LOADING ) {
|
|
2293
|
+
const pending = oModule.pending?.reduce((acc, dep) => {
|
|
2294
|
+
const oDepModule = Module.get(dep);
|
|
2295
|
+
if ( oDepModule.state !== READY ) {
|
|
2296
|
+
acc.push( dep + "(" + stateNames[oDepModule.state] + ")");
|
|
2297
|
+
}
|
|
2298
|
+
return acc;
|
|
2299
|
+
}, []);
|
|
2300
|
+
if ( pending?.length > 0 ) {
|
|
2301
|
+
addtlInfo = "waiting for " + pending.join(", ");
|
|
2302
|
+
}
|
|
2303
|
+
} else if ( oModule.state === FAILED ) {
|
|
2304
|
+
addtlInfo = (oModule.error.name || "Error") + ": " + oModule.error.message;
|
|
2305
|
+
}
|
|
2306
|
+
info(" " + (idx + 1) + " " + sModule + (addtlInfo ? " (" + addtlInfo + ")" : ""));
|
|
2307
|
+
count++;
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
if ( count === 0 ) {
|
|
2311
|
+
info(" none");
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
/**
|
|
2318
|
+
* Returns a flat copy of the current set of URL prefixes.
|
|
2319
|
+
*
|
|
2320
|
+
* @private
|
|
2321
|
+
*/
|
|
2322
|
+
function getUrlPrefixes() {
|
|
2323
|
+
const mUrlPrefixesCopy = Object.create(null);
|
|
2324
|
+
forEach(mUrlPrefixes, function(sNamePrefix, oUrlInfo) {
|
|
2325
|
+
mUrlPrefixesCopy[sNamePrefix] = oUrlInfo.url;
|
|
2326
|
+
});
|
|
2327
|
+
return mUrlPrefixesCopy;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
/**
|
|
2331
|
+
* Removes a set of resources from the resource cache.
|
|
2332
|
+
*
|
|
2333
|
+
* @param {string} sName unified resource name of a resource or the name of a preload group to be removed
|
|
2334
|
+
* @param {boolean} [bPreloadGroup=true] whether the name specifies a preload group, defaults to true
|
|
2335
|
+
* @param {boolean} [bUnloadAll] Whether all matching resources should be unloaded, even if they have been executed already.
|
|
2336
|
+
* @param {boolean} [bDeleteExports] Whether exports (global variables) should be destroyed as well. Will be done for UI5 module names only.
|
|
2337
|
+
* @experimental Since 1.16.3 API might change completely, apps must not develop against it.
|
|
2338
|
+
* @private
|
|
2339
|
+
*/
|
|
2340
|
+
function unloadResources(sName, bPreloadGroup, bUnloadAll, bDeleteExports) {
|
|
2341
|
+
const aModules = [];
|
|
2342
|
+
|
|
2343
|
+
if ( bPreloadGroup == null ) {
|
|
2344
|
+
bPreloadGroup = true;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
if ( bPreloadGroup ) {
|
|
2348
|
+
// collect modules that belong to the given group
|
|
2349
|
+
for ( const sURN in mModules ) {
|
|
2350
|
+
const oModule = mModules[sURN];
|
|
2351
|
+
if ( oModule && oModule.group === sName ) {
|
|
2352
|
+
aModules.push(sURN);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
} else {
|
|
2356
|
+
// single module
|
|
2357
|
+
if ( mModules[sName] ) {
|
|
2358
|
+
aModules.push(sName);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
aModules.forEach((sURN) => {
|
|
2363
|
+
const oModule = mModules[sURN];
|
|
2364
|
+
if ( oModule && bDeleteExports && sURN.match(/\.js$/) ) {
|
|
2365
|
+
// @evo-todo move to compat layer?
|
|
2366
|
+
setGlobalProperty(urnToUI5(sURN), undefined);
|
|
2367
|
+
}
|
|
2368
|
+
if ( oModule && (bUnloadAll || oModule.state === PRELOADED) ) {
|
|
2369
|
+
delete mModules[sURN];
|
|
2370
|
+
}
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
function getModuleContent(name, url) {
|
|
2375
|
+
if ( name ) {
|
|
2376
|
+
name = getMappedName(name);
|
|
2377
|
+
} else {
|
|
2378
|
+
name = guessResourceName(url, true);
|
|
2379
|
+
}
|
|
2380
|
+
const oModule = name && mModules[name];
|
|
2381
|
+
if ( oModule ) {
|
|
2382
|
+
oModule.state = LOADED;
|
|
2383
|
+
return oModule.data;
|
|
2384
|
+
} else {
|
|
2385
|
+
return undefined;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/**
|
|
2390
|
+
* Returns an info about all known resources keyed by their URN.
|
|
2391
|
+
*
|
|
2392
|
+
* If the URN can be converted to a UI5 module name, then the value in the map
|
|
2393
|
+
* will be that name. Otherwise it will be null or undefined.
|
|
2394
|
+
*
|
|
2395
|
+
* @return {Object.<string,string>} Map of all module names keyed by their resource name
|
|
2396
|
+
* @see isDeclared
|
|
2397
|
+
* @private
|
|
2398
|
+
*/
|
|
2399
|
+
function getAllModules() {
|
|
2400
|
+
const mSnapshot = Object.create(null);
|
|
2401
|
+
forEach(mModules, function(sURN, oModule) {
|
|
2402
|
+
mSnapshot[sURN] = {
|
|
2403
|
+
state: oModule.state,
|
|
2404
|
+
ui5: urnToUI5(sURN)
|
|
2405
|
+
};
|
|
2406
|
+
});
|
|
2407
|
+
return mSnapshot;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
function loadJSResourceAsync(sResource, bIgnoreErrors) {
|
|
2411
|
+
sResource = getMappedName(sResource);
|
|
2412
|
+
const promise = requireModule(null, sResource, /* bAsync = */ true).then(unwrapExport);
|
|
2413
|
+
return bIgnoreErrors ? promise.catch(noop) : promise;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// ---- config --------------------------------------------------------------------------------
|
|
2417
|
+
|
|
2418
|
+
const mUI5ConfigHandlers = {
|
|
2419
|
+
baseUrl(url) {
|
|
2420
|
+
registerResourcePath("", url);
|
|
2421
|
+
},
|
|
2422
|
+
paths: registerResourcePath, // has length 2
|
|
2423
|
+
shim(module, shim) {
|
|
2424
|
+
if ( Array.isArray(shim) ) {
|
|
2425
|
+
shim = { deps : shim };
|
|
2426
|
+
}
|
|
2427
|
+
mShims[module + '.js'] = shim;
|
|
2428
|
+
},
|
|
2429
|
+
amd(bValue) {
|
|
2430
|
+
bValue = !!bValue;
|
|
2431
|
+
if ( bExposeAsAMDLoader !== bValue ) {
|
|
2432
|
+
bExposeAsAMDLoader = bValue;
|
|
2433
|
+
if (bValue) {
|
|
2434
|
+
vOriginalDefine = __global.define;
|
|
2435
|
+
vOriginalRequire = __global.require;
|
|
2436
|
+
__global.define = amdDefine;
|
|
2437
|
+
__global.require = amdRequire;
|
|
2438
|
+
|
|
2439
|
+
// Enable async loading behaviour implicitly when switching to amd mode
|
|
2440
|
+
bGlobalAsyncMode = true;
|
|
2441
|
+
} else {
|
|
2442
|
+
__global.define = vOriginalDefine;
|
|
2443
|
+
__global.require = vOriginalRequire;
|
|
2444
|
+
// NOTE: Do not set async mode back to false when amd mode gets deactivated
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
},
|
|
2448
|
+
async(async) {
|
|
2449
|
+
if (bGlobalAsyncMode && !async) {
|
|
2450
|
+
throw new Error("Changing the ui5loader config from async to sync is not supported. Only a change from sync to async is allowed.");
|
|
2451
|
+
}
|
|
2452
|
+
bGlobalAsyncMode = !!async;
|
|
2453
|
+
},
|
|
2454
|
+
bundles(bundle, modules) {
|
|
2455
|
+
bundle += '.js';
|
|
2456
|
+
modules.forEach(
|
|
2457
|
+
(module) => { Module.get(module + '.js').group = bundle; }
|
|
2458
|
+
);
|
|
2459
|
+
},
|
|
2460
|
+
bundlesUI5(bundle, resources) {
|
|
2461
|
+
resources.forEach(
|
|
2462
|
+
(module) => { Module.get(module).group = bundle; }
|
|
2463
|
+
);
|
|
2464
|
+
},
|
|
2465
|
+
debugSources(debug) {
|
|
2466
|
+
bDebugSources = !!debug;
|
|
2467
|
+
},
|
|
2468
|
+
depCache(module, deps) {
|
|
2469
|
+
mDepCache[module + '.js'] = deps.map((dep) => dep + '.js');
|
|
2470
|
+
},
|
|
2471
|
+
depCacheUI5(module, deps) {
|
|
2472
|
+
mDepCache[module] = deps;
|
|
2473
|
+
},
|
|
2474
|
+
ignoreBundledResources(filter) {
|
|
2475
|
+
if(global["sap-ui-no-preload"] === true ) {
|
|
2476
|
+
fnIgnorePreload = () => true;
|
|
2477
|
+
}
|
|
2478
|
+
else if ( filter == null || typeof filter === 'function' ) {
|
|
2479
|
+
fnIgnorePreload = filter;
|
|
2480
|
+
}
|
|
2481
|
+
},
|
|
2482
|
+
map(context, map) {
|
|
2483
|
+
// @evo-todo ignore empty context, empty prefix?
|
|
2484
|
+
if ( map == null ) {
|
|
2485
|
+
delete mMaps[context];
|
|
2486
|
+
} else if ( typeof map === 'string' ) {
|
|
2487
|
+
// SystemJS style config
|
|
2488
|
+
mMaps['*'][context] = map;
|
|
2489
|
+
} else {
|
|
2490
|
+
mMaps[context] ||= Object.create(null);
|
|
2491
|
+
forEach(map, function(alias, name) {
|
|
2492
|
+
mMaps[context][alias] = name;
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
},
|
|
2496
|
+
reportSyncCalls(report) {
|
|
2497
|
+
if ( report === 0 || report === 1 || report === 2 ) {
|
|
2498
|
+
syncCallBehavior = report;
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
noConflict(bValue) {
|
|
2502
|
+
log.warning("Config option 'noConflict' has been deprecated, use option 'amd' instead, if still needed.");
|
|
2503
|
+
mUI5ConfigHandlers.amd(!bValue);
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
/**
|
|
2508
|
+
* Config handlers used when amd mode is enabled.
|
|
2509
|
+
* References only methods defined in the AMD spec.
|
|
2510
|
+
*/
|
|
2511
|
+
const mAMDConfigHandlers = {
|
|
2512
|
+
baseUrl: mUI5ConfigHandlers.baseUrl,
|
|
2513
|
+
paths(module, url) {
|
|
2514
|
+
registerResourcePath(module, resolveURL(url, getResourcePath("") + "/"));
|
|
2515
|
+
},
|
|
2516
|
+
map: mUI5ConfigHandlers.map,
|
|
2517
|
+
shim: mUI5ConfigHandlers.shim
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2520
|
+
/**
|
|
2521
|
+
* Executes all available handlers which are defined in the config object
|
|
2522
|
+
*
|
|
2523
|
+
* @param {object} oCfg config to handle
|
|
2524
|
+
* @param {Object<string,function>} mHandlers all available handlers
|
|
2525
|
+
*/
|
|
2526
|
+
function handleConfigObject(oCfg, mHandlers) {
|
|
2527
|
+
|
|
2528
|
+
function processConfig(key, value) {
|
|
2529
|
+
const handler = mHandlers[key];
|
|
2530
|
+
if ( typeof handler === 'function' ) {
|
|
2531
|
+
if ( handler.length === 1) {
|
|
2532
|
+
handler(value);
|
|
2533
|
+
} else if ( value != null ) {
|
|
2534
|
+
forEach(value, handler);
|
|
2535
|
+
}
|
|
2536
|
+
} else {
|
|
2537
|
+
log.warning(`configuration option ${key} not supported (ignored)`);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// Make sure the 'baseUrl' handler is called first as
|
|
2542
|
+
// other handlers (e.g. paths) depend on it
|
|
2543
|
+
if (oCfg.baseUrl) {
|
|
2544
|
+
processConfig("baseUrl", oCfg.baseUrl);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
forEach(oCfg, function(key, value) {
|
|
2548
|
+
// Ignore "baseUrl" here as it will be handled above
|
|
2549
|
+
if (key !== "baseUrl") {
|
|
2550
|
+
processConfig(key, value);
|
|
2551
|
+
}
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
function ui5Config(cfg) {
|
|
2556
|
+
if ( cfg === undefined ) {
|
|
2557
|
+
return {
|
|
2558
|
+
amd: bExposeAsAMDLoader,
|
|
2559
|
+
async: bGlobalAsyncMode,
|
|
2560
|
+
noConflict: !bExposeAsAMDLoader // TODO needed?
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
handleConfigObject(cfg, mUI5ConfigHandlers);
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
function amdConfig(cfg) {
|
|
2567
|
+
if ( cfg === undefined ) {
|
|
2568
|
+
return undefined;
|
|
2569
|
+
}
|
|
2570
|
+
handleConfigObject(cfg, mAMDConfigHandlers);
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// expose preload function as property of sap.ui.require
|
|
2574
|
+
ui5Require.preload = preload;
|
|
2575
|
+
|
|
2576
|
+
// @evo-todo really use this hook for loading. But how to differentiate between sync and async?
|
|
2577
|
+
// for now, it is only a notification hook to attach load tests
|
|
2578
|
+
ui5Require.load = function(context, url, id) {
|
|
2579
|
+
};
|
|
2580
|
+
|
|
2581
|
+
const privateAPI = {
|
|
2582
|
+
|
|
2583
|
+
// properties
|
|
2584
|
+
get assert() {
|
|
2585
|
+
return assert;
|
|
2586
|
+
},
|
|
2587
|
+
set assert(v) {
|
|
2588
|
+
assert = v;
|
|
2589
|
+
},
|
|
2590
|
+
get logger() {
|
|
2591
|
+
return log;
|
|
2592
|
+
},
|
|
2593
|
+
set logger(v) {
|
|
2594
|
+
log = v;
|
|
2595
|
+
aEarlyLogs.forEach(({level, message}) => log[level](message));
|
|
2596
|
+
},
|
|
2597
|
+
get measure() {
|
|
2598
|
+
return measure;
|
|
2599
|
+
},
|
|
2600
|
+
set measure(v) {
|
|
2601
|
+
measure = v;
|
|
2602
|
+
},
|
|
2603
|
+
get translate() {
|
|
2604
|
+
return translate;
|
|
2605
|
+
},
|
|
2606
|
+
set translate(v) {
|
|
2607
|
+
translate = v;
|
|
2608
|
+
},
|
|
2609
|
+
get callbackInMicroTask() {
|
|
2610
|
+
return simulateAsyncCallback === executeInMicroTask;
|
|
2611
|
+
},
|
|
2612
|
+
set callbackInMicroTask(v) {
|
|
2613
|
+
simulateAsyncCallback = v ? executeInMicroTask : executeInSeparateTask;
|
|
2614
|
+
},
|
|
2615
|
+
get maxTaskDuration() {
|
|
2616
|
+
return iMaxTaskDuration;
|
|
2617
|
+
},
|
|
2618
|
+
set maxTaskDuration(v) {
|
|
2619
|
+
updateMaxTaskDuration(v);
|
|
2620
|
+
},
|
|
2621
|
+
|
|
2622
|
+
// methods
|
|
2623
|
+
amdDefine,
|
|
2624
|
+
amdRequire,
|
|
2625
|
+
config: ui5Config,
|
|
2626
|
+
declareModule(sResourceName) {
|
|
2627
|
+
/* void */ declareModule( normalize(sResourceName) );
|
|
2628
|
+
},
|
|
2629
|
+
defineModuleSync,
|
|
2630
|
+
dump: dumpInternals,
|
|
2631
|
+
getAllModules,
|
|
2632
|
+
getModuleContent,
|
|
2633
|
+
getModuleState(sResourceName) {
|
|
2634
|
+
return mModules[sResourceName] ? mModules[sResourceName].state : INITIAL;
|
|
2635
|
+
},
|
|
2636
|
+
getResourcePath,
|
|
2637
|
+
getSyncCallBehavior,
|
|
2638
|
+
getUrlPrefixes,
|
|
2639
|
+
loadJSResourceAsync,
|
|
2640
|
+
resolveURL,
|
|
2641
|
+
guessResourceName,
|
|
2642
|
+
toUrl,
|
|
2643
|
+
unloadResources
|
|
2644
|
+
};
|
|
2645
|
+
|
|
2646
|
+
|
|
2647
|
+
// establish APIs in the sap.ui namespace
|
|
2648
|
+
|
|
2649
|
+
__global.sap = __global.sap || {};
|
|
2650
|
+
sap.ui = sap.ui || {};
|
|
2651
|
+
|
|
2652
|
+
/**
|
|
2653
|
+
* Provides access to UI5 loader configuration.
|
|
2654
|
+
*
|
|
2655
|
+
* The configuration is used by {@link sap.ui.require} and {@link sap.ui.define}.
|
|
2656
|
+
*
|
|
2657
|
+
* @public
|
|
2658
|
+
* @namespace
|
|
2659
|
+
* @ui5-global-only
|
|
2660
|
+
*/
|
|
2661
|
+
sap.ui.loader = {
|
|
2662
|
+
|
|
2663
|
+
/**
|
|
2664
|
+
* Sets the configuration for the UI5 loader. The configuration can be updated multiple times.
|
|
2665
|
+
* Later changes do not impact modules that have been loaded before.
|
|
2666
|
+
*
|
|
2667
|
+
* If no parameter is given, a partial copy of UI5 loader configuration in use is returned.
|
|
2668
|
+
*
|
|
2669
|
+
* The configuration options are aligned with the "Common Config" draft of the AMD spec
|
|
2670
|
+
* (https://github.com/amdjs/amdjs-api/blob/master/CommonConfig.md).
|
|
2671
|
+
*
|
|
2672
|
+
* The following code shows an example of what a UI5 loader configuration might look like:
|
|
2673
|
+
* <pre>
|
|
2674
|
+
*
|
|
2675
|
+
* sap.ui.loader.config({
|
|
2676
|
+
*
|
|
2677
|
+
* // location from where to load all modules by default
|
|
2678
|
+
* baseUrl: '../../resources/',
|
|
2679
|
+
*
|
|
2680
|
+
* paths: {
|
|
2681
|
+
* // load modules whose ID equals to or starts with 'my/module' from example.com
|
|
2682
|
+
* 'my/module': 'https://example.com/resources/my/module'
|
|
2683
|
+
* },
|
|
2684
|
+
*
|
|
2685
|
+
* map: {
|
|
2686
|
+
* // if any module requires 'sinon', load module 'sap/ui/thirdparty/sinon-4'
|
|
2687
|
+
* '*': {
|
|
2688
|
+
* 'sinon': 'sap/ui/thirdparty/sinon-4'
|
|
2689
|
+
* },
|
|
2690
|
+
* // but if a module whose ID equals to or starts with 'app' requires 'sinon'
|
|
2691
|
+
* // then load a legacy version instead
|
|
2692
|
+
* "app": {
|
|
2693
|
+
* 'sinon': 'sap/ui/legacy/sinon'
|
|
2694
|
+
* }
|
|
2695
|
+
* },
|
|
2696
|
+
*
|
|
2697
|
+
* // define two bundles that consists of JS modules only
|
|
2698
|
+
* bundles: {
|
|
2699
|
+
* bundle1: ['module1', 'module2'],
|
|
2700
|
+
* bundle2: ['moduleX', 'moduleY']
|
|
2701
|
+
* },
|
|
2702
|
+
*
|
|
2703
|
+
* // define a bundle that also contains non-JS resources
|
|
2704
|
+
* bundlesUI5: {
|
|
2705
|
+
* 'all.js': ['Component.js', 'manifest.json',
|
|
2706
|
+
* 'App.controller.js', 'App.view.xml']
|
|
2707
|
+
* },
|
|
2708
|
+
*
|
|
2709
|
+
* // activate real async loading and module definitions
|
|
2710
|
+
* async: true,
|
|
2711
|
+
*
|
|
2712
|
+
* // provide dependency and export metadata for non-UI5 modules
|
|
2713
|
+
* shim: {
|
|
2714
|
+
* 'sap/ui/thirdparty/blanket': {
|
|
2715
|
+
* amd: true,
|
|
2716
|
+
* exports: 'blanket'
|
|
2717
|
+
* }
|
|
2718
|
+
* }
|
|
2719
|
+
*
|
|
2720
|
+
* });
|
|
2721
|
+
*
|
|
2722
|
+
* </pre>
|
|
2723
|
+
*
|
|
2724
|
+
* @param {object} [cfg]
|
|
2725
|
+
* The provided configuration gets merged with the UI5 loader configuration in use.
|
|
2726
|
+
* If <code>cfg</code> is omitted or <code>undefined</code>, a copy of the current configuration
|
|
2727
|
+
* gets returned, containing at least the properties <code>amd</code> and <code>async</code>.
|
|
2728
|
+
*
|
|
2729
|
+
* @param {string} [cfg.baseUrl='./']
|
|
2730
|
+
* Default location to load modules from. If none of the configured <code>paths</code> prefixes
|
|
2731
|
+
* matches a module ID, the module will be loaded from the concatenation of the <code>baseUrl</code>
|
|
2732
|
+
* and the module ID.
|
|
2733
|
+
*
|
|
2734
|
+
* If the <code>baseUrl</code> itself is a relative URL, it is evaluated relative to <code>document.baseURI</code>.
|
|
2735
|
+
*
|
|
2736
|
+
* @param {Object.<string, string>} [cfg.paths]
|
|
2737
|
+
* A map of resource locations keyed by a corresponding module ID prefix.
|
|
2738
|
+
* When a module is to be loaded, the longest key in <code>paths</code> is searched that is a
|
|
2739
|
+
* prefix of the module ID. The module will be loaded from the concatenation of the corresponding
|
|
2740
|
+
* value in <code>paths</code> and the remainder of the module ID (after the prefix). If no entry
|
|
2741
|
+
* in <code>paths</code> matches, then the module will be loaded from the <code>baseUrl</code>.
|
|
2742
|
+
*
|
|
2743
|
+
* The prefixes (keys) must not contain relative segments (./ or ../), a trailing slash will be
|
|
2744
|
+
* removed, and only full name segment matches are considered a match (prefix 'sap/m' does not
|
|
2745
|
+
* match a module ID 'sap/main').
|
|
2746
|
+
*
|
|
2747
|
+
* <b>Note</b>: In contrast to the "Common Config" of the AMD spec, the paths (values in the map)
|
|
2748
|
+
* are interpreted relative to <code>document.baseURI</code>, not relative to <code>cfg.baseUrl</code>.
|
|
2749
|
+
*
|
|
2750
|
+
* @param {Object.<string, Object.<string, string>>} [cfg.map]
|
|
2751
|
+
* A map of maps that defines how to map module IDs to other module IDs (inner maps)
|
|
2752
|
+
* in the context of a specific set of modules (keys of outer map).
|
|
2753
|
+
*
|
|
2754
|
+
* Each key of the outer map represents a module ID prefix that describes the context for which
|
|
2755
|
+
* its value (inner map) has to be used. The special key <code>*</code> describes the default
|
|
2756
|
+
* context which applies for any module. Only the most specific matching context will be taken
|
|
2757
|
+
* into account.
|
|
2758
|
+
*
|
|
2759
|
+
* Each inner map maps a module ID or module ID prefix to another module ID or module ID prefix.
|
|
2760
|
+
* Again, only the most specific match is taken into account and only one mapping is evaluated
|
|
2761
|
+
* (the evaluation of the mappings is not done recursively).
|
|
2762
|
+
*
|
|
2763
|
+
* Matches are always complete matches, a prefix 'a/b/c' does not match the module ID 'a/b/com'.
|
|
2764
|
+
*
|
|
2765
|
+
* @param {Object.<string, {amd: boolean, deps: string[], exports: (string|string[])}>} [cfg.shim]
|
|
2766
|
+
* Defines additional metadata for modules for which the normal behavior of the AMD APIs is
|
|
2767
|
+
* not sufficient.
|
|
2768
|
+
*
|
|
2769
|
+
* A typical example are scripts that don't use <code>define</code> or <code>sap.ui.define</code>,
|
|
2770
|
+
* but export to a global name. With the <code>exports</code> property, one or more export
|
|
2771
|
+
* names can be specified, and the loader can retrieve the exported value after executing the
|
|
2772
|
+
* corresponding module. If such a module has dependencies, they can be specified in the
|
|
2773
|
+
* <code>deps</code> array and are loaded and executed before executing the module.
|
|
2774
|
+
*
|
|
2775
|
+
* The <code>amd</code> flag of a shim is a ui5loader-specific extension of the standard AMD shims.
|
|
2776
|
+
* If set, the ui5loader hides a currently active AMD loader before executing the module
|
|
2777
|
+
* and restores it afterwards. Otherwise, it might miss the export of third party modules that
|
|
2778
|
+
* check for an AMD loader and register with it instead of exporting to a global name. A future
|
|
2779
|
+
* version of the ui5loader might ignore this flag when it acts as an AMD loader by itself.
|
|
2780
|
+
*
|
|
2781
|
+
* <b>Note:</b> The ui5loader does not support the <code>init</code> option described by the
|
|
2782
|
+
* "Common Config" section of the AMD spec.
|
|
2783
|
+
*
|
|
2784
|
+
* @param {Object.<string, string[]>} [cfg.bundles]
|
|
2785
|
+
* A map of arrays that each define the modules contained in a bundle.
|
|
2786
|
+
*
|
|
2787
|
+
* Each key of the map represents the module ID of a bundle file. The array value represents
|
|
2788
|
+
* the set of JavaScript modules (their module IDs) that are contained in the bundle.
|
|
2789
|
+
*
|
|
2790
|
+
* When a module is required that has not been loaded yet, and for which a containing bundle is
|
|
2791
|
+
* known, that bundle will be required first. Only then the original module will be required
|
|
2792
|
+
* again and usually be taken from the just loaded bundle.
|
|
2793
|
+
*
|
|
2794
|
+
* A bundle will be loaded asynchronously only when the loader is in asynchronous mode and when
|
|
2795
|
+
* the request for the contained module originates from an asynchronous API. In all other cases,
|
|
2796
|
+
* the bundle has to be loaded synchronously to fulfill API contracts.
|
|
2797
|
+
*
|
|
2798
|
+
* <b>Note:</b> The loader only supports one containing bundle per module. If a module is declared
|
|
2799
|
+
* to be part of multiple bundles, only the last one will be taken into account.
|
|
2800
|
+
*
|
|
2801
|
+
* This configuration option is basically provided to be compatible with requireJS or SystemJS
|
|
2802
|
+
* configuration.
|
|
2803
|
+
*
|
|
2804
|
+
* @param {Object.<string, string[]>} [cfg.bundlesUI5]
|
|
2805
|
+
* A map of arrays that each define the resources contained in a bundle.
|
|
2806
|
+
*
|
|
2807
|
+
* This is similar to <code>bundles</code>, but all strings are unified resource names including
|
|
2808
|
+
* a file type extension, not only module IDs. This allows to represent more than just JavaScript
|
|
2809
|
+
* modules.
|
|
2810
|
+
*
|
|
2811
|
+
* Each key of the map represents the resource name (in unified resource name syntax) of a bundle
|
|
2812
|
+
* file. The array value represents the set of resources (also in unified resource name syntax)
|
|
2813
|
+
* that are contained in the bundle. The array can contain JavaScript as well as other textual
|
|
2814
|
+
* resource types (e.g. *.xml or *.json resources).
|
|
2815
|
+
*
|
|
2816
|
+
* When a module is required that has not been loaded yet, and for which a containing bundle is
|
|
2817
|
+
* known, that bundle will be required first. Only then the original module will be required
|
|
2818
|
+
* again and usually be taken from the just loaded bundle.
|
|
2819
|
+
*
|
|
2820
|
+
* A bundle will be loaded asynchronously only when the loader is in asynchronous mode and when
|
|
2821
|
+
* the request for the contained module originates from an asynchronous API. In all other cases,
|
|
2822
|
+
* the bundle has to be loaded synchronously to fulfill API contracts.
|
|
2823
|
+
*
|
|
2824
|
+
* <b>Note:</b> The loader only supports one containing bundle per module. If a module is declared
|
|
2825
|
+
* to be part of multiple bundles, only the last one will be taken into account.
|
|
2826
|
+
*
|
|
2827
|
+
* <b>Note:</b> Although non-JS resources can be declared to be part of a bundle, only requests for
|
|
2828
|
+
* JavaScript modules will currently trigger the loading of a bundle.
|
|
2829
|
+
*
|
|
2830
|
+
* @param {boolean} [cfg.async=false]
|
|
2831
|
+
* When set to true, <code>sap.ui.require</code> loads modules asynchronously via script tags and
|
|
2832
|
+
* <code>sap.ui.define</code> executes asynchronously. To enable this feature, it is recommended to
|
|
2833
|
+
* set the attribute <code>data-sap-ui-async="true"</code> on the application bootstrap tag.
|
|
2834
|
+
*
|
|
2835
|
+
* <b>Note:</b> Switching back from async to sync is not supported and trying to do so will throw
|
|
2836
|
+
* an <code>Error</code>
|
|
2837
|
+
*
|
|
2838
|
+
* @param {boolean} [cfg.amd=false]
|
|
2839
|
+
* When set to true, the ui5loader will overwrite the global properties <code>define</code>
|
|
2840
|
+
* and <code>require</code> with its own implementations. Any previously active AMD loader will
|
|
2841
|
+
* be remembered internally and can be restored by setting <code>amd</code> to false again.
|
|
2842
|
+
*
|
|
2843
|
+
* <b>Note:</b> Switching to the <code>amd</code> mode, the ui5loader will set <code>async</code>
|
|
2844
|
+
* to true implicitly for activating asynchronous loading. Once the loading behaviour has been
|
|
2845
|
+
* defined to be asynchronous, it can not be changed to synchronous behaviour again, also not
|
|
2846
|
+
* via setting <code>amd</code> to false.
|
|
2847
|
+
*
|
|
2848
|
+
* @returns {{amd: boolean, async: boolean, noConflict: boolean}|undefined} UI5 loader configuration in use.
|
|
2849
|
+
* @throws {Error} When trying to switch back from async mode to sync mode.
|
|
2850
|
+
* @public
|
|
2851
|
+
* @since 1.56.0
|
|
2852
|
+
* @function
|
|
2853
|
+
* @ui5-global-only
|
|
2854
|
+
*/
|
|
2855
|
+
config: ui5Config,
|
|
2856
|
+
|
|
2857
|
+
/**
|
|
2858
|
+
* Internal API of the UI5 loader.
|
|
2859
|
+
*
|
|
2860
|
+
* Must not be used by code outside sap.ui.core.
|
|
2861
|
+
* @private
|
|
2862
|
+
* @ui5-restricted sap.ui.core
|
|
2863
|
+
*/
|
|
2864
|
+
_: privateAPI
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
/**
|
|
2868
|
+
* Sets the configuration of the ui5loader. The configuration can be updated multiple times.
|
|
2869
|
+
* Later changes do not impact modules that have been loaded before.
|
|
2870
|
+
*
|
|
2871
|
+
* Setting the <code>amd</code> option of the sap.ui.loader.config to <code>true</code> is a
|
|
2872
|
+
* prerequisite to use the <code>require.config</code> function
|
|
2873
|
+
* (see {@link sap.ui.loader.config sap.ui.loader.config option amd}).
|
|
2874
|
+
*
|
|
2875
|
+
* The ui5loader acts more AMD compliant in relation to resolution of paths defined as
|
|
2876
|
+
* part of the <code>paths</code> configuration option.
|
|
2877
|
+
*
|
|
2878
|
+
* @param {object} cfg The provided configuration gets merged with the UI5 loader configuration in use.
|
|
2879
|
+
*
|
|
2880
|
+
* @param {string} [cfg.baseUrl='./']
|
|
2881
|
+
* Default location to load modules from. If none of the configured <code>paths</code> prefixes
|
|
2882
|
+
* matches a module ID, the module will be loaded from the concatenation of the <code>baseUrl</code>
|
|
2883
|
+
* and the module ID.
|
|
2884
|
+
*
|
|
2885
|
+
* If the <code>baseUrl</code> itself is a relative URL, it is evaluated relative to <code>document.baseURI</code>.
|
|
2886
|
+
*
|
|
2887
|
+
* @param {object} [cfg.paths]
|
|
2888
|
+
* A map of resource locations keyed by a corresponding module ID prefix.
|
|
2889
|
+
* When a module is to be loaded, the longest key in <code>paths</code> is searched that is a
|
|
2890
|
+
* prefix of the module ID. The module will be loaded from the concatenation of the corresponding
|
|
2891
|
+
* value in <code>paths</code> and the remainder of the module ID (after the prefix). If no entry
|
|
2892
|
+
* in <code>paths</code> matches, then the module will be loaded from the <code>baseUrl</code>.
|
|
2893
|
+
*
|
|
2894
|
+
* The prefixes (keys) must not contain relative segments (./ or ../), a trailing slash will be
|
|
2895
|
+
* removed, and only full name segment matches are considered a match (prefix 'sap/m' does not
|
|
2896
|
+
* match a module ID 'sap/main').
|
|
2897
|
+
*
|
|
2898
|
+
* <b>Note</b>: In contrast to the {@link sap.ui.loader.config sap.ui.loader.config option paths},
|
|
2899
|
+
* the paths (values in the map) are interpreted relative to <code>cfg.baseUrl</code>,
|
|
2900
|
+
* not relative to <code>document.baseURI</code>. The behaviour is exactly as described in the "Common Config" draft
|
|
2901
|
+
* of the AMD spec (https://github.com/amdjs/amdjs-api/blob/master/CommonConfig.md).
|
|
2902
|
+
*
|
|
2903
|
+
* @param {Object.<string, Object.<string, string>>} [cfg.map]
|
|
2904
|
+
* A map of maps that defines how to map module IDs to other module IDs (inner maps)
|
|
2905
|
+
* in the context of a specific set of modules (keys of outer map).
|
|
2906
|
+
*
|
|
2907
|
+
* Each key of the outer map represents a module ID prefix that describes the context for which
|
|
2908
|
+
* its value (inner map) has to be used. The special key <code>*</code> describes the default
|
|
2909
|
+
* context which applies for any module. Only the most specific matching context will be taken
|
|
2910
|
+
* into account.
|
|
2911
|
+
*
|
|
2912
|
+
* Each inner map maps a module ID or module ID prefix to another module ID or module ID prefix.
|
|
2913
|
+
* Again, only the most specific match is taken into account and only one mapping is evaluated
|
|
2914
|
+
* (the evaluation of the mappings is not done recursively).
|
|
2915
|
+
*
|
|
2916
|
+
* Matches are always complete matches, a prefix 'a/b/c' does not match the module ID 'a/b/com'.
|
|
2917
|
+
*
|
|
2918
|
+
* @param {Object.<string, {deps: string[], exports: (string|string[])}>} [cfg.shim]
|
|
2919
|
+
* Defines additional metadata for modules for which the normal behavior of the AMD APIs is
|
|
2920
|
+
* not sufficient.
|
|
2921
|
+
*
|
|
2922
|
+
* A typical example are scripts that don't use <code>define</code> or <code>sap.ui.define</code>,
|
|
2923
|
+
* but export to a global name. With the <code>exports</code> property, one or more export
|
|
2924
|
+
* names can be specified, and the loader can retrieve the exported value after executing the
|
|
2925
|
+
* corresponding module. If such a module has dependencies, they can be specified in the
|
|
2926
|
+
* <code>deps</code> array and are loaded and executed before executing the module.
|
|
2927
|
+
*
|
|
2928
|
+
* <b>Note:</b> The ui5loader does not support the <code>init</code> option described by the
|
|
2929
|
+
* "Common Config" section of the AMD spec.
|
|
2930
|
+
*
|
|
2931
|
+
* @returns {undefined}
|
|
2932
|
+
* @public
|
|
2933
|
+
* @name require_config
|
|
2934
|
+
* @function
|
|
2935
|
+
*/
|
|
2936
|
+
amdRequire.config = amdConfig;
|
|
2937
|
+
|
|
2938
|
+
/**
|
|
2939
|
+
* Defines a JavaScript module with its ID, its dependencies and a module export value or factory.
|
|
2940
|
+
*
|
|
2941
|
+
* The typical and only suggested usage of this method is to have one single, top level call to
|
|
2942
|
+
* <code>sap.ui.define</code> in one JavaScript resource (file). When a module is requested by its
|
|
2943
|
+
* module ID for the first time, the corresponding resource is determined from the ID and the current
|
|
2944
|
+
* {@link sap.ui.loader.config configuration}. The resource will be loaded and executed
|
|
2945
|
+
* which in turn will execute the top level <code>sap.ui.define</code> call.
|
|
2946
|
+
*
|
|
2947
|
+
* If the module ID was omitted from that call, it will be substituted by the ID that was used to
|
|
2948
|
+
* request the module. As a preparation step, the dependencies as well as their transitive dependencies,
|
|
2949
|
+
* will be loaded. Then, the module value (its export) will be determined: if a static value (object, literal)
|
|
2950
|
+
* was given as <code>vFactory</code>, that value will be the module value. If a function was given, that
|
|
2951
|
+
* function will be called (providing the module exports of the declared dependencies as parameters
|
|
2952
|
+
* to the function) and its return value will be used as module export value. The framework internally
|
|
2953
|
+
* associates the resulting value with the module ID and provides it to the original requester of the module.
|
|
2954
|
+
* Whenever the module is requested again, the same export value will be returned (modules are executed only once).
|
|
2955
|
+
*
|
|
2956
|
+
* <i>Example:</i><br>
|
|
2957
|
+
* The following example defines a module, but doesn't hard code the module ID.
|
|
2958
|
+
* If stored in a file 'sap/mylib/SomeClass.js', it can be requested with the ID 'sap/mylib/SomeClass'.
|
|
2959
|
+
* <pre>
|
|
2960
|
+
* sap.ui.define(['./Helper', 'sap/m/Bar'], function(Helper,Bar) {
|
|
2961
|
+
*
|
|
2962
|
+
* // create a new class
|
|
2963
|
+
* var SomeClass = function() {};
|
|
2964
|
+
*
|
|
2965
|
+
* // add methods to its prototype
|
|
2966
|
+
* SomeClass.prototype.foo = function() {
|
|
2967
|
+
*
|
|
2968
|
+
* // use a function from the dependency 'Helper' in the same package (e.g. 'sap/mylib/Helper' )
|
|
2969
|
+
* var mSettings = Helper.foo();
|
|
2970
|
+
*
|
|
2971
|
+
* // create and return an sap.m.Bar (using its local name 'Bar')
|
|
2972
|
+
* return new Bar(mSettings);
|
|
2973
|
+
*
|
|
2974
|
+
* }
|
|
2975
|
+
*
|
|
2976
|
+
* // return the class as module value
|
|
2977
|
+
* return SomeClass;
|
|
2978
|
+
*
|
|
2979
|
+
* });
|
|
2980
|
+
* </pre>
|
|
2981
|
+
*
|
|
2982
|
+
* In another module or in an application HTML page, the {@link sap.ui.require} API can be used
|
|
2983
|
+
* to load the sap/mylib/Something module and to work with it:
|
|
2984
|
+
*
|
|
2985
|
+
* <pre>
|
|
2986
|
+
* sap.ui.require(['sap/mylib/Something'], function(Something) {
|
|
2987
|
+
*
|
|
2988
|
+
* // instantiate a Something and call foo() on it
|
|
2989
|
+
* new Something().foo();
|
|
2990
|
+
*
|
|
2991
|
+
* });
|
|
2992
|
+
* </pre>
|
|
2993
|
+
*
|
|
2994
|
+
*
|
|
2995
|
+
* <h3>Module Name Syntax</h3>
|
|
2996
|
+
*
|
|
2997
|
+
* <code>sap.ui.define</code> uses a simplified variant of the {@link jQuery.sap.getResourcePath
|
|
2998
|
+
* unified resource name} syntax for the module's own name as well as for its dependencies.
|
|
2999
|
+
* The only difference to that syntax is, that for <code>sap.ui.define</code> and
|
|
3000
|
+
* <code>sap.ui.require</code>, the extension (which always would be '.js') has to be omitted.
|
|
3001
|
+
* Both methods always add this extension internally.
|
|
3002
|
+
*
|
|
3003
|
+
* As a convenience, the name of a dependency can start with the segment './' which will be
|
|
3004
|
+
* replaced by the name of the package that contains the currently defined module (relative name).
|
|
3005
|
+
*
|
|
3006
|
+
* It is best practice to omit the name of the defined module (first parameter) and to use
|
|
3007
|
+
* relative names for the dependencies whenever possible. This reduces the necessary configuration,
|
|
3008
|
+
* simplifies renaming of packages and allows to map them to a different namespace.
|
|
3009
|
+
*
|
|
3010
|
+
*
|
|
3011
|
+
* <h3>Dependency to Modules</h3>
|
|
3012
|
+
*
|
|
3013
|
+
* If a dependencies array is given, each entry represents the name of another module that
|
|
3014
|
+
* the currently defined module depends on. All dependency modules are loaded before the export
|
|
3015
|
+
* of the currently defined module is determined. The module export of each dependency module
|
|
3016
|
+
* will be provided as a parameter to a factory function, the order of the parameters will match
|
|
3017
|
+
* the order of the modules in the dependencies array.
|
|
3018
|
+
*
|
|
3019
|
+
* <b>Note:</b> The order in which the dependency modules are <i>executed</i> is <b>not</b>
|
|
3020
|
+
* defined by the order in the dependencies array! The execution order is affected by dependencies
|
|
3021
|
+
* <i>between</i> the dependency modules as well as by their current state (whether a module
|
|
3022
|
+
* already has been loaded or not). Neither module implementations nor dependents that require
|
|
3023
|
+
* a module set must make any assumption about the execution order (other than expressed by
|
|
3024
|
+
* their dependencies).
|
|
3025
|
+
*
|
|
3026
|
+
* <b>Note:</b> A static module export (a literal provided to <code>sap.ui.define</code>) cannot
|
|
3027
|
+
* depend on the module exports of the dependency modules as it has to be calculated before
|
|
3028
|
+
* the dependencies are resolved. As an alternative, modules can define a factory function,
|
|
3029
|
+
* calculate a static export value in that function, potentially based on the dependencies, and
|
|
3030
|
+
* return the result as module export value. The same approach must be taken when the module
|
|
3031
|
+
* export is supposed to be a function.
|
|
3032
|
+
*
|
|
3033
|
+
*
|
|
3034
|
+
* <h3>Asynchronous Contract</h3>
|
|
3035
|
+
*
|
|
3036
|
+
* <code>sap.ui.define</code> is designed to support real Asynchronous Module Definitions (AMD)
|
|
3037
|
+
* in future, although it internally still might use synchronous module loading, depending on
|
|
3038
|
+
* configuration and context. However, callers of <code>sap.ui.define</code> must never rely on
|
|
3039
|
+
* any synchronous behavior that they might observe in a specific test scenario.
|
|
3040
|
+
*
|
|
3041
|
+
* For example, callers of <code>sap.ui.define</code> must not use the module export value
|
|
3042
|
+
* immediately after invoking <code>sap.ui.define</code>:
|
|
3043
|
+
*
|
|
3044
|
+
* <pre>
|
|
3045
|
+
* // COUNTER EXAMPLE HOW __NOT__ TO DO IT
|
|
3046
|
+
*
|
|
3047
|
+
* // define a class Something as AMD module
|
|
3048
|
+
* sap.ui.define('Something', [], function() {
|
|
3049
|
+
* var Something = function() {};
|
|
3050
|
+
* return Something;
|
|
3051
|
+
* });
|
|
3052
|
+
*
|
|
3053
|
+
* // DON'T DO THAT!
|
|
3054
|
+
* // accessing the class _synchronously_ after sap.ui.define was called
|
|
3055
|
+
* new Something();
|
|
3056
|
+
*
|
|
3057
|
+
* </pre>
|
|
3058
|
+
*
|
|
3059
|
+
* Applications that need to ensure synchronous module definition or synchronous loading of dependencies
|
|
3060
|
+
* <b>MUST</b> use the deprecated legacy APIs {@link jQuery.sap.declare} and {@link jQuery.sap.require}.
|
|
3061
|
+
*
|
|
3062
|
+
*
|
|
3063
|
+
* <h3>(No) Global References</h3>
|
|
3064
|
+
*
|
|
3065
|
+
* To be in line with AMD best practices, modules defined with <code>sap.ui.define</code>
|
|
3066
|
+
* should not make any use of global variables if those variables are also available as module
|
|
3067
|
+
* exports. Instead, they should add dependencies to those modules and use the corresponding parameter
|
|
3068
|
+
* of the factory function to access the module exports.
|
|
3069
|
+
*
|
|
3070
|
+
* As the current programming model and the documentation of UI5 heavily rely on global names,
|
|
3071
|
+
* there will be a transition phase where UI5 enables AMD modules and local references to module
|
|
3072
|
+
* exports in parallel to the old global names. The fourth parameter of <code>sap.ui.define</code>
|
|
3073
|
+
* has been added to support that transition phase. When this parameter is set to true, the framework
|
|
3074
|
+
* provides two additional features
|
|
3075
|
+
*
|
|
3076
|
+
* <ol>
|
|
3077
|
+
* <li>Before the factory function is called, the existence of the global parent namespace for
|
|
3078
|
+
* the current module is ensured</li>
|
|
3079
|
+
* <li>The module export returned by the module's factory function will be automatically exported
|
|
3080
|
+
* under the global name which is derived from the ID of the module</li>
|
|
3081
|
+
* </ol>
|
|
3082
|
+
*
|
|
3083
|
+
* The parameter lets the framework know whether any of those two operations is needed or not.
|
|
3084
|
+
* In future versions of UI5, a central configuration option is planned to suppress those 'exports'.
|
|
3085
|
+
*
|
|
3086
|
+
*
|
|
3087
|
+
* <h3>Third Party Modules</h3>
|
|
3088
|
+
* Although third party modules don't use UI5 APIs, they still can be listed as dependencies in
|
|
3089
|
+
* a <code>sap.ui.define</code> call. They will be requested and executed like UI5 modules, but to
|
|
3090
|
+
* make their exports available, so called <em>shims</em> have to be defined.
|
|
3091
|
+
*
|
|
3092
|
+
* Note that UI5 temporarily deactivates an existing AMD loader while it executes third party modules
|
|
3093
|
+
* known to support AMD. This sounds contradictorily at a first glance as UI5 wants to support AMD,
|
|
3094
|
+
* but for now it is necessary to fully support UI5 applications that rely on global names for such modules.
|
|
3095
|
+
*
|
|
3096
|
+
* For third-party modules that UI5 delivers (e.g. those in namespace <code>sap/ui/thirdparty/</code>),
|
|
3097
|
+
* the necessary shims are defined by UI5 itself by executing the private module <code>ui5loader-autoconfig.js</code>
|
|
3098
|
+
* during bootstrap.
|
|
3099
|
+
*
|
|
3100
|
+
* Example:
|
|
3101
|
+
* <pre>
|
|
3102
|
+
* // module 'Something' wants to use third party library 'URI.js'
|
|
3103
|
+
* // It is packaged by UI5 as non-UI5-module 'sap/ui/thirdparty/URI'
|
|
3104
|
+
* // the following shim helps UI5 to correctly load URI.js and to retrieve the module's export value
|
|
3105
|
+
* // Apps don't have to define that shim, it is already applied by ui5loader-autconfig.js
|
|
3106
|
+
* sap.ui.loader.config({
|
|
3107
|
+
* shim: {
|
|
3108
|
+
* 'sap/ui/thirdparty/URI': {
|
|
3109
|
+
* amd: true, // URI.js reacts on an AMD loader, this flag lets UI5 temp. disable such loaders
|
|
3110
|
+
* exports: 'URI' // name of the global variable under which URI.js exports its module value
|
|
3111
|
+
* }
|
|
3112
|
+
* }
|
|
3113
|
+
* });
|
|
3114
|
+
*
|
|
3115
|
+
* // now the module can be retrieved like other modules
|
|
3116
|
+
* sap.ui.define('Something', ['sap/ui/thirdparty/URI'], function(URIModuleValue) {
|
|
3117
|
+
*
|
|
3118
|
+
* new URIModuleValue(...); // same as the global 'URI' name: new URI(...)
|
|
3119
|
+
*
|
|
3120
|
+
* ...
|
|
3121
|
+
* });
|
|
3122
|
+
* </pre>
|
|
3123
|
+
*
|
|
3124
|
+
*
|
|
3125
|
+
* <h3>Differences to Standard AMD</h3>
|
|
3126
|
+
*
|
|
3127
|
+
* The current implementation of <code>sap.ui.define</code> differs from the AMD specification
|
|
3128
|
+
* (https://github.com/amdjs/amdjs-api) or from concrete AMD loaders like <code>requireJS</code>
|
|
3129
|
+
* in several aspects:
|
|
3130
|
+
* <ul>
|
|
3131
|
+
* <li>The name <code>sap.ui.define</code> is different from the plain <code>define</code>.
|
|
3132
|
+
* This has two reasons: first, it avoids the impression that <code>sap.ui.define</code> is
|
|
3133
|
+
* an exact implementation of an AMD loader. And second, it allows the coexistence of an AMD
|
|
3134
|
+
* loader (e.g. requireJS) and <code>sap.ui.define</code> in one application as long as UI5 or
|
|
3135
|
+
* applications using UI5 are not fully prepared to run with an AMD loader.
|
|
3136
|
+
* Note that the difference of the API names also implies that the UI5 loader can't be used
|
|
3137
|
+
* to load 'real' AMD modules as they expect methods <code>define</code> and <code>require</code>
|
|
3138
|
+
* to be available. Modules that use Unified Module Definition (UMD) syntax, can be loaded,
|
|
3139
|
+
* but only when no AMD loader is present or when they expose their export also to the global
|
|
3140
|
+
* namespace, even when an AMD loader is present (as e.g. jQuery does) or when a shim is
|
|
3141
|
+
* defined for them using the <code>amd:true</code> flag (see example above)</li>
|
|
3142
|
+
* <li>Depending on configuration and the current context, <code>sap.ui.define</code> loads
|
|
3143
|
+
* the dependencies of a module either synchronously using a sync XHR call + eval or asynchronously
|
|
3144
|
+
* via script tags. The sync loading is basically a tribute to the synchronous history of UI5.
|
|
3145
|
+
* There's no way for a module developer to enforce synchronous loading of the dependencies and
|
|
3146
|
+
* on the long run, sync loading will be faded out.
|
|
3147
|
+
* Applications that need to ensure synchronous loading of dependencies <b>MUST</b> use the
|
|
3148
|
+
* deprecated legacy APIs like {@link jQuery.sap.require}.</li>
|
|
3149
|
+
* <li><code>sap.ui.define</code> does not support plugins to use other file types, formats or
|
|
3150
|
+
* protocols. It is not planned to support this in future</li>
|
|
3151
|
+
* <li><code>sap.ui.define</code> does not support absolute URLs as module names (dependencies)
|
|
3152
|
+
* nor does it allow module names that start with a slash. To refer to a module at an absolute
|
|
3153
|
+
* URL, a resource root can be registered that points to that URL (or to a prefix of it).</li>
|
|
3154
|
+
* <li><code>sap.ui.define</code> does <b>not</b> support the 'sugar' of requireJS where CommonJS
|
|
3155
|
+
* style dependency declarations using <code>sap.ui.require("something")</code> are automagically
|
|
3156
|
+
* converted into <code>sap.ui.define</code> dependencies before executing the factory function.</li>
|
|
3157
|
+
* </ul>
|
|
3158
|
+
*
|
|
3159
|
+
*
|
|
3160
|
+
* <h3>Restrictions, Design Considerations</h3>
|
|
3161
|
+
* <ul>
|
|
3162
|
+
* <li><b>Restriction</b>: as dependency management is not supported for Non-UI5 modules, the only way
|
|
3163
|
+
* to ensure proper execution order for such modules currently is to rely on the order in the
|
|
3164
|
+
* dependency array. Obviously, this only works as long as <code>sap.ui.define</code> uses
|
|
3165
|
+
* synchronous loading. It will be enhanced when asynchronous loading is implemented.</li>
|
|
3166
|
+
* <li>It was discussed to enforce asynchronous execution of the module factory function (e.g. with a
|
|
3167
|
+
* timeout of 0). But this would have invalidated the current migration scenario where a
|
|
3168
|
+
* sync <code>jQuery.sap.require</code> call can load a <code>sap.ui.define</code>'ed module.
|
|
3169
|
+
* If the module definition would not execute synchronously, the synchronous contract of the
|
|
3170
|
+
* require call would be broken (default behavior in existing UI5 applications)</li>
|
|
3171
|
+
* <li>A single file must not contain multiple calls to <code>sap.ui.define</code>. Multiple calls
|
|
3172
|
+
* currently are only supported in the so called 'preload' files that the UI5 merge tooling produces.
|
|
3173
|
+
* The exact details of how this works might be changed in future implementations and are not
|
|
3174
|
+
* part of the API contract</li>
|
|
3175
|
+
* </ul>
|
|
3176
|
+
* @param {string} [sModuleName] ID of the module in simplified resource name syntax.
|
|
3177
|
+
* When omitted, the loader determines the ID from the request.
|
|
3178
|
+
* @param {string[]} [aDependencies] List of dependencies of the module
|
|
3179
|
+
* @param {function(...any):any|any} vFactory The module export value or a function that calculates that value
|
|
3180
|
+
* @param {boolean} [bExport] Whether an export to global names is required - should be used by SAP-owned code only
|
|
3181
|
+
* @since 1.27.0
|
|
3182
|
+
* @public
|
|
3183
|
+
* @see https://github.com/amdjs/amdjs-api
|
|
3184
|
+
* @function
|
|
3185
|
+
* @ui5-global-only
|
|
3186
|
+
*/
|
|
3187
|
+
sap.ui.define = ui5Define;
|
|
3188
|
+
|
|
3189
|
+
/**
|
|
3190
|
+
* @private
|
|
3191
|
+
* @ui5-restricted bundles created with UI5 tooling
|
|
3192
|
+
* @function
|
|
3193
|
+
* @ui5-global-only
|
|
3194
|
+
*/
|
|
3195
|
+
sap.ui.predefine = predefine;
|
|
3196
|
+
|
|
3197
|
+
/**
|
|
3198
|
+
* Resolves one or more module dependencies.
|
|
3199
|
+
*
|
|
3200
|
+
* <h3>Synchronous Retrieval of a Single Module Export Value (Probing)</h3>
|
|
3201
|
+
*
|
|
3202
|
+
* When called with a single string, that string is assumed to be the ID of an already loaded
|
|
3203
|
+
* module and the export of that module is returned. If the module has not been loaded yet,
|
|
3204
|
+
* or if it is a Non-UI5 module (e.g. third-party module) without a shim, <code>undefined</code>
|
|
3205
|
+
* is returned.
|
|
3206
|
+
*
|
|
3207
|
+
* This signature variant allows synchronous access to module exports without initiating module loading.
|
|
3208
|
+
*
|
|
3209
|
+
* Sample:
|
|
3210
|
+
* <pre>
|
|
3211
|
+
* var JSONModel = sap.ui.require("sap/ui/model/json/JSONModel");
|
|
3212
|
+
* </pre>
|
|
3213
|
+
*
|
|
3214
|
+
* For modules that are known to be UI5 modules, this signature variant can be used to check whether
|
|
3215
|
+
* the module has been loaded.
|
|
3216
|
+
*
|
|
3217
|
+
*
|
|
3218
|
+
* <h3>Asynchronous Loading of Multiple Modules</h3>
|
|
3219
|
+
*
|
|
3220
|
+
* If an array of strings is given and (optionally) a callback function, then the strings
|
|
3221
|
+
* are interpreted as module IDs and the corresponding modules (and their transitive
|
|
3222
|
+
* dependencies) are loaded. Then the callback function will be called asynchronously.
|
|
3223
|
+
* The module exports of the specified modules will be provided as parameters to the callback
|
|
3224
|
+
* function in the same order in which they appeared in the dependencies array.
|
|
3225
|
+
*
|
|
3226
|
+
* The return value for the asynchronous use case is <code>undefined</code>.
|
|
3227
|
+
*
|
|
3228
|
+
* <pre>
|
|
3229
|
+
* sap.ui.require(['sap/ui/model/json/JSONModel', 'sap/ui/core/UIComponent'], function(JSONModel,UIComponent) {
|
|
3230
|
+
*
|
|
3231
|
+
* var MyComponent = UIComponent.extend('MyComponent', {
|
|
3232
|
+
* ...
|
|
3233
|
+
* });
|
|
3234
|
+
* ...
|
|
3235
|
+
*
|
|
3236
|
+
* });
|
|
3237
|
+
* </pre>
|
|
3238
|
+
*
|
|
3239
|
+
* This method uses the same variation of the {@link jQuery.sap.getResourcePath unified resource name}
|
|
3240
|
+
* syntax that {@link sap.ui.define} uses: module names are specified without the implicit extension '.js'.
|
|
3241
|
+
* Relative module names are not supported.
|
|
3242
|
+
*
|
|
3243
|
+
* @param {string|string[]} vDependencies Dependency (dependencies) to resolve
|
|
3244
|
+
* @param {function(...any)} [fnCallback] Callback function to execute after resolving an array of dependencies
|
|
3245
|
+
* @param {function(Error)} [fnErrback] Callback function to execute if an error was detected while loading the
|
|
3246
|
+
* dependencies or executing the factory function. Note that due to browser restrictions
|
|
3247
|
+
* not all errors will be reported via this callback. In general, module loading is
|
|
3248
|
+
* designed for the non-error case. Error handling is not complete.
|
|
3249
|
+
* @returns {any|undefined} A single module export value (sync probing variant) or <code>undefined</code> (async loading variant)
|
|
3250
|
+
* @public
|
|
3251
|
+
* @function
|
|
3252
|
+
* @ui5-global-only
|
|
3253
|
+
*/
|
|
3254
|
+
sap.ui.require = ui5Require;
|
|
3255
|
+
|
|
3256
|
+
/**
|
|
3257
|
+
* Calculates a URL from the provided resource name.
|
|
3258
|
+
*
|
|
3259
|
+
* The calculation takes any configured ID mappings or resource paths into account
|
|
3260
|
+
* (see {@link sap.ui.loader.config config options map and paths}. It also supports relative
|
|
3261
|
+
* segments such as <code>./</code> and <code>../</code> within the path, but not at its beginning.
|
|
3262
|
+
* If relative navigation would cross the root namespace (e.g. <code>sap.ui.require.toUrl("../")</code>)
|
|
3263
|
+
* or when the resource name starts with a slash or with a relative segment, an error is thrown.
|
|
3264
|
+
*
|
|
3265
|
+
* <b>Note:</b> <code>toUrl</code> does not resolve the returned URL; whether it is an absolute
|
|
3266
|
+
* URL or a relative URL depends on the configured <code>baseUrl</code> and <code>paths</code>.
|
|
3267
|
+
*
|
|
3268
|
+
* @example
|
|
3269
|
+
* sap.ui.loader.config({
|
|
3270
|
+
* baseUrl: "/home"
|
|
3271
|
+
* });
|
|
3272
|
+
*
|
|
3273
|
+
* sap.ui.require.toUrl("app/data") === "/home/app/data"
|
|
3274
|
+
* sap.ui.require.toUrl("app/data.json") === "/home/app/data.json"
|
|
3275
|
+
* sap.ui.require.toUrl("app/data/") === "/home/app/data/"
|
|
3276
|
+
* sap.ui.require.toUrl("app/.config") === "/home/app/.config"
|
|
3277
|
+
* sap.ui.require.toUrl("app/test/../data.json") === "/home/data.json"
|
|
3278
|
+
* sap.ui.require.toUrl("app/test/./data.json") === "/home/test/data.json"
|
|
3279
|
+
* sap.ui.require.toUrl("app/../../data") throws Error because root namespace is left
|
|
3280
|
+
* sap.ui.require.toUrl("/app") throws Error because first character is a slash
|
|
3281
|
+
*
|
|
3282
|
+
* @param {string} sName Name of a resource e.g. <code>'app/data.json'</code>
|
|
3283
|
+
* @returns {string} Path to the resource, e.g. <code>'/home/app/data.json'</code>
|
|
3284
|
+
* @see https://github.com/amdjs/amdjs-api/wiki/require#requiretourlstring-
|
|
3285
|
+
* @throws {Error} If the input name is absolute (starts with a slash character <code>'/'</code>),
|
|
3286
|
+
* starts with a relative segment or if resolving relative segments would cross the root
|
|
3287
|
+
* namespace
|
|
3288
|
+
* @public
|
|
3289
|
+
* @name sap.ui.require.toUrl
|
|
3290
|
+
* @function
|
|
3291
|
+
* @ui5-global-only
|
|
3292
|
+
*/
|
|
3293
|
+
|
|
3294
|
+
/**
|
|
3295
|
+
* Load a single module synchronously and return its module value.
|
|
3296
|
+
*
|
|
3297
|
+
* Basically, this method is a combination of {@link jQuery.sap.require} and {@link sap.ui.require}.
|
|
3298
|
+
* Its main purpose is to simplify the migration of modules to AMD style in those cases where some dependencies
|
|
3299
|
+
* have to be loaded late (lazy) and synchronously.
|
|
3300
|
+
*
|
|
3301
|
+
* The method accepts a single module name in the same syntax that {@link sap.ui.define} and {@link sap.ui.require}
|
|
3302
|
+
* already use (a simplified variation of the {@link jQuery.sap.getResourcePath unified resource name}:
|
|
3303
|
+
* slash separated names without the implicit extension '.js'). As for <code>sap.ui.require</code>,
|
|
3304
|
+
* relative names (using <code>./</code> or <code>../</code>) are not supported.
|
|
3305
|
+
* If not loaded yet, the named module will be loaded synchronously and the export value of the module will be returned.
|
|
3306
|
+
* While a module is executing, a value of <code>undefined</code> will be returned in case it is required again during
|
|
3307
|
+
* that period of time (e.g. in case of cyclic dependencies).
|
|
3308
|
+
*
|
|
3309
|
+
* <b>Note:</b> the scope of this method is limited to the sap.ui.core library. Callers are strongly encouraged to use
|
|
3310
|
+
* this method only when synchronous loading is unavoidable. Any code that uses this method won't benefit from future
|
|
3311
|
+
* performance improvements that require asynchronous module loading (e.g. HTTP/2). And such code never can comply with
|
|
3312
|
+
* a content security policies (CSP) that forbids 'eval'.
|
|
3313
|
+
*
|
|
3314
|
+
* @param {string} sModuleName Module name in requireJS syntax
|
|
3315
|
+
* @returns {any} Export value of the loaded module (can be <code>undefined</code>)
|
|
3316
|
+
* @private
|
|
3317
|
+
* @ui5-restricted sap.ui.core
|
|
3318
|
+
* @function
|
|
3319
|
+
* @ui5-global-only
|
|
3320
|
+
*/
|
|
3321
|
+
sap.ui.requireSync = requireSync;
|
|
3322
|
+
|
|
3323
|
+
}(globalThis));
|