UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

1,456 lines (1,268 loc) 155 kB
/*! * OpenUI5 * (c) Copyright 2009-2021 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/Global', 'sap/ui/base/BindingParser', 'sap/ui/base/DataType', 'sap/ui/base/EventProvider', 'sap/ui/base/Interface', 'sap/ui/base/Object', 'sap/ui/base/ManagedObject', 'sap/ui/performance/trace/Interaction', './Component', './Configuration', './Control', './Element', './ElementMetadata', './FocusHandler', './RenderManager', './ResizeHandler', './ThemeCheck', './UIArea', './message/MessageManager', "sap/ui/util/ActivityDetection", "sap/ui/dom/getScrollbarSize", "sap/base/i18n/ResourceBundle", "sap/base/Log", "sap/ui/performance/Measurement", "sap/ui/security/FrameOptions", "sap/base/assert", "sap/ui/dom/includeStylesheet", "sap/base/util/ObjectPath", "sap/base/util/Version", "sap/base/util/array/uniqueSort", "sap/base/util/uid", 'sap/ui/performance/trace/initTraces', 'sap/base/util/LoaderExtensions', 'sap/base/util/isEmptyObject', 'sap/base/util/each', 'sap/ui/VersionInfo', 'sap/ui/events/jquery/EventSimulation' ], function( jQuery, Device, Global, BindingParser, DataType, EventProvider, Interface, BaseObject, ManagedObject, Interaction, Component, Configuration, Control, Element, ElementMetadata, FocusHandler, RenderManager, ResizeHandler, ThemeCheck, UIArea, MessageManager, ActivityDetection, getScrollbarSize, ResourceBundle, Log, Measurement, FrameOptions, assert, includeStylesheet, ObjectPath, Version, uniqueSort, uid, initTraces, LoaderExtensions, isEmptyObject, each, VersionInfo /* ,EventSimulation */ ) { "use strict"; /*global Map, Promise */ // when the Core module has been executed before, don't execute it again if (sap.ui.getCore && sap.ui.getCore()) { return sap.ui.getCore(); } // Initialize SAP Passport or FESR initTraces(); // share the rendering log with the UIArea var oRenderLog = UIArea._oRenderLog; /** * Set of libraries that have been loaded and initialized already. * This is maintained separately from Core.mLibraries to protect it against * modification from the outside (objects in mLibraries are currently exposed * by getLoadedLibraries()) */ var mLoadedLibraries = {}; /** * Bookkeeping for the preloading of libraries. * * Might contain an object for each library (keyed by the library name). * While the preload is pending for a library, the object has a property preload = true. * In any case, the object contains a promise that fulfills / rejects when the preload * fulfills / rejects. * @private */ var mLibraryPreloadBundles = {}; /** * 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.87.1 * @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; /** * Whether the DOM is ready (document.ready) * @private */ this.bDomReady = false; /** * Available plugins in the order of registration. * @private */ this.aPlugins = []; /** * Collection of loaded or adhoc created libraries, keyed by their name. * @private */ this.mLibraries = {}; /** * Already loaded resource bundles keyed by library and locale. * @private * @see sap.ui.core.Core.getLibraryResourceBundle */ this.mResourceBundles = {}; /** * Currently created UIAreas keyed by their id. * @private * @todo FIXME how can a UI area ever be removed? */ this.mUIAreas = {}; /** * 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; /** * The ID of a timer that will execute the next rendering. * * A non-falsy value indicates that a timer exists already, or at least that no * new timer needs to be created as. During the boot phase, this member is set * to the special value <code>this</code> which is non-falsy and which should never * represent a valid timer ID (no chance of misinterpretation). */ this._sRerenderTimer = this; /** * Tasks that are called just before the rendering starts. * @private */ this.aPrerenderingTasks = []; Log.info("Creating Core",null,METHOD); Measurement.start("coreComplete", "Core.js - complete"); Measurement.start("coreBoot", "Core.js - boot"); Measurement.start("coreInit", "Core.js - init"); /** * Object holding the interpreted configuration * Initialized from the global "sap-ui-config" object and from URL parameters * @private */ this.oConfiguration = new Configuration(this); // initialize frameOptions script (anti-clickjacking, etc.) var oFrameOptionsConfig = this.oConfiguration["frameOptionsConfig"] || {}; oFrameOptionsConfig.mode = this.oConfiguration.getFrameOptions(); oFrameOptionsConfig.allowlistService = this.oConfiguration.getAllowlistService(); this.oFrameOptions = new FrameOptions(oFrameOptionsConfig); // enable complex bindings if configured if ( this.oConfiguration["bindingSyntax"] === "complex" ) { ManagedObject.bindingParser = BindingParser.complexParser; } // switch bindingParser to designTime mode if configured if (this.oConfiguration["xx-designMode"] == true ) { BindingParser._keepBindingStrings = true; } // let Element and Component get friend access to the respective register/deregister methods this._grantFriendAccess(); // handle modules var aModules = this.oConfiguration.modules; if ( this.oConfiguration.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 (this.oConfiguration["xx-lesssupport"] && aModules.indexOf("sap.ui.core.plugin.LessSupport") == -1) { Log.info("Including LessSupport into declared modules"); aModules.push("sap.ui.core.plugin.LessSupport"); } // determine preload mode (e.g. resolve default or auto) var sPreloadMode = this.oConfiguration.preload; // if debug sources are requested, then the preload feature must be deactivated if ( window["sap-ui-debug"] === true ) { sPreloadMode = ""; } // when the preload mode is 'auto', it will be set to 'async' or 'sync' for optimized sources // depending on whether the ui5loader is configured async if ( sPreloadMode === "auto" ) { if (window["sap-ui-optimized"]) { sPreloadMode = sap.ui.loader.config().async ? "async" : "sync"; } else { sPreloadMode = ""; } } // write back the determined mode for later evaluation (e.g. loadLibrary) this.oConfiguration.preload = sPreloadMode; // 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; // evaluate configuration for library preload file types this.oConfiguration['xx-libraryPreloadFiles'].forEach(function(v){ var fields = String(v).trim().split(/\s*:\s*/), name = fields[0], fileType = fields[1]; if ( fields.length === 1 ) { fileType = name; name = ''; } if ( /^(?:none|js|json|both)$/.test(fileType) ) { mLibraryPreloadFileTypes[name] = fileType; } }); Log.info("Declared modules: " + aModules, METHOD); this._setupThemes(); 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(); }; // create the RenderManager so it can be used already this.oRenderManager = new RenderManager(); // sync point 1 synchronizes document ready and rest of UI5 boot var oSyncPoint1 = new SyncPoint("UI5 Document Ready", function(iOpenTasks, iFailures) { that.bDomReady = true; 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 (this.oConfiguration["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); }; // only use async mode if library preload is async var vReturn = sap.ui.getVersionInfo({ async: bAsync, failOnError: false }); if (vReturn instanceof Promise) { vReturn.then(fnCallback, function(oError) { // this should only happen when there is a script error as "failOnError=false" // prevents throwing a loading error (e.g. HTTP 404) Log.error("Unexpected error when loading \"sap-ui-version.json\": " + oError); oSyncPoint2.finishTask(iVersionInfoTask); }); } else { fnCallback(vReturn); } } // when a boot task is configured, add it to syncpoint2 var fnCustomBootTask = this.oConfiguration["xx-bootTask"]; if ( fnCustomBootTask ) { var iCustomBootTask = oSyncPoint2.startTask("custom boot task"); fnCustomBootTask( function(bSuccess) { oSyncPoint2.finishTask(iCustomBootTask, typeof bSuccess === "undefined" || bSuccess === true ); }); } this._polyfillFlexbox(); // when the bootstrap script has finished, it calls sap.ui.getCore().boot() var iBootstrapScriptTask = oSyncPoint2.startTask("bootstrap script"); this.boot = function() { if (this.bBooted) { return; } this.bBooted = true; oSyncPoint2.finishTask(iBootstrapScriptTask); }; if ( sPreloadMode === "sync" || sPreloadMode === "async" ) { // determine set of libraries var aLibs = aModules.reduce(function(aResult, sModule) { var iPos = sModule.search(/\.library$/); if ( iPos >= 0 ) { aResult.push(sModule.slice(0, iPos)); } return aResult; }, []); var preloaded = this.loadLibraries(aLibs, { async: bAsync, preloadOnly: true }); if ( bAsync ) { var iPreloadLibrariesTask = oSyncPoint2.startTask("preload bootstrap libraries"); preloaded.then(function() { oSyncPoint2.finishTask(iPreloadLibrariesTask); }, function() { oSyncPoint2.finishTask(iPreloadLibrariesTask, false); }); } } // initializes the application cachebuster mechanism if configured var aACBConfig = this.oConfiguration.getAppCacheBuster(); if (aACBConfig && aACBConfig.length > 0) { var AppCacheBuster = sap.ui.requireSync('sap/ui/core/AppCacheBuster'); AppCacheBuster.boot(oSyncPoint2); } // Initialize support info stack if (this.oConfiguration.getSupportMode() !== null) { var iSupportInfoTask = oSyncPoint2.startTask("support info script"); var fnCallbackSupportBootstrapInfo = function(Support, Bootstrap) { Support.initializeSupportMode(that.oConfiguration.getSupportMode(), bAsync); Bootstrap.initSupportRules(that.oConfiguration.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"), sap.ui.requireSync("sap/ui/support/Bootstrap") ); } } // Initialize test tools if (this.oConfiguration.getTestRecorderMode() !== null) { var iTestRecorderTask = oSyncPoint2.startTask("test recorder script"); var fnCallbackTestRecorder = function (Bootstrap) { Bootstrap.init(that.oConfiguration.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") ); } } oSyncPoint2.finishTask(iCreateTasksTask); }, metadata : { publicMethods: ["boot", "isInitialized","isThemeApplied","attachInitEvent","attachInit","getRenderManager","createRenderManager", "getConfiguration", "setRoot", "createUIArea", "getUIArea", "getUIDirty", "getElementById", "getCurrentFocusedControlId", "getControl", "getComponent", "getTemplate", "lock", "unlock","isLocked", "attachEvent","detachEvent","applyChanges", "getEventBus", "applyTheme","setThemeRoot","attachThemeChanged","detachThemeChanged","getStaticAreaRef", "attachThemeScopingChanged","detachThemeScopingChanged","fireThemeScopingChanged", "notifyContentDensityChanged", "registerPlugin","unregisterPlugin","getLibraryResourceBundle", "byId", "getLoadedLibraries", "loadLibrary", "loadLibraries", "initLibrary", "includeLibraryTheme", "setModel", "getModel", "hasModel", "isMobile", "attachControlEvent", "detachControlEvent", "attachIntervalTimer", "detachIntervalTimer", "attachParseError", "detachParseError", "fireParseError", "attachValidationError", "detachValidationError", "fireValidationError", "attachFormatError", "detachFormatError", "fireFormatError", "attachValidationSuccess", "detachValidationSuccess", "fireValidationSuccess", "attachLocalizationChanged", "detachLocalizationChanged", "attachLibraryChanged", "detachLibraryChanged", "isStaticAreaRef", "createComponent", "getRootComponent", "getApplication", "setMessageManager", "getMessageManager","byFieldGroupId", "addPrerenderingTask"] } }); /** * 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"}; // Id of the static UIArea var STATIC_UIAREA_ID = "sap-ui-static"; // to protect against nested rendering we use an array of Steps instead of a single one Core.aFnDone = []; /** * The core allows some friend components to register/deregister themselves * @private */ Core.prototype._grantFriendAccess = function() { var that = this; // grant ElementMetadata "friend" access to Core for registration ElementMetadata.prototype.register = function(oMetadata) { that.registerElementClass(oMetadata); }; // grant Element "friend" access to Core / FocusHandler to update the given elements focus info Element._updateFocusInfo = function(oElement) { if (that.oFocusHandler) { that.oFocusHandler.updateControlFocusInfo(oElement); } }; }; /** * Initializes the window "sap-ui-config" property, sets theme roots, initializes sTheme, sets theme CSS classes * @private */ Core.prototype._setupThemes = function() { var METHOD = "sap.ui.core.Core"; var oCfgData = window["sap-ui-config"]; // Configuration might have a themeRoot, if so integrate it in themeroots if ( this.oConfiguration.themeRoot ) { oCfgData = oCfgData || {}; oCfgData.themeroots = oCfgData.themeroots || {}; oCfgData.themeroots[this.oConfiguration.getTheme()] = this.oConfiguration.themeRoot; } if (oCfgData) { // read themeRoots configuration if (oCfgData.themeroots) { for (var themeName in oCfgData.themeroots) { var themeRoot = oCfgData.themeroots[themeName]; if (typeof themeRoot === "string") { this.setThemeRoot(themeName, themeRoot); } else { for (var lib in themeRoot) { if (lib.length > 0) { this.setThemeRoot(themeName, [lib], themeRoot[lib]); } else { this.setThemeRoot(themeName, themeRoot[lib]); } } } } } } // set CSS class for the theme name this.sTheme = this.oConfiguration.getTheme(); document.documentElement.classList.add("sapUiTheme-" + this.sTheme); Log.info("Declared theme " + this.sTheme,null,METHOD); }; /** * Set the document's dir property * @private */ Core.prototype._setupContentDirection = function() { var METHOD = "sap.ui.core.Core", sDir = this.oConfiguration.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 sf and ie do 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; case Device.os.OS.BLACKBERRY: osCSS = "sap-bb"; break; case Device.os.OS.WINDOWS_PHONE: osCSS = "sap-winphone"; 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 = this.oConfiguration.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() { // We check for the existence of the configuration object, because the _setupAnimation function // will first be called from the Configuration constructor within the Core constructor. // During this first call, the configuration object is not yet set on the Core instance. if (this.oConfiguration) { var html = document.documentElement; var bAnimation = this.oConfiguration.getAnimation(); html.dataset.sapUiAnimation = bAnimation ? "on" : "off"; if (typeof jQuery !== "undefined") { jQuery.fx.off = !bAnimation; } var sAnimationMode = this.oConfiguration.getAnimationMode(); html.dataset.sapUiAnimationMode = sAnimationMode; } }; /** * 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) { // if a list of preloaded library CSS is configured, request a merged CSS (if application did not already do it) var aCSSLibs = this.oConfiguration['preloadLibCss']; if (aCSSLibs && aCSSLibs.length > 0 && !aCSSLibs.appManaged) { this.includeLibraryTheme("sap-ui-merged", undefined, "?l=" + aCSSLibs.join(",")); } // 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.oConfiguration.modules.forEach( function(mod) { var m = mod.match(/^(.*)\.library$/); if ( m ) { this.loadLibrary(m[1]); } else { // data-sap-ui-modules might contain legacy jquery.sap.* modules sap.ui.requireSync( /^jquery\.sap\./.test(mod) ? mod : mod.replace(/\./g, "/")); } }.bind(this)); fnCallback(); }; Core.prototype._requireModulesAsync = function() { var aLibs = [], aModules = []; this.oConfiguration.modules.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([ this.loadLibraries(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 = this.oConfiguration._normalizeTheme(sThemeName, sThemeBaseUrl); if (sThemeBaseUrl) { this.setThemeRoot(sThemeName, sThemeBaseUrl); } // only apply the theme if it is different from the active one if (sThemeName && this.sTheme != sThemeName) { var sCurrentTheme = this.sTheme; var html = document.documentElement; this._updateThemeUrls(sThemeName, /* bSuppressFOUC */ true); this.sTheme = sThemeName; this.oConfiguration._setTheme(sThemeName); // modify the <html> tag's CSS class with the theme name html.classList.remove("sapUiTheme-" + sCurrentTheme); html.classList.add("sapUiTheme-" + sThemeName); // notify the listeners if ( this.oThemeCheck ) { this.oThemeCheck.fireThemeChangedEvent(false); } } }; // this function is also used by "sap.ui.core.ThemeCheck" to load a fallback theme for a single library Core.prototype._updateThemeUrl = function(oLink, sThemeName, bSuppressFOUC) { var sLibName, iQueryIndex = oLink.href.search(/[?#]/), sLibFileName, sQuery, sStandardLibFilePrefix = "library", sRTL = this.oConfiguration.getRTL() ? "-RTL" : "", sHref, pos; // derive lib name from id via regex var mLinkId = /^sap-ui-theme(?:skeleton)?-(.*)$/i.exec(oLink.id); if (Array.isArray(mLinkId)) { sLibName = mLinkId[1]; } else { // fallback to legacy logic sLibName = oLink.id.slice(13); // length of "sap-ui-theme-" } if (iQueryIndex > -1) { // Split href on query and/or fragment to check for the standard lib file prefix sLibFileName = oLink.href.substring(0, iQueryIndex); sQuery = oLink.href.substring(iQueryIndex); } else { sLibFileName = oLink.href; sQuery = ""; } // Get basename of stylesheet (e.g. "library.css") sLibFileName = sLibFileName.substring(sLibFileName.lastIndexOf("/") + 1); // handle 'variants' if ((pos = sLibName.indexOf("-[")) > 0) { // assumes that "-[" does not occur as part of a library name sStandardLibFilePrefix += sLibName.slice(pos + 2, -1); // 2=length of "-]" sLibName = sLibName.slice(0, pos); } // try to distinguish "our" library css from custom css included with the ':' notation in includeLibraryTheme if ( sLibFileName === (sStandardLibFilePrefix + ".css") || sLibFileName === (sStandardLibFilePrefix + "-RTL.css") ) { sLibFileName = sStandardLibFilePrefix + sRTL + ".css"; } sHref = this._getThemePath(sLibName, sThemeName) + sLibFileName + sQuery; if ( sHref != oLink.href ) { // sap/ui/dom/includeStylesheet has a special FOUC handling // which is activated once the attribute data-sap-ui-foucmarker is // present on the link to be replaced (usage of the Promise // API is not sufficient as it will change the sync behavior) if (bSuppressFOUC) { oLink.dataset.sapUiFoucmarker = oLink.id; } // Replace the current <link> tag with a new one. // Changing "oLink.href" would also trigger loading the new stylesheet but // the load/error handlers would not get called which causes issues with the ThemeCheck // as the "data-sap-ui-ready" attribute won't be set. includeStylesheet(sHref, oLink.id); } }; // modify style sheet URLs to point to the given theme, using the current RTL mode Core.prototype._updateThemeUrls = function(sThemeName, bSuppressFOUC) { // select "our" stylesheets var oQueryResult = document.querySelectorAll("link[id^=sap-ui-theme-],link[id^=sap-ui-themeskeleton-]"); Array.prototype.forEach.call(oQueryResult, function(oHTMLElement) { this._updateThemeUrl(oHTMLElement, sThemeName, bSuppressFOUC); }.bind(this)); }; /** * Makes sure to register the correct module path for the given library and theme * in case a themeRoot has been defined. * * @param {string} sLibName Library name (dot separated) * @param {string} sThemeName Theme name * @private */ Core.prototype._ensureThemeRoot = function(sLibName, sThemeName) { if (this._mThemeRoots) { var path = this._mThemeRoots[sThemeName + " " + sLibName] || this._mThemeRoots[sThemeName]; // check whether for this combination (theme+lib) a URL is registered or for this theme a default location is registered if (path) { path = path + sLibName.replace(/\./g, "/") + "/themes/" + sThemeName + "/"; registerModulePath(sLibName + ".themes." + sThemeName, path); } } }; /** * Returns the URL of the folder in which the CSS file for the given theme and the given library is located. * * @param {string} sLibName Library name (dot separated) * @param {string} sThemeName Theme name * @returns {string} module path URL (ends with a slash) * @private */ Core.prototype._getThemePath = function(sLibName, sThemeName) { // make sure to register correct theme module path in case themeRoots are defined this._ensureThemeRoot(sLibName, sThemeName); // use the library location as theme location return getModulePath(sLibName + ".themes." + sThemeName, "/"); }; /** * 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://openui5.hana.ondemand.com/resources/sap/ui/core/themes/sap_belize/library.css * https://openui5.hana.ondemand.com/resources/sap/ui/layout/themes/sap_belize/library.css * https://openui5.hana.ondemand.com/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) { assert(typeof sThemeName === "string", "sThemeName must be a string"); assert((Array.isArray(aLibraryNames) && typeof sThemeBaseUrl === "string") || (typeof aLibraryNames === "string" && sThemeBaseUrl === undefined), "either the second parameter must be a string (and the third is undefined), or it must be an array and the third parameter is a string"); if (!this._mThemeRoots) { this._mThemeRoots = {}; } // normalize parameters if (typeof aLibraryNames === "string") { bForceUpdate = sThemeBaseUrl; sThemeBaseUrl = aLibraryNames; aLibraryNames = undefined; } sThemeBaseUrl = sThemeBaseUrl + (sThemeBaseUrl.slice( -1) == "/" ? "" : "/"); if (aLibraryNames) { // registration of URL for several libraries for (var i = 0; i < aLibraryNames.length; i++) { var lib = aLibraryNames[i]; this._mThemeRoots[sThemeName + " " + lib] = sThemeBaseUrl; } } else { // registration of theme default base URL this._mThemeRoots[sThemeName] = sThemeBaseUrl; } // Update theme urls when theme roots of currently loaded theme have changed if (bForceUpdate && sThemeName === this.sTheme) { this._updateThemeUrls(this.sTheme); } return this; }; /** * Initializes the Core after the initial page was loaded * @private */ Core.prototype.init = function() { if (this.bInitialized) { return; } var METHOD = "sap.ui.core.Core.init()"; // ensure that the core is booted now (e.g. loadAllMode) this.boot(); Log.info("Initializing",null,METHOD); this.oFocusHandler = new FocusHandler(document.body, this); this.oRenderManager._setFocusHandler(this.oFocusHandler); //Let the RenderManager know the FocusHandler this.oResizeHandler = new ResizeHandler(this); this.oThemeCheck = new ThemeCheck(this); Log.info("Initialized",null,METHOD); Measurement.end("coreInit"); // start the plugins Log.info("Starting Plugins",null,METHOD); this.startPlugins(); Log.info("Plugins started",null,METHOD); this._createUIAreas(); this._setBodyAccessibilityRole(); this.oThemeCheck.fireThemeChangedEvent(true); var sWaitForTheme = this.oConfiguration['xx-waitForTheme']; if ( this.isThemeApplied() || !sWaitForTheme ) { Core.aFnDone.push(Interaction.notifyAsyncStep()); this._executeInitialization(); this.renderPendingUIUpdates("during Core init"); // directly render without setTimeout, so rendering is guaranteed to be finished when init() ends Measurement.end("coreComplete"); } else if (sWaitForTheme === "rendering") { Core.aFnDone.push(Interaction.notifyAsyncStep()); this._executeInitialization(); oRenderLog.debug("delay initial rendering until theme has been loaded"); _oEventProvider.attachEventOnce(Core.M_EVENTS.ThemeChanged, function() { setTimeout( this.renderPendingUIUpdates.bind(this, "after theme has been loaded"), Device.browser.safari ? 50 : 0 ); }, this); Measurement.end("coreComplete"); } else if (sWaitForTheme === "init") { oRenderLog.debug("delay init event and initial rendering until theme has been loaded"); Core.aFnDone.push(Interaction.notifyAsyncStep()); _oEventProvider.attachEventOnce(Core.M_EVENTS.ThemeChanged, function() { this._executeInitialization(); setTimeout( this.renderPendingUIUpdates.bind(this, "after theme has been loaded"), Device.browser.safari ? 50 : 0 ); Measurement.end("coreComplete"); }, this); } }; Core.prototype._createUIAreas = function() { var oConfig = this.oConfiguration; // create any pre-configured UIAreas // if ( oConfig.areas && oConfig.areas.length > 0 ) { if ( oConfig.areas ) { // Log.warning("deprecated config option '(data-sap-ui-)areas' used."); for (var i = 0, l = oConfig.areas.length; i < l; i++) { this.createUIArea(oConfig.areas[i]); } oConfig.areas = undefined; } }; Core.prototype._executeOnInit = function() { var vOnInit = this.oConfiguration.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 */ window.eval(vOnInit); // csp-ignore-legacy-api } } } this.oConfiguration.onInit = undefined; } }; Core.prototype._setupRootComponent = function() { var METHOD = "sap.ui.core.Core.init()", oConfig = this.oConfiguration; // load the root component var sRootComponent = oConfig.getRootComponent(); if (sRootComponent) { Log.info("Loading Root Component: " + sRootComponent,null,METHOD); var oComponent = sap.ui.component({ name: sRootComponent }); this.oRootComponent = oComponent; var sRootNode = oConfig["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'), 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 LEGACY CODE: load the application (TODO: remove when Application is removed!) var sApplication = oConfig.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, "/")); 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 oConfig = this.oConfiguration, body = document.body; //Add ARIA role 'application' if (oConfig.getAccessibility() && oConfig.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() { if (this.bInitialized) { return; } this.bInitialized = true; this._executeOnInit(); this._setupRootComponent(); 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 () { return ThemeCheck.themeLoaded; }; /** * 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 {functio