@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
1,420 lines (1,266 loc) • 117 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the real core class sap.ui.core.Core of SAPUI5
sap.ui.define([
'jquery.sap.global',
'sap/ui/Device',
'sap/ui/base/EventProvider',
'sap/ui/base/Interface',
'sap/ui/base/Object',
'sap/ui/base/ManagedObject',
'./Component',
'./Configuration',
'./Element',
'./ElementMetadata',
'./Lib',
'./Rendering',
'./RenderManager',
'./UIArea',
'./message/MessageManager',
"sap/base/Log",
"sap/ui/performance/Measurement",
"sap/ui/security/FrameOptions",
"sap/base/assert",
"sap/base/util/ObjectPath",
'sap/ui/performance/trace/initTraces',
'sap/base/util/isEmptyObject',
'sap/base/util/each',
'sap/ui/VersionInfo',
'sap/ui/events/jquery/EventSimulation'
],
function(
jQuery,
Device,
EventProvider,
Interface,
BaseObject,
ManagedObject,
Component,
Configuration,
Element,
ElementMetadata,
Library,
Rendering,
RenderManager,
UIArea,
MessageManager,
Log,
Measurement,
FrameOptions,
assert,
ObjectPath,
initTraces,
isEmptyObject,
each,
VersionInfo
/* ,EventSimulation */
) {
"use strict";
// when the Core module has been executed before, don't execute it again
if (sap.ui.getCore && sap.ui.getCore()) {
return sap.ui.getCore();
}
/**
* FocusHandler module reference, lazily probed via public "getCurrentFocusedControlId" API.
*/
var FocusHandler;
/**
* ThemeManager module reference, lazily retrieved via private "_getThemeManager" API.
*/
var ThemeManager;
// Initialize SAP Passport or FESR
initTraces();
/**
* EventProvider instance, EventProvider is no longer extended
* @private
*/
var _oEventProvider;
/*
* Internal class that can help to synchronize a set of asynchronous tasks.
* Each task must be registered in the sync point by calling startTask with
* an (purely informative) title. The returned value must be used in a later
* call to finishTask.
* When finishTask has been called for all tasks that have been started,
* the fnCallback will be fired.
* When a timeout is given and reached, the callback is called at that
* time, no matter whether all tasks have been finished or not.
*/
var SyncPoint = function (sName, fnCallback) {
var aTasks = [],
iOpenTasks = 0,
iFailures = 0;
this.startTask = function(sTitle) {
var iId = aTasks.length;
aTasks[iId] = { name : sTitle, finished : false };
iOpenTasks++;
return iId;
};
this.finishTask = function(iId, bSuccess) {
if ( !aTasks[iId] || aTasks[iId].finished ) {
throw new Error("trying to finish non existing or already finished task");
}
aTasks[iId].finished = true;
iOpenTasks--;
if ( bSuccess === false ) {
iFailures++;
}
if ( iOpenTasks === 0 ) {
Log.info("Sync point '" + sName + "' finished (tasks:" + aTasks.length + ", open:" + iOpenTasks + ", failures:" + iFailures + ")");
finish();
}
};
function finish() {
if ( fnCallback ) {
fnCallback(iOpenTasks, iFailures);
}
fnCallback = null;
}
Log.info("Sync point '" + sName + "' created");
};
/**
* @class Core Class of the SAP UI Library.
*
* This class boots the Core framework and makes it available for the application
* via method <code>sap.ui.getCore()</code>.
*
* Example:
* <pre>
*
* var oCore = sap.ui.getCore();
*
* </pre>
*
* With methods of the Core framework you can {@link #attachInit execute code} after the framework has been initialized.
* It provides access to the {@link #getConfiguration configuration} and exposes events that
* an application or a control can register to (e.g. {@link #event:localizationChanged localizationChanged},
* {@link #event:parseError parseError}, {@link #event:validationError validationError},
* {@link #event:formatError formatError}, {@link #event:validationSuccess validationSuccess}).
*
* Example:
* <pre>
*
* oCore.attachInit(function() {
* if ( oCore.getConfiguration().getRTL() ) {
* ...
* }
* });
*
* oCore.attachLocalizationChanged(function(oEvent) {
* ...
* });
*
* </pre>
*
* @extends sap.ui.base.Object
* @final
* @author SAP SE
* @version 1.111.5
* @alias sap.ui.core.Core
* @public
* @hideconstructor
*/
var Core = BaseObject.extend("sap.ui.core.Core", /** @lends sap.ui.core.Core.prototype */ {
constructor : function() {
var that = this,
METHOD = "sap.ui.core.Core";
// when a Core instance has been created before, don't create another one
if (sap.ui.getCore && sap.ui.getCore()) {
Log.error("Only the framework must create an instance of sap/ui/core/Core." +
" To get access to its functionality, use sap.ui.getCore().");
return sap.ui.getCore();
}
BaseObject.call(this);
_oEventProvider = new EventProvider();
// Generate all functions from EventProvider for backward compatibility
["attachEvent", "detachEvent", "getEventingParent"].forEach(function (sFuncName) {
Core.prototype[sFuncName] = _oEventProvider[sFuncName].bind(_oEventProvider);
});
/**
* Whether the core has been booted
* @private
*/
this.bBooted = false;
/**
* Whether the core has been initialized
* @private
*/
this.bInitialized = false;
/**
* Available plugins in the order of registration.
* @private
*/
this.aPlugins = [];
/**
* Default model used for databinding
* @private
*/
this.oModels = {};
/**
* The event bus (initialized lazily)
* @private
*/
this.oEventBus = null;
Object.defineProperty(this, "mElements", {
get: function() {
Log.error("oCore.mElements was a private member and has been removed. Use one of the methods in sap.ui.core.Element.registry instead");
return Element.registry.all(); // this is a very costly snapshot!
},
configurable: false
});
/**
* Map of of created objects structured by their type which contains a map
* containing the created objects keyed by their type.
*
* Each object registers itself in its constructor and deregisters itself in its
* destroy method.
*
* @private
* @todo get rid of this collection as it represents a candidate for memory leaks
*/
this.mObjects = {
"template": {}
};
/**
* The instance of the root component (defined in the configuration {@link sap.ui.core.Configuration#getRootComponent})
* @private
*/
this.oRootComponent = null;
/**
* Ordered collection of initEvent listeners
* Moved here (before boot()) so that the libraries can be registered for lazy load!!
* @private
*/
this.aInitListeners = [];
/**
* Whether the legacy library has to be loaded.
* @private
*/
this.bInitLegacyLib = false;
Log.info("Creating Core",null,METHOD);
Measurement.start("coreComplete", "Core.js - complete");
Measurement.start("coreBoot", "Core.js - boot");
Measurement.start("coreInit", "Core.js - init");
// freeze Config
Configuration.setCore(this);
// initialize frameOptions script (anti-clickjacking, etc.)
var oFrameOptionsConfig = Configuration.getValue("frameOptionsConfig") || {};
oFrameOptionsConfig.mode = Configuration.getFrameOptions();
oFrameOptionsConfig.allowlistService = Configuration.getAllowlistService();
this.oFrameOptions = new FrameOptions(oFrameOptionsConfig);
// let Element and Component get friend access to the respective register/deregister methods
this._grantFriendAccess();
// handle modules
var aModules = this.aModules = Configuration.getValue("modules");
if ( Configuration.getDebug() ) {
// add debug module if configured
aModules.unshift("sap.ui.debug.DebugEnv");
}
// enforce the core library as the first loaded module
var i = aModules.indexOf("sap.ui.core.library");
if ( i != 0 ) {
if ( i > 0 ) {
aModules.splice(i,1);
}
aModules.unshift("sap.ui.core.library");
}
// enable LessSupport if specified in configuration
if (Configuration.getValue("xx-lesssupport") && aModules.indexOf("sap.ui.core.plugin.LessSupport") == -1) {
Log.info("Including LessSupport into declared modules");
aModules.push("sap.ui.core.plugin.LessSupport");
}
var sPreloadMode = Configuration.getPreload();
// This flag controls the core initialization flow.
// We can switch to async when an async preload is used or the ui5loader
// is in async mode. The latter might also happen for debug scenarios
// where no preload is used at all.
var bAsync = sPreloadMode === "async" || sap.ui.loader.config().async;
// adding the following classList is done here for compatibility reasons
document.documentElement.classList.add("sapUiTheme-" + Configuration.getTheme());
Log.info("Declared theme " + Configuration.getTheme(), null, METHOD);
Log.info("Declared modules: " + aModules, METHOD);
this._setupContentDirection();
this._setupBrowser();
this._setupOS();
this._setupLang();
this._setupAnimation();
// create accessor to the Core API early so that initLibrary and others can use it
/**
* Retrieve the {@link sap.ui.core.Core SAPUI5 Core} instance for the current window.
* @returns {sap.ui.core.Core} the API of the current SAPUI5 Core instance.
* @public
* @function
* @ui5-global-only
*/
sap.ui.getCore = function() {
return that.getInterface();
};
// sync point 1 synchronizes document ready and rest of UI5 boot
var oSyncPoint1 = new SyncPoint("UI5 Document Ready", function(iOpenTasks, iFailures) {
that.init();
});
var iDocumentReadyTask = oSyncPoint1.startTask("document.ready");
var iCoreBootTask = oSyncPoint1.startTask("preload and boot");
var fnContentLoadedCallback = function() {
Log.trace("document is ready");
oSyncPoint1.finishTask(iDocumentReadyTask);
document.removeEventListener("DOMContentLoaded", fnContentLoadedCallback);
};
// immediately execute callback if the ready state is already 'complete'
if (document.readyState !== "loading") {
fnContentLoadedCallback();
} else {
// task 1 is to wait for document.ready
document.addEventListener("DOMContentLoaded", fnContentLoadedCallback);
}
// sync point 2 synchronizes all library preloads and the end of the bootstrap script
var oSyncPoint2 = new SyncPoint("UI5 Core Preloads and Bootstrap Script", function(iOpenTasks, iFailures) {
Log.trace("Core loaded: open=" + iOpenTasks + ", failures=" + iFailures);
that._boot(bAsync, function() {
oSyncPoint1.finishTask(iCoreBootTask);
Measurement.end("coreBoot");
});
});
// a helper task to prevent the premature completion of oSyncPoint2
var iCreateTasksTask = oSyncPoint2.startTask("create sp2 tasks task");
// load the version info file in case of a custom theme to determine
// the distribution version which should be provided in library.css requests.
if (Configuration.getValue("versionedLibCss")) {
var iVersionInfoTask = oSyncPoint2.startTask("load version info");
var fnCallback = function(oVersionInfo) {
if (oVersionInfo) {
Log.trace("Loaded \"sap-ui-version.json\".");
} else {
Log.error("Could not load \"sap-ui-version.json\".");
}
oSyncPoint2.finishTask(iVersionInfoTask);
};
// use async mode if library preload is async
if ( bAsync ) {
VersionInfo.load().then(fnCallback, function(oError) {
Log.error("Unexpected error when loading \"sap-ui-version.json\": " + oError);
oSyncPoint2.finishTask(iVersionInfoTask);
});
} else {
fnCallback(sap.ui.getVersionInfo({ async: bAsync, failOnError: false })); // legacy-relevant: sync path
}
}
this._polyfillFlexbox();
// when the bootstrap script has finished, it calls require("sap/ui/core/Core").boot()
var iBootstrapScriptTask = oSyncPoint2.startTask("bootstrap script");
this.boot = function() {
if (this.bBooted) {
return;
}
this.bBooted = true;
postConstructorTasks.call(this);
oSyncPoint2.finishTask(iBootstrapScriptTask);
};
function postConstructorTasks() {
// when a boot task is configured, add it to syncpoint2
var fnCustomBootTask = Configuration.getValue("xx-bootTask");
if ( fnCustomBootTask ) {
var iCustomBootTask = oSyncPoint2.startTask("custom boot task");
fnCustomBootTask( function(bSuccess) {
oSyncPoint2.finishTask(iCustomBootTask, typeof bSuccess === "undefined" || bSuccess === true );
});
}
if ( sPreloadMode === "sync" || sPreloadMode === "async" ) {
// determine set of libraries
var aLibs = that.aModules.reduce(function(aResult, sModule) {
var iPos = sModule.search(/\.library$/);
if ( iPos >= 0 ) {
aResult.push(sModule.slice(0, iPos));
}
return aResult;
}, []);
var pLibraryPreloaded = Library._load(aLibs, {
sync: !bAsync,
preloadOnly: true
});
if ( bAsync ) {
var iPreloadLibrariesTask = oSyncPoint2.startTask("preload bootstrap libraries");
pLibraryPreloaded.then(function() {
oSyncPoint2.finishTask(iPreloadLibrariesTask);
}, function() {
oSyncPoint2.finishTask(iPreloadLibrariesTask, false);
});
}
}
// initializes the application cachebuster mechanism if configured
var aACBConfig = Configuration.getAppCacheBuster();
if (aACBConfig && aACBConfig.length > 0) {
if ( bAsync ) {
var iLoadACBTask = oSyncPoint2.startTask("require AppCachebuster");
sap.ui.require(["sap/ui/core/AppCacheBuster"], function(AppCacheBuster) {
AppCacheBuster.boot(oSyncPoint2);
// finish the task only after ACB had a chance to create its own task(s)
oSyncPoint2.finishTask(iLoadACBTask);
});
} else {
var AppCacheBuster = sap.ui.requireSync('sap/ui/core/AppCacheBuster'); // legacy-relevant: Synchronous path
AppCacheBuster.boot(oSyncPoint2);
}
}
// Initialize support info stack
if (Configuration.getSupportMode() !== null) {
var iSupportInfoTask = oSyncPoint2.startTask("support info script");
var fnCallbackSupportBootstrapInfo = function(Support, Bootstrap) {
Support.initializeSupportMode(Configuration.getSupportMode(), bAsync);
Bootstrap.initSupportRules(Configuration.getSupportMode());
oSyncPoint2.finishTask(iSupportInfoTask);
};
if (bAsync) {
sap.ui.require(["sap/ui/core/support/Support", "sap/ui/support/Bootstrap"], fnCallbackSupportBootstrapInfo, function (oError) {
Log.error("Could not load support mode modules:", oError);
});
} else {
Log.warning("Synchronous loading of Support mode. Set preload configuration to 'async' or switch to asynchronous bootstrap to prevent these synchronous request.", "SyncXHR", null, function() {
return {
type: "SyncXHR",
name: "support-mode"
};
});
fnCallbackSupportBootstrapInfo(
sap.ui.requireSync("sap/ui/core/support/Support"), // legacy-relevant: Synchronous path
sap.ui.requireSync("sap/ui/support/Bootstrap") // legacy-relevant: Synchronous path
);
}
}
// Initialize test tools
if (Configuration.getTestRecorderMode() !== null) {
var iTestRecorderTask = oSyncPoint2.startTask("test recorder script");
var fnCallbackTestRecorder = function (Bootstrap) {
Bootstrap.init(Configuration.getTestRecorderMode());
oSyncPoint2.finishTask(iTestRecorderTask);
};
if (bAsync) {
sap.ui.require([
"sap/ui/testrecorder/Bootstrap"
], fnCallbackTestRecorder, function (oError) {
Log.error("Could not load test recorder:", oError);
});
} else {
Log.warning("Synchronous loading of Test recorder mode. Set preload configuration to 'async' or switch to asynchronous bootstrap to prevent these synchronous request.", "SyncXHR", null, function() {
return {
type: "SyncXHR",
name: "test-recorder-mode"
};
});
fnCallbackTestRecorder(
sap.ui.requireSync("sap/ui/testrecorder/Bootstrap") // legacy-relevant: Synchronous preloading
);
}
}
oSyncPoint2.finishTask(iCreateTasksTask);
}
},
metadata : {
// while this list contains mostly public methods,
// a set of private API is exposed for sap.ui.core restricted usage
publicMethods: [
// @public
// - Init
"isInitialized","attachInit",
"getConfiguration",
"lock", "unlock","isLocked",
// - UIArea & Rendering
"createUIArea", "getUIArea", "getUIDirty", "applyChanges", "getStaticAreaRef",
"createRenderManager",
// - Theming
"applyTheme","setThemeRoot","attachThemeChanged","detachThemeChanged",
"isThemeApplied",
"notifyContentDensityChanged",
// - Control & App dev.
"getCurrentFocusedControlId",
"isMobile",
"getEventBus",
"byId", "byFieldGroupId",
// - Libraries
"getLoadedLibraries", "loadLibrary", "initLibrary",
"getLibraryResourceBundle",
// - Models & Messaging
"setModel", "getModel", "hasModel",
"getMessageManager",
// - Events
"attachEvent","detachEvent",
"attachControlEvent", "detachControlEvent",
"attachParseError", "detachParseError",
"attachValidationError", "detachValidationError",
"attachFormatError", "detachFormatError",
"attachValidationSuccess", "detachValidationSuccess",
"attachLocalizationChanged", "detachLocalizationChanged",
// @protected
"isStaticAreaRef",
"fireFormatError", "fireValidationSuccess", "fireValidationError", "fireParseError",
// @private, @ui5-restricted sap.ui.core
// - Init
"boot",
// - UIArea & Rendering
"addPrerenderingTask",
// - Messaging
"setMessageManager",
// - Libraries
"attachLibraryChanged", "detachLibraryChanged",
"loadLibraries",
// - Theming
"attachThemeScopingChanged","detachThemeScopingChanged","fireThemeScopingChanged",
"includeLibraryTheme",
// @deprecated
// - Init & Plugins
"attachInitEvent",
"registerPlugin","unregisterPlugin",
// - Application/Root-Component
"setRoot",
"getRootComponent", "getApplication",
// - legacy registries & factories
"getControl", "getComponent", "getTemplate",
"createComponent",
// - Control dev.
"attachIntervalTimer", "detachIntervalTimer",
"getElementById",
// - UIArea & Rendering
"getRenderManager"]
}
});
/**
* Map of event names and ids, that are provided by this class
* @private
*/
Core.M_EVENTS = {ControlEvent: "ControlEvent", UIUpdated: "UIUpdated", ThemeChanged: "ThemeChanged", ThemeScopingChanged: "themeScopingChanged", LocalizationChanged: "localizationChanged",
LibraryChanged : "libraryChanged",
ValidationError : "validationError", ParseError : "parseError", FormatError : "formatError", ValidationSuccess : "validationSuccess"};
/**
* The core allows some friend components to register/deregister themselves
* @private
*/
Core.prototype._grantFriendAccess = function() {
// grant ElementMetadata "friend" access to Core for registration
ElementMetadata.prototype.register = function(oMetadata) {
Library._registerElement(oMetadata);
};
};
/**
* Set the document's dir property
* @private
*/
Core.prototype._setupContentDirection = function() {
var METHOD = "sap.ui.core.Core",
sDir = Configuration.getRTL() ? "rtl" : "ltr";
document.documentElement.setAttribute("dir", sDir); // webkit does not allow setting document.dir before the body exists
Log.info("Content direction set to '" + sDir + "'",null,METHOD);
};
/**
* Set the body's browser-related attributes.
* @private
*/
Core.prototype._setupBrowser = function() {
var METHOD = "sap.ui.core.Core";
//set the browser for CSS attribute selectors. do not move this to the onload function because Safari does not
//use the classes
var html = document.documentElement;
var b = Device.browser;
var id = b.name;
if (id) {
if (id === b.BROWSER.SAFARI && b.mobile) {
id = "m" + id;
}
id = id + (b.version === -1 ? "" : Math.floor(b.version));
html.dataset.sapUiBrowser = id;
Log.debug("Browser-Id: " + id, null, METHOD);
}
};
/**
* Set the body's OS-related attribute and CSS class
* @private
*/
Core.prototype._setupOS = function(html) {
var html = document.documentElement;
html.dataset.sapUiOs = Device.os.name + Device.os.versionStr;
var osCSS = null;
switch (Device.os.name) {
case Device.os.OS.IOS:
osCSS = "sap-ios";
break;
case Device.os.OS.ANDROID:
osCSS = "sap-android";
break;
}
if (osCSS) {
html.classList.add(osCSS);
}
};
/**
* Set the body's lang attribute and attach the localization change event
* @private
*/
Core.prototype._setupLang = function() {
var html = document.documentElement;
// append the lang info to the document (required for ARIA support)
var fnUpdateLangAttr = function() {
var oLocale = Configuration.getLocale();
oLocale ? html.setAttribute("lang", oLocale.toString()) : html.removeAttribute("lang");
};
fnUpdateLangAttr.call(this);
// listen to localization change event to update the lang info
this.attachLocalizationChanged(fnUpdateLangAttr, this);
};
/**
* Set the body's Animation-related attribute and configures jQuery animations accordingly.
* @private
*/
Core.prototype._setupAnimation = function() {
var html = document.documentElement;
var sAnimationMode = Configuration.getAnimationMode();
html.dataset.sapUiAnimationMode = sAnimationMode;
var bAnimation = (sAnimationMode !== Configuration.AnimationMode.minimal && sAnimationMode !== Configuration.AnimationMode.none);
html.dataset.sapUiAnimation = bAnimation ? "on" : "off";
if (typeof jQuery !== "undefined") {
jQuery.fx.off = !bAnimation;
}
};
/**
* Initializes the jQuery.support.useFlexBoxPolyfill property
* @private
*/
Core.prototype._polyfillFlexbox = function() {
/**
* Whether the current browser needs a polyfill as a fallback for flex box support
* @type {boolean}
* @private
* @name jQuery.support.useFlexBoxPolyfill
* @since 1.12.0
* @deprecated since version 1.16.0
*
* For backwards compatibility we can't remove the deprecated flexbox polyfill.
* However, if the compatibility version is 1.16 or higher then the polyfill
* should not be used.
*/
jQuery.support.useFlexBoxPolyfill = false;
};
/**
* Boots the core and injects the necessary CSS and JavaScript files for the library.
* Applications shouldn't call this method. It is automatically called by the bootstrap scripts (e.g. sap-ui-core.js)
*
* @param {boolean} bAsync - Flag if modules should be loaded asynchronously
* @param {function} fnCallback - Callback after modules have been loaded
* @returns {undefined|Promise}
* @private
*/
Core.prototype._boot = function(bAsync, fnCallback) {
// add CalendarClass to list of modules
this.aModules.push("sap/ui/core/date/" + Configuration.getCalendarType());
// load all modules now
if ( bAsync ) {
return this._requireModulesAsync().then(function() {
fnCallback();
});
}
Log.warning("Modules and libraries declared via bootstrap-configuration are loaded synchronously. Set preload configuration to" +
" 'async' or switch to asynchronous bootstrap to prevent these requests.", "SyncXHR", null, function() {
return {
type: "SyncXHR",
name: "legacy-module"
};
});
this.aModules.forEach( function(mod) {
var m = mod.match(/^(.*)\.library$/);
if ( m ) {
Library._load(m[1], {
sync: true
});
} else {
// data-sap-ui-modules might contain legacy jquery.sap.* modules
sap.ui.requireSync( /^jquery\.sap\./.test(mod) ? mod : mod.replace(/\./g, "/")); // legacy-relevant: Sync loading of modules and libraries
}
});
fnCallback();
};
Core.prototype._requireModulesAsync = function() {
var aLibs = [],
aModules = [];
this.aModules.forEach(function(sModule) {
var m = sModule.match(/^(.*)\.library$/);
if (m) {
aLibs.push(m[1]);
} else {
// data-sap-ui-modules might contain legacy jquery.sap.* modules
aModules.push(/^jquery\.sap\./.test(sModule) ? sModule : sModule.replace(/\./g, "/"));
}
});
// TODO: require libs and modules in parallel or define a sequence?
return Promise.all([
Library._load(aLibs),
new Promise(function(resolve) {
sap.ui.require(aModules, function() {
resolve(Array.prototype.slice.call(arguments));
});
})
]);
};
/**
* Applies the theme with the given name (by loading the respective style sheets, which does not disrupt the application).
*
* By default, the theme files are expected to be located at path relative to the respective control library ([libraryLocation]/themes/[themeName]).
* Different locations can be configured by using the method setThemePath() or by using the second parameter "sThemeBaseUrl" of applyTheme().
* Usage of this second parameter is a shorthand for setThemePath and internally calls setThemePath, so the theme location is then known.
*
* sThemeBaseUrl is a single URL to specify the default location of all theme files. This URL is the base folder below which the control library folders
* are located. E.g. if the CSS files are not located relative to the root location of UI5, but instead they are at locations like
* http://my.server/myapp/resources/sap/ui/core/themes/my_theme/library.css
* then the URL that needs to be given is:
* http://my.server/myapp/resources
* All theme resources are then loaded from below this folder - except if for a certain library a different location has been registered.
*
* If the theme resources are not all either below this base location or with their respective libraries, then setThemePath must be
* used to configure individual locations.
*
* @param {string} sThemeName the name of the theme to be loaded
* @param {string} [sThemeBaseUrl] the (optional) base location of the theme
* @public
*/
Core.prototype.applyTheme = function(sThemeName, sThemeBaseUrl) {
assert(typeof sThemeName === "string", "sThemeName must be a string");
assert(typeof sThemeBaseUrl === "string" || typeof sThemeBaseUrl === "undefined", "sThemeBaseUrl must be a string or undefined");
sThemeName = Configuration.normalizeTheme(sThemeName, sThemeBaseUrl);
// Configuration needs to be updated synchronously but only
// applyTheme in case theme changed
// Check is duplicated in applyTheme in ThemeManager
// be aware to keep both in sync
if ((sThemeName && Configuration.getTheme() != sThemeName)) {
Configuration.setTheme(sThemeName);
this._getThemeManager().then(function(ThemeManager) {
ThemeManager.applyTheme(sThemeName, sThemeBaseUrl, /* bForce = */ true);
});
}
};
/**
* Defines the root directory from below which UI5 should load the theme with the given name.
* Optionally allows restricting the setting to parts of a theme covering specific control libraries.
*
* Example:
* <pre>
* sap.ui.getCore().setThemeRoot("my_theme", "https://mythemeserver.com/allThemes");
* sap.ui.getCore().applyTheme("my_theme");
* </pre>
*
* will cause the following file to be loaded (assuming that the bootstrap is configured to load
* libraries <code>sap.m</code> and <code>sap.ui.layout</code>):
* <pre>
* https://mythemeserver.com/allThemes/sap/ui/core/themes/my_theme/library.css
* https://mythemeserver.com/allThemes/sap/ui/layout/themes/my_theme/library.css
* https://mythemeserver.com/allThemes/sap/m/themes/my_theme/library.css
* </pre>
*
* If parts of the theme are at different locations (e.g. because you provide a standard theme
* like "sap_belize" for a custom control library and this self-made part of the standard theme is at a
* different location than the UI5 resources), you can also specify for which control libraries the setting
* should be used, by giving an array with the names of the respective control libraries as second parameter:
* <pre>
* sap.ui.getCore().setThemeRoot("sap_belize", ["my.own.library"], "https://mythemeserver.com/allThemes");
* </pre>
*
* This will cause the Belize theme to be loaded from the UI5 location for all standard libraries.
* Resources for styling the <code>my.own.library</code> controls will be loaded from the configured
* location:
* <pre>
* https://sdk.openui5.org/resources/sap/ui/core/themes/sap_belize/library.css
* https://sdk.openui5.org/resources/sap/ui/layout/themes/sap_belize/library.css
* https://sdk.openui5.org/resources/sap/m/themes/sap_belize/library.css
* https://mythemeserver.com/allThemes/my/own/library/themes/sap_belize/library.css
* </pre>
*
* If the custom theme should be loaded initially (via bootstrap attribute), the <code>themeRoots</code>
* property of the <code>window["sap-ui-config"]</code> object must be used instead of calling
* <code>sap.ui.getCore().setThemeRoot(...)</code> in order to configure the theme location early enough.
*
* @param {string} sThemeName Name of the theme for which to configure the location
* @param {string[]} [aLibraryNames] Optional library names to which the configuration should be restricted
* @param {string} sThemeBaseUrl Base URL below which the CSS file(s) will be loaded from
* @param {boolean} [bForceUpdate=false] Force updating URLs of currently loaded theme
* @return {this} the Core, to allow method chaining
* @since 1.10
* @public
*/
Core.prototype.setThemeRoot = function(sThemeName, aLibraryNames, sThemeBaseUrl, bForceUpdate) {
this._getThemeManager().then(function(ThemeManager) {
ThemeManager.setThemeRoot(sThemeName, aLibraryNames, sThemeBaseUrl, bForceUpdate);
});
return this;
};
/**
* Initializes the Core after the initial page was loaded
* @private
*/
Core.prototype.init = function() {
if (this.bInitialized) {
return;
}
// provide core for event handling and UIArea creation
UIArea.setCore(this);
var METHOD = "sap.ui.core.Core.init()";
Log.info("Initializing",null,METHOD);
Measurement.end("coreInit");
this._setBodyAccessibilityRole();
var sWaitForTheme = Configuration.getValue('xx-waitForTheme');
// If there is no waitForTheme or ThemeManager is already available and theme is loaded render directly sync
if (this.isThemeApplied() || !sWaitForTheme) {
this._executeInitialization();
} else {
Rendering.suspend();
if (sWaitForTheme === "rendering") {
Rendering.notifyInteractionStep();
this._executeInitialization();
Rendering.getLogger().debug("delay initial rendering until theme has been loaded");
_oEventProvider.attachEventOnce(Core.M_EVENTS.ThemeChanged, function() {
Rendering.resume("after theme has been loaded");
}, this);
} else if (sWaitForTheme === "init") {
Rendering.getLogger().debug("delay init event and initial rendering until theme has been loaded");
Rendering.notifyInteractionStep();
_oEventProvider.attachEventOnce(Core.M_EVENTS.ThemeChanged, function() {
this._executeInitialization();
Rendering.resume("after theme has been loaded");
}, this);
}
// Require ThemeManager if not already done to ensure ThemeManager is available and ThemeChanged event will be fired
this._getThemeManager();
}
};
Core.prototype._executeOnInit = function() {
var vOnInit = Configuration.getValue("onInit");
// execute a configured init hook
if ( vOnInit ) {
if ( typeof vOnInit === "function" ) {
vOnInit();
} else if (typeof vOnInit === "string") {
// determine onInit being a module name prefixed via module or a global name
var aResult = /^module\:((?:[_$.\-a-zA-Z0-9]+\/)*[_$.\-a-zA-Z0-9]+)$/.exec(vOnInit);
if (aResult && aResult[1]) {
// ensure that the require is done async and the Core is finally booted!
setTimeout(sap.ui.require.bind(sap.ui, [aResult[1]]), 0);
} else {
// lookup the name specified in onInit and try to call the function directly
var fn = ObjectPath.get(vOnInit);
if (typeof fn === "function") {
fn();
} else {
Log.warning("[Deprecated] Do not use inline JavaScript code with the oninit attribute."
+ " Use the module:... syntax or the name of a global function");
/*
* In contrast to eval(), window.eval() executes the given string
* in the global context, without closure variables.
* See http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2
*/
// eslint-disable-next-line no-eval
window.eval(vOnInit); // csp-ignore-legacy-api
}
}
}
}
};
/**
* Creates a "rootComponent" or "sap.ui.app.Application".
* Both concepts are deprecated.
* Called during Core initialization.
* @deprecated since 1.95
* @private
*/
Core.prototype._setupRootComponent = function() {
var METHOD = "sap.ui.core.Core.init()";
// load the root component
// @deprecated concept, superseded by "sap/ui/core/ComponentSupport"
var sRootComponent = Configuration.getRootComponent();
if (sRootComponent) {
Log.info("Loading Root Component: " + sRootComponent,null,METHOD);
var oComponent = sap.ui.component({ //legacy-relevant: Deprecated rootComponent API
name: sRootComponent
});
this.oRootComponent = oComponent;
var sRootNode = Configuration.getValue("xx-rootComponentNode");
if (sRootNode && oComponent.isA('sap.ui.core.UIComponent')) {
var oRootNode = document.getElementById(sRootNode);
if (oRootNode) {
Log.info("Creating ComponentContainer for Root Component: " + sRootComponent,null,METHOD);
var ComponentContainer = sap.ui.requireSync('sap/ui/core/ComponentContainer'), // legacy-relevant: Deprecated rootComponent API
oContainer = new ComponentContainer({
component: oComponent,
propagateModel: true /* TODO: is this a configuration or do this by default? right now it behaves like the application */
});
oContainer.placeAt(oRootNode);
}
}
} else {
// @deprecated concept, superseded by "sap/ui/core/Component"
var sApplication = Configuration.getApplication();
if (sApplication) {
Log.warning("The configuration 'application' is deprecated. Please use the configuration 'component' instead! " +
"Please migrate from sap.ui.app.Application to sap.ui.core.Component.", "SyncXHR", null, function () {
return {
type: "Deprecation",
name: "sap.ui.core"
};
});
Log.info("Loading Application: " + sApplication,null,METHOD);
sap.ui.requireSync(sApplication.replace(/\./g, "/")); // legacy-relevant: deprecated
var oClass = ObjectPath.get(sApplication);
assert(oClass !== undefined, "The specified application \"" + sApplication + "\" could not be found!");
var oApplication = new oClass();
assert(BaseObject.isA(oApplication, 'sap.ui.app.Application'), "The specified application \"" + sApplication + "\" must be an instance of sap.ui.app.Application!");
}
}
};
Core.prototype._setBodyAccessibilityRole = function() {
var body = document.body;
//Add ARIA role 'application'
if (Configuration.getAccessibility() && Configuration.getAutoAriaBodyRole() && !body.getAttribute("role")) {
body.setAttribute("role", "application");
}
};
Core.prototype._executeInitListeners = function() {
var METHOD = "sap.ui.core.Core.init()";
// make sure that we have no concurrent modifications on the init listeners
var aCallbacks = this.aInitListeners;
// reset the init listener so that we are aware the listeners are already
// executed and the initialization phase is over / follow up registration
// would then immediately call the init event handler
this.aInitListeners = undefined;
// execute registered init event handlers
if (aCallbacks && aCallbacks.length > 0) {
// execute the callbacks
Log.info("Fire Loaded Event",null,METHOD);
aCallbacks.forEach(function(fn) {
fn();
});
}
};
Core.prototype._executeInitialization = function() {
var METHOD = "sap.ui.core.Core.init()"; // Because it's only used from init
if (this.bInitialized) {
return;
}
this.bInitialized = true;
Log.info("Initialized",null,METHOD);
// start the plugins
Log.info("Starting Plugins",null,METHOD);
this.startPlugins();
Log.info("Plugins started",null,METHOD);
this._executeOnInit();
this._setupRootComponent(); // @legacy-relevant: private API for 2 deprecated concepts "rootComponent" & "sap.ui.app.Application"
this._executeInitListeners();
};
/**
* Returns true if the Core has already been initialized. This means that instances
* of RenderManager etc. do already exist and the init event has already been fired
* (and will not be fired again).
*
* @return {boolean} whether the Core has already been initialized
* @public
*/
Core.prototype.isInitialized = function () {
return this.bInitialized;
};
/**
* Returns true, if the styles of the current theme are already applied, false otherwise.
*
* This function must not be used before the init event of the Core.
* If the styles are not yet applied a theme changed event will follow when the styles will be applied.
*
* @return {boolean} whether the styles of the current theme are already applied
* @public
*/
Core.prototype.isThemeApplied = function () {
ThemeManager = ThemeManager || sap.ui.require("sap/ui/core/theming/ThemeManager");
return ThemeManager ? ThemeManager.themeLoaded : false;
};
Core.prototype._getThemeManager = function (bClear) {
ThemeManager = ThemeManager || sap.ui.require("sap/ui/core/theming/ThemeManager");
if (!this.pThemeManager) {
if (!ThemeManager) {
this.pThemeManager = new Promise(function (resolve, reject) {
sap.ui.require(["sap/ui/core/theming/ThemeManager"], function (ThemeManager) {
resolve(ThemeManager);
}, reject);
});
} else {
this.pThemeManager = Promise.resolve(ThemeManager);
}
this.pThemeManager = this.pThemeManager.then(function(ThemeManager) {
ThemeManager.attachEvent("ThemeChanged", function(oEvent) {
this.fireThemeChanged(oEvent.getParameters());
}.bind(this));
return ThemeManager;
}.bind(this));
}
// This is only used within initLibrary to reset flag themeLoaded synchronously in case
// a theme for a new library will be loaded
if (ThemeManager && bClear) {
ThemeManager.reset();
}
return this.pThemeManager;
};
/**
* Registers a given function that is executed after the framework has been initialized.
*
* The method is executed only once and only if the framework has not been initialized already.
* This could be checked by calling {@link #isInitialized}, but in most cases it is more convenient to
* use {@link #attachInit} instead. This guarantees that the given function is executed exactly once,
* independent of the state of the framework.
*
* @param {function} fnFunction Function that is called after initialization of the framework
* @public
* @deprecated since 1.13.2 Register with the more convenient {@link #attachInit} function instead
*/
Core.prototype.attachInitEvent = function (fnFunction) {
assert(typeof fnFunction === "function", "fnFunction must be a function");
if (this.aInitListeners) {
this.aInitListeners.push(fnFunction);
}
};
/**
* Registers a given function that is executed after the framework has been initialized.
*
* The given function will either be called as soon as the framework has been initialized
* or, if it has been initialized already, it will be called immediately.
*
* More information about the initialization process and the steps it consists of can be found
* in the documentation topic "{@link topic:91f2c9076f4d1014b6dd926db0e91070 Initialization Process}".
*
* @param {function} fnFunction Function to be after initialization of the framework
* @public
* @since 1.13.2
*/
Core.prototype.attachInit = function (fnFunction) {
assert(typeof fnFunction === "function", "fnFunction must be a function");
if (this.aInitListeners) {
this.aInitListeners.push(fnFunction);
} else {
fnFunction();
}
};
/**
* Locks the Core. No browser events are dispatched to the controls.
*
* Lock should be called before and after the DOM is modified for rendering, roundtrips...
* Exceptions might be the case for asynchronous UI behavior
* @public
*/
Core.prototype.lock = function () {
// TODO clarify it the documentation is really (still?) true
this.bLocked = true;
};
/**
* Unlocks the Core.
*
* Browser events are dispatched to the controls again after this method is called.
* @public
*/
Core.prototype.unlock = function () {
this.bLocked = false;
};
/**
* Returns the locked state of the <code>sap.ui.core.Core</code>
* @return {boolean} locked state
* @public
*/
Core.prototype.isLocked = function () {
return this.bLocked;
};
/**
* Returns the Configuration of the Core.
*
* @return {sap.ui.core.Configuration} the Configuration of the current Core.
* @public
*/
Core.prototype.getConfiguration = function () {
return Configuration;
};
/**
* Creates a new <code>RenderManager</code> instance for use by the caller.
*
* @returns {sap.ui.core.RenderManager} A newly createdRenderManeger
* @public
* @deprecated Since version 0.15.0. Replaced by <code>createRenderManager()</code>
*/
Core.prototype.getRenderManager = function() {
return this.createRenderManager(); //this.oRenderManager;
};
/**
* Returns a new instance of the RenderManager for exclusive use by the caller.
*
* The caller must take care to destroy the render manager when it is no longer needed.
* Calling this method before the Core has been {@link #isInitialized initialized},
* is not recommended.
*
* @return {sap.ui.core.RenderManager} New instance of the RenderManager
* @public
*/
Core.prototype.createRenderManager = function() {
assert(this.isInitialized(), "A RenderManager should be created only after the Core has been initialized");
var oRm = new RenderManager();
return oRm.getInterface();
};
/**
* Returns the Id of the control/element currently in focus.
* @return {string} the Id of the control/element currently in focus.
* @public
*/
Core.prototype.getCurrentFocusedControlId = function() {
if (!this.isInitialized()) {
throw new Error("Core must be initialized");
}
FocusHandler = FocusHandler || sap.ui.require("sap/ui/core/FocusHandler");
return FocusHandler ? FocusHandler.getCurrentFocusedControlId() : null;
};
/**
* Loads the given library and its dependencies and makes its content available to the application.
*
*
* <h3>What it does</h3>
*
* When library preloads are not suppressed for the given library, then a library-preload bundle
* will be loaded for it. By default, the bundle will be loaded synchronously (for compatibility
* reasons). Only when the optional parameter <code>vUrl</code> is given as <code>true</code> or as
* a configuration object with a property of <code>async:true</code>, then the bundle will be loaded
* asynchronously and a <code>Promise</code> will be returned (preferred usage).
*
* After preloading the bundle, dependency information from the bundle is evaluated and any
* missing libraries are also preloaded.
*
* Only then the library entry module (named <code><i>your/lib</i>/library.js</code>) will be required
* and executed. The module is supposed to call <code>sap.ui.getCore().initLibrary(...)</code>
* providing the framework with additional metadata about the library, e.g. its version, the set of contained
* enums, types, interfaces, controls and elements and whether the library requires CSS. If the library
* requires CSS, a <link> will be added to the page referring to the corresponding <code>library.css</code>
* stylesheet for the library and the current theme.
*
* When the optional parameter <code>vUrl</code> is given as a string or when a configuration object is given
* with a non-empty, string-valued property <code>url</code>, then that URL will be registered for the
* namespace of the library and all resources will be loaded from that location. This is convenience for
* a call like
* <pre>
* sap.ui.loader.config({
* paths: {
* "lib/with/slashes": vUrl
* }
* });
* </pre>
*
* When the given library has been loaded already, no further action will be taken, especially, a given
* URL will not be registered! In the case of asynchronous loading, a Promise will be returned, but will be
* resolved immediately.
*
*
* <h3>When to use</h3>
*
* For applications that follow the best practices and use components with component descriptors (manifest.json),
* the framework will load all declared mandatory libraries and their dependencies automatically before instantiating
* the application component.
*
* The same is true for libraries that are listed in the bootstrap configuration (e.g. with the attribute
* <code>data-sap-ui-libs</code>). They will be loaded before the <code>init</code> event of the UI5 Core is fired.
*
* Only when an app declares a library to be a lazy library dependency or when code does not use descriptors at all,
* then an explicit call to <code>loadLibrary</code> becomes necessary. The call should be made before artifacts
* (controls, elements, types, helpers, modules etc.) from the library are used or required. This allows the framework
* to optimize access to those artifacts.
*
* For example, when an app uses a heavy-weight charting library that shouldn't be loaded during startup, it can
* declare it as "lazy" and load it just before it loads and displays a view that uses the charting library:
* <pre>
* sap.ui.getCore().loadLibrary("heavy.charting", {async: true})
* .then(function() {
* View.create({
* name: "myapp.views.HeavyChartingView",
* type: ViewType.XML
* });
* });
* </pre>
*
* @param {string} sLibrary Name of the library to load
* @param {string|boolean|object} [vUrl] URL to load the library from or the async flag or a complex configuration object
* @param {boolean} [vUrl.async] Whether to load the library asynchronously
* @param {string} [vUrl.url] URL to load the library from
* @returns {object|Promise<object>} An info object for the library (sync) or a Promise on it (async).
* @public
*/
Core.prototype.loadLibrary = function(sLibrary, vUrl) {
var mLibConfig = {
name: sLibrary
};
var mOptions = {
sync: true
};
if (typeof vUrl === "boolean") {
mOptions.sync = !vUrl;
} else if (typeof vUrl === "string") {
mLibConfig.url = vUrl;
} else if (typeof vUrl === "object") {
mOptions.sync = !vUrl.async;
mLibConfig.url = vUrl.url;
}
var vLoaded = Library._load(mLibConfig, mOptions);
if (!mOptions.sync) {
return vLoaded.then(function(aLibs) {
return aLibs[0];
});
} else {
return vLoaded[0];
}
};
/**
* Loads a set of libraries, preferably asynchronously.
*
* The module loading is still synchronous, so if a library loads additional modules besides
* its library.js file, those modules might be loaded synchronously by the library.js
* The async loading is only supported by the means of the library-preload.js(on) files, so if a
* library doesn't provide a preload or when the preload is deactivated (configuration, debug mode)
* then this API falls back to synchronous loading. However, the contract (Promise) remains valid
* and a Promise will be returned if async is specified - even when the real loading
* is done synchronously.
*
* @param {string[]} aLibraries set of libraries that should be loaded
* @param {object} [mOptions] configuration options
* @param {boolean} [mOptions.async=true] whether to load the libraries async (default)
* @param {boolean} [mOptions.preloadOnly=false] whether to preload the libraries only (default is to require them as well)
* @returns {Promise|undefined} returns a Promise in async mode, otherwise <code>undefined</code>
*
* @experimental Since 1.27.0 This API is not mature yet and might be changed or removed completely.
* Productive code should not use it, except code that is delivered as part of UI5.
* @private
* @ui5-restricted sap.ui.core,sap.ushell
*/
Core.prototype.loadLibraries = function(aLibraries, mOptions) {
mOptions = Object.assign({
async: true
}, mOptions);
mOptions.sync = !mOptions.async;
var pLoaded = Library._load(aLibraries, mOptions);
if (!mOptions.sync) {
return pLoaded;
} else {
return undefined;
}
};
/**
* Creates a component with the provided id and settings.
*
* When the optional parameter <code>sUrl</code> is given, then all request for resources of the
* library will be redirected to the given URL. This is convenience for a call to
* <pre>
* sap.ui.loader.config({
* paths: {
* "lib/with/slashes": vUrl
* }
* });
* </pre>
*
* @param {string|object} vComponent name of the component to import or object containing all needed parameters
* @param {string} [vComponent.name] name of the component to import
* @param {string} [vComponent.url] URL to load the component from
* @param {string} [vComponent.id] ID for the component instance
* @param {object} [vComponent.settings] settings object for the component
* @param {any} [vComponent.componentData] user specific data which is available during the whole lifecycle of the component
* @param {string} [sUrl] the URL to load the comp