UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

1,420 lines (1,266 loc) 117 kB
/*! * 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 &lt;link&gt; 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