UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

755 lines (681 loc) 29.3 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides class sap.ui.core.theming.ThemeManager sap.ui.define([ "sap/base/assert", "sap/base/Eventing", "sap/base/future", "sap/base/Log", "sap/base/i18n/Localization", "sap/ui/base/OwnStatics", "sap/ui/VersionInfo", "sap/ui/core/Theming", "sap/ui/core/theming/ThemeHelper", "sap/ui/dom/includeStylesheet" ], function( assert, Eventing, future, Log, Localization, OwnStatics, VersionInfo, Theming, ThemeHelper, includeStylesheet ) { "use strict"; const CUSTOM_CSS_CHECK = /\.sapUiThemeDesignerCustomCss/i; const MODULE_NAME = "sap.ui.core.theming.ThemeManager"; const CUSTOM_ID = "sap-ui-core-customcss"; const THEME_PREFIX = "sap-ui-theme-"; const LINK_ID_REGGEX_STRING = `^${THEME_PREFIX}(.*)`; const LINK_ID_CHECK = new RegExp(LINK_ID_REGGEX_STRING); const LINK_ID_WITH_VARIANT_CHECK = new RegExp(`${LINK_ID_REGGEX_STRING}-(?=\\[(.*)\\]).*$`); const oEventing = new Eventing(); const mAllLoadedLibraries = new Map(); const { attachChange, registerThemeManager, getThemePath } = OwnStatics.get(Theming); let CORE_VERSION; let pAllCssRequests = Promise.resolve(); let _customCSSAdded = false; let _themeCheckedForCustom = null; let sFallbackTheme = null; let sUi5Version; let mAllDistLibraries; const aFailedLibs = []; function isVersionInfoNeeded() { const theme = Theming.getTheme(); return !ThemeHelper.isStandardTheme(theme) && Theming.getThemeRoot(theme); } // UI5 version is only needed in case a theming service is active but we always add it to the request // therefore request it as early as possible const versionInfoLoaded = VersionInfo.load().then((oVersionInfo) => { sUi5Version = oVersionInfo.version; mAllDistLibraries = new Set(oVersionInfo.libraries.map((library) => library.name)); }, (e) => { if (isVersionInfoNeeded()) { Log.error("UI5 theming lifecycle requires valid version information when a theming service is active. Please check why the version info could not be loaded in this system.", e, MODULE_NAME); } }); /** * The ThemeManager is responsible for managing and applying themes within the application. * It handles the addition and updating of library CSS, including custom CSS if needed. It also * detects and deals with UI5 relevant library CSS added to the DOM for preloading and includes * them into the lifecycle. Additionally, it notifies subscribers after a theme has been applied, * regardless of whether the theme was applied successfully or not. * * @namespace * @author SAP SE * @private * @ui5-restricted sap.ui.core * @alias sap.ui.core.theming.ThemeManager */ const ThemeManager = { /** * Wether theme is already loaded or not * @private * @ui5-restricted sap.ui.core */ themeLoaded: true }; /** * Helper functions */ /** * Retrieves the library info object for theming. * * If the library info object does not exist yet, it will be created and stored. * Otherwise, the existing object is returned. * * @param {object} libInfo - The library info configuration. * @param {string} libInfo.libName - The name of the library. * @param {string} [libInfo.variant] - Optional variant name. * @param {string} [libInfo.fileName] - Optional file name for the CSS file. * @returns {object} The library info object with theming metadata and helper methods. */ function getLibraryInfo(libInfo) { /** * Creates a new library info object for theming purposes. * * The returned object contains metadata and helper methods for managing the theme CSS of a UI5 library, * including its ID, name, link ID, CSS link element, loading state, variant, and file name. * It also provides a method to generate the correct CSS URL for the library, considering RTL mode, * variants, and versioning. * * If the ID matches the custom CSS ID, the file name and library name are set accordingly. * The link ID and CSS link element are automatically determined. * * @param {object} libInfo - The library info configuration. * @param {string} libInfo.id - The unique ID for the library info object. * @param {string} libInfo.libName - The name of the library. * @param {string} [libInfo.variant] - Optional variant name. * @param {string} [libInfo.fileName] - Optional file name for the CSS file. * @returns {object} The library info object with theming metadata and helper methods. */ function createLibraryInfoObject(libInfo) { const oLibInfoTemplate = { id: "", libName: "", linkId: "", cssLinkElement: null, cssLoaded: null, failed: false, customCss: false, fileName: "library", variant: "", themeFallback: false, getUrl: function(sTheme) { const buildUrl = (skipVersionInfo) => { sTheme ??= Theming.getTheme(); /** * Custom libs which are not part of the DIST layer have no custom theme * except they provide it as part of the library (no themeroots for the custom library) */ if (mAllDistLibraries && !mAllDistLibraries.has(this.libName) && !ThemeHelper.isStandardTheme(sTheme) && Theming.getThemeRoot(sTheme, this.libName)) { sTheme = sFallbackTheme; // We don't want to use the UI5 default theme yet, // since there could be a better option after from the metadata of the first request if (!sTheme) { return undefined; } } const sCssBasePath = new URL(getThemePath(this.libName, sTheme), document.baseURI).toString(); const sVariant = this.variant || ""; let sCssPath; /* * Create the library file name. * By specifying a library name containing a colon (":") you can specify * the file name of the CSS file to include (ignoring RTL). */ const iIdx = this.libName.indexOf(":"); if (this.libName && iIdx == -1) { sCssPath = `${sCssBasePath}${this.fileName}${sVariant}${Localization.getRTL() ? "-RTL" : ""}.css`; } else { sCssPath = `${sCssBasePath}${this.libName.substring(iIdx + 1)}${sVariant}.css`; } // Create a link tag and set the URL as href in order to ensure AppCacheBuster handling. // AppCacheBuster ID is added to the href by defineProperty for the "href" property of // HTMLLinkElement in AppCacheBuster.js // Note: Considered to use AppCacheBuster.js#convertURL for adding the AppCachebuster ID // but there would be a dependency to AppCacheBuster as trade-off const oTmpLink = document.createElement("link"); oTmpLink.href = `${sCssPath}${skipVersionInfo ? "" : `?sap-ui-dist-version=${sUi5Version || CORE_VERSION || ""}`}`; return oTmpLink.href; }; return { urlPromise: versionInfoLoaded.then(buildUrl), url: buildUrl(), baseUrl: buildUrl(true) }; } }; const newLibInfo = Object.assign(oLibInfoTemplate, libInfo); if (newLibInfo.id === CUSTOM_ID) { newLibInfo.fileName = "custom"; newLibInfo.libName = "sap.ui.core"; } newLibInfo.linkId = `${newLibInfo.id === CUSTOM_ID ? "" : THEME_PREFIX}${newLibInfo.id}`; newLibInfo.cssLinkElement ??= document.querySelector(`link[id='${newLibInfo.linkId}']`); return newLibInfo; } libInfo.id ??= `${libInfo.libName}${libInfo.variant ? `-[${libInfo.variant}]` : ""}`; if (!mAllLoadedLibraries.has(libInfo.id)) { libInfo = createLibraryInfoObject(libInfo); mAllLoadedLibraries.set(libInfo.id, libInfo); } else { libInfo = mAllLoadedLibraries.get(libInfo.id); } return libInfo; } /** * Includes a library theme into the current page (if a variant is specified it * will include the variant library theme) * @param {string} libraryInfo the library info object * @private * @ui5-restricted sap.ui.core */ function includeLibraryTheme(libraryInfo) { const { libName, variant, version } = libraryInfo; assert(typeof libName === "string", "libName must be a string"); assert(variant === undefined || typeof variant === "string", "variant must be a string or undefined"); if (libName === "sap.ui.core") { CORE_VERSION = version; } /** * include the stylesheet for the library (except for "classic" and "legacy" lib) * @deprecated */ if (libName === "sap.ui.legacy" || libName === "sap.ui.classic") { return; } const oLibInfo = getLibraryInfo({ libName, variant }); updateThemeUrl({ libInfo: oLibInfo, suppressFOUC: true }); } /** * Adds or updates the CSS link element for the specified library info object and theme. * * If a link element for the library already exists, this function compares the current stylesheet URL * with the new one for the given theme and determines whether an update is necessary. * If the URLs differ, it loads the new stylesheet, manages the loading state, handles FOUC (Flash of Unstyled Content) markers, * and updates the internal state accordingly. The function also triggers theme lifecycle events such as success, failure, * and completion, and manages the global themeLoaded flag. * * Additionally, the function adds the CSS loading promise to the collection of all requested CSS promises, * ensuring that the themeApplied event is fired only after all CSS files have finished loading. * * @param {object} params - Parameters for updating the theme URL. * @param {object} params.libInfo - The library info object containing theming metadata. * @param {string} params.theme - The name of the theme to apply. * @param {boolean} params.suppressFOUC - Whether to suppress Flash of Unstyled Content (FOUC) handling. * @param {boolean} params.force - Whether to including stylesheet regardless the baseUrl is identical or not. * Use the old URL to prevent unnecessary requests. */ function updateThemeUrl({libInfo, theme, suppressFOUC, force}) { if (!sUi5Version && isVersionInfoNeeded()) { Log.error("UI5 theming lifecycle requires valid version information when a theming service is active. Please check why the version info could not be loaded in this system.", undefined, MODULE_NAME); } // Compare the link including the UI5 version only if it is already available; otherwise, compare the link without the version to prevent unnecessary requests. const sOldUrl = libInfo.cssLinkElement?.href; const sOldUrlWoVersion = sOldUrl?.replace(/\?.*/, ""); const sUrl = libInfo.getUrl(theme).baseUrl; if (!sUrl || sOldUrlWoVersion !== sUrl || force) { if (suppressFOUC) { pAllCssRequests = Promise.resolve(); ThemeManager.themeLoaded = false; Log.debug(`Register theme change for library ${libInfo.id}`, undefined, MODULE_NAME); } libInfo.finishedLoading = false; if (suppressFOUC) { // Only add stylesheet in case there is no existing stylesheet or the href is different // use the special FOUC handling for initially existing stylesheets // to ensure that they are not just replaced when using the // includeStyleSheet API and to be removed later fnAddFoucmarker(libInfo.linkId); } const pCssLoaded = libInfo.getUrl(theme).urlPromise.then((sUrl) => { if (sUrl) { Log.debug(`Add new CSS for library ${libInfo.id} with URL: ${sUrl}`, undefined, MODULE_NAME); return includeStylesheet({ url: force ? sOldUrl : sUrl, id: libInfo.linkId }); } else { // If there is no URL, a theme fallback must be detected first. // We reject here because and add only a placeholder link element to the DOM. // The handleThemeFailed function will process this rejection and apply the fallback // theme for the library once it has been detected. const oLink = document.createElement("link"); oLink.setAttribute("id", libInfo.linkId); oLink.setAttribute("rel", "stylesheet"); if (libInfo.cssLinkElement) { libInfo.cssLinkElement.parentNode.replaceChild(oLink, libInfo.cssLinkElement); } else { document.head.insertBefore(oLink, document.getElementById(CUSTOM_ID)); } libInfo.cssLinkElement = oLink; return Promise.reject(); } }); if (libInfo.cssLoaded) { libInfo.cssLoaded.aborted = true; } includeStyleSheetPostProcessing(libInfo, pCssLoaded, suppressFOUC); } } /** * Handles post-processing of CSS link elements after they have been requested. * * This function manages the lifecycle of CSS loading for a library by attaching handlers to the CSS loading promise. * It handles success and failure cases, as well as generic post-processing that occurs regardless of the request status. * The function is responsible for: * * 1. Managing the loading state of the CSS for the library * 2. Removing FOUC (Flash of Unstyled Content) markers when loading completes * 3. Updating references to the CSS link element * 4. Triggering appropriate theme lifecycle events (success, failure, completion) * 5. Updating the global CSS request promise collection * 6. Managing the global theme loaded state * 7. Firing the "applied" event when all CSS requests have completed * * The function uses promise chaining to ensure proper sequencing of operations and to handle * both successful and failed CSS loading scenarios. It also includes an abort mechanism to * prevent stale CSS requests from affecting the UI when a new request supersedes them. * * @param {object} libInfo - The library info object containing metadata about the library and its CSS * @param {Promise} cssLoadededPromise - The promise that resolves when the CSS has been loaded * @param {boolean} suppressFOUC - Whether to suppress Flash of Unstyled Content during theme changes */ function includeStyleSheetPostProcessing(libInfo, cssLoadededPromise, suppressFOUC) { const cssLoaded = libInfo.cssLoaded = cssLoadededPromise.finally(function() { if (!cssLoaded.aborted) { libInfo.finishedLoading = true; document.querySelector(`link[data-sap-ui-foucmarker='${libInfo.linkId}']`)?.remove(); libInfo.cssLinkElement = document.getElementById(`${libInfo.linkId}`); Log.debug(`New stylesheet loaded and old stylesheet removed for library: ${libInfo.id}`, undefined, MODULE_NAME); } }).then(function() { if (!cssLoaded.aborted) { handleThemeSucceeded(libInfo.id); } }).catch(function() { if (!cssLoaded.aborted) { handleThemeFailed(libInfo.id); } }).finally(function() { if (!cssLoaded.aborted) { handleThemeFinished(libInfo.id); pAllCssRequests = Promise.allSettled([...mAllLoadedLibraries.values()].map((libInfo) => libInfo.cssLoaded)); pAllCssRequests.finally(function() { if (this === pAllCssRequests && !applyFallbackTheme()) { Log.debug("Theme change finished", undefined, MODULE_NAME); // Even if suppressFOUC is not set, we must fire the event if themeLoaded was previously set to false, // because this indicates that at least one theme change was caused by a theming-relevant trigger. if (suppressFOUC || !ThemeManager.themeLoaded) { ThemeManager.themeLoaded = true; oEventing.fireEvent("applied", { theme: Theming.getTheme() }); } } }.bind(pAllCssRequests)); } }); } /** * Updates all existing CSS link elements to reflect the provided theme. * * This function iterates over all loaded library info objects and updates their CSS link elements * to ensure they point to the correct theme resources. It guarantees that all CSS links are up-to-date * with respect to the given theme, RTL mode, SAP UI5 distribution version, and theme roots. * * @param {string} themeName - The name of the theme to apply. * @param {boolean} suppressFOUC - Whether to suppress Flash of Unstyled Content (FOUC) handling. */ function updateThemeUrls(themeName, suppressFOUC) { for (const [, libInfo] of mAllLoadedLibraries) { updateThemeUrl({libInfo, themeName, suppressFOUC}); } } /** * Handles post-processing after a CSS request for a library has finished loading and was not aborted. * * This function checks if custom CSS needs to be added or updated for the current theme and performs the necessary actions. * It also attempts to derive a fallback theme from the theme root if the requested theme could not be loaded, * and applies the fallback theme for the affected library if available. * * @param {string} libId - The ID of the library whose CSS request has finished. */ function handleThemeFinished(libId) { const sThemeName = Theming.getTheme(); ThemeHelper.reset(); if (!_customCSSAdded || _themeCheckedForCustom != sThemeName) { if (!ThemeHelper.isStandardTheme(sThemeName) && checkCustom(libId)) { const oCustomLibInfo = getLibraryInfo({ id: CUSTOM_ID }); updateThemeUrl({ libInfo: oCustomLibInfo, suppressFOUC: true }); _customCSSAdded = true; _themeCheckedForCustom = sThemeName; Log.debug("Delivered custom CSS needs to be loaded, Theme not yet applied", undefined, MODULE_NAME); } else if (_customCSSAdded) { // remove stylesheet once the particular class is not available (e.g. after theme switch) // check for custom theme was not successful, so we need to make sure there are no custom style sheets attached document.querySelector(`LINK[id='${CUSTOM_ID}']`)?.remove(); mAllLoadedLibraries.delete(CUSTOM_ID); Log.debug("Custom CSS removed", undefined, MODULE_NAME); _customCSSAdded = false; } } // Only retrieve the fallback theme once per ThemeManager cycle if (!sFallbackTheme) { const sThemeRoot = Theming.getThemeRoot(sThemeName, libId); if (sThemeRoot) { const rBaseTheme = /~v=[^\/]+\(([a-zA-Z0-9_]+)\)/; // base theme should be matched in the first capturing group sFallbackTheme = rBaseTheme.exec(sThemeRoot)?.[1]; } } } /** * Handles post-processing after a CSS request for a library has successfully finished loading and was not aborted. * * This function attempts to derive a fallback theme from the theme metadata if the requested theme could not be loaded. * The fallback theme is determined from the "Extends" property in the theme metadata. * * @param {string} libId - The ID of the library whose CSS request has successfully finished. */ function handleThemeSucceeded(libId) { if (!sFallbackTheme) { const oThemeMetaData = ThemeHelper.getMetadata(libId); if (oThemeMetaData && oThemeMetaData.Extends && oThemeMetaData.Extends[0]) { sFallbackTheme = oThemeMetaData.Extends[0]; } } } /** * Handles post-processing after a CSS request for a library has failed and was not aborted. * * This function detects whether the fallback theme should be requested for the library, * based on the current theme and the loading state of the CSS link element. * * @param {string} libId - The ID of the library whose CSS request has failed. */ function handleThemeFailed(libId) { const oLibThemingInfo = getLibraryInfo({id: libId}); // Collect all libs that failed to load and no fallback has been applied, yet. // The fallback relies on custom theme metadata, so it is not done for standard themes if (!ThemeHelper.isStandardTheme(Theming.getTheme()) && !oLibThemingInfo.themeFallback) { // Check for error marker (data-sap-ui-ready=false) and that there are no rules // to be sure the stylesheet couldn't be loaded at all. // E.g. in case an @import within the stylesheet fails, the error marker will // also be set, but in this case no fallback should be done as there is a (broken) theme if (oLibThemingInfo.cssLinkElement && !(oLibThemingInfo.cssLinkElement.sheet && hasSheetCssRules(oLibThemingInfo.cssLinkElement.sheet))) { aFailedLibs.push(oLibThemingInfo); } } } /** * Applies the fallback theme for libraries whose CSS failed to load. * * This function processes all libraries in the failed library queue and attempts to load * their CSS resources using a fallback theme. The fallback theme is validated and may be * adjusted to the latest default theme if it is no longer supported. * * For each failed library, the function: * - Logs a warning about the failed custom theme * - Updates the theme URL to point to the fallback theme resources * - Marks the library with a fallback flag to prevent repeated fallback attempts * * @returns {boolean} Returns true if new CSS requests were triggered for one or more libraries, * false if no new CSS was requested (i.e., no libraries required fallback). */ function applyFallbackTheme() { const pOldAllCssRequests = pAllCssRequests; // pass derived fallback theme through our default theme handling // in case the fallback theme is not supported anymore, we fall up to the latest default theme sFallbackTheme = ThemeHelper.validateAndFallbackTheme(sFallbackTheme); while (aFailedLibs.length) { const oLib = aFailedLibs.shift(); Log.warning(`Custom theme '${Theming.getTheme()}' could not be loaded for library '${oLib.id}'. Falling back to its base theme '${sFallbackTheme}'.`, undefined, MODULE_NAME); // Change the URL to load the fallback theme updateThemeUrl({ libInfo: oLib, theme: sFallbackTheme, suppressFOUC: true }); // remember the lib to prevent doing the fallback multiple times // (if the fallback also can't be loaded) oLib.themeFallback = true; } // Check whether fallback CSS requests have been triggered or not return pOldAllCssRequests !== pAllCssRequests; } /** * checks if a particular class is available * * @param {string} lib The library name * @returns {boolean} Wether lib has custom css or not */ function checkCustom(lib) { const cssFile = window.document.getElementById(`${THEME_PREFIX}${lib}`); if (!cssFile) { return false; } /* Check if custom.css indication rule is applied to <link> element The rule looks like this: link[id^="sap-ui-theme-"]::after Selector is to apply it to the <link> elements. */ const style = window.getComputedStyle(cssFile, ':after'); let content = style ? style.getPropertyValue('content') : null; if (content && content !== "none") { try { // Strip surrounding quotes (single or double depending on browser) if (content[0] === "'" || content[0] === '"') { content = content.substring(1, content.length - 1); } // Cast to boolean (returns true if string equals "true", otherwise false) return content === "true"; } catch (e) { // parsing error future.errorThrows("Custom check: Error parsing JSON string for custom.css indication.", { cause: e }); } } //*********************************** // Fallback legacy customcss check //*********************************** /* * checks if a particular class is available at the beginning of the stylesheet */ const aRules = cssFile.sheet ? safeAccessSheetCssRules(cssFile.sheet) : null; if (!aRules || aRules.length === 0) { Log.warning(`Custom check: Failed retrieving a CSS rule from stylesheet ${lib}`, undefined, MODULE_NAME); return false; } // we should now have some rule name ==> try to match against custom check for (let i = 0; (i < 2 && i < aRules.length) ; i++) { if (CUSTOM_CSS_CHECK.test(aRules[i].selectorText)) { return true; } } return false; } // helper to add the FOUC marker to the CSS for the given id function fnAddFoucmarker(sLinkId) { const oLink = document.getElementById(sLinkId); if (oLink) { oLink.dataset.sapUiFoucmarker = sLinkId; } } /** * 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(). * 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 {object} oTheme Theme object containing the old and the new theme * @param {string} oTheme.new Name of the new theme * @param {string} oTheme.old Name of the previous theme */ function applyTheme(oTheme) { const html = document.documentElement; const sTheme = oTheme.new; for (const [, oLibInfo] of mAllLoadedLibraries) { delete oLibInfo.themeFallback; } sFallbackTheme = null; Log.debug(`ThemeManager: Theme changed from ${oTheme.old} to ${sTheme}`, undefined, MODULE_NAME); updateThemeUrls(sTheme, /* bSuppressFOUC */ true); // modify the <html> tag's CSS class with the theme name html.classList.remove(`sapUiTheme-${oTheme.old}`); html.classList.add(`sapUiTheme-${sTheme}`); } function safeAccessSheetCssRules(sheet) { try { return sheet.cssRules; } catch (e) { // Firefox throws a SecurityError or InvalidAccessError if "sheet.cssRules" // is accessed on a stylesheet with 404 response code. // Most browsers also throw when accessing from a different origin (CORS). return null; } } function hasSheetCssRules(sheet) { const aCssRules = safeAccessSheetCssRules(sheet); return !!aCssRules && aCssRules.length > 0; } // Collect all UI5 relevant CSS files which have been added upfront // and add them to UI5 theming lifecycle document.querySelectorAll(`link[id^=${THEME_PREFIX}]`).forEach(function(cssLinkElement) { let bPreloadedCssReady = true; const sLinkId = cssLinkElement.getAttribute("id"); const [,libName, variant] = (sLinkId.match(LINK_ID_WITH_VARIANT_CHECK) || sLinkId.match(LINK_ID_CHECK)); const oLibInfo = getLibraryInfo({ libName, variant, linkId: sLinkId }); Log.info(`Preloaded CSS for library ${libName + (variant ? ` with variant ${variant} ` : "")} detected: ${cssLinkElement.href}`, undefined, MODULE_NAME); const { promise: cssLoaded, resolve, reject } = Promise.withResolvers(); const handleReady = function(bError) { if (bError) { reject(); } else { resolve(); } }; try { bPreloadedCssReady = !!(cssLinkElement.sheet?.href === cssLinkElement.href && cssLinkElement.sheet?.cssRules); if (!bPreloadedCssReady) { ThemeManager.themeLoaded = bPreloadedCssReady; cssLinkElement.addEventListener("load", () => { handleReady(false); }); cssLinkElement.addEventListener("error", () => { handleReady(true); }); } else { handleReady(!(cssLinkElement.sheet.cssRules.length > 0)); } includeStyleSheetPostProcessing(oLibInfo, cssLoaded, true); } catch (e) { // If the stylesheet is cross-origin and throws a security error, we can't verify directly Log.info("Could not detect ready state of preloaded CSS. Request stylesheet again to verify the response status", undefined, MODULE_NAME); ThemeManager.themeLoaded = false; updateThemeUrl({ libInfo: oLibInfo, suppressFOUC: true, force: true }); } }); // set CSS class for the theme name document.documentElement.classList.add("sapUiTheme-" + Theming.getTheme()); Log.info(`Declared theme ${Theming.getTheme()}`, undefined, MODULE_NAME); attachChange(function(oEvent) { var mThemeRoots = oEvent.themeRoots; var oTheme = oEvent.theme; var oLib = oEvent.library; if (mThemeRoots && mThemeRoots.forceUpdate) { updateThemeUrls(Theming.getTheme()); } if (oTheme) { applyTheme(oTheme); } if (oLib) { includeLibraryTheme(oLib); } }); // handle RTL changes Localization.attachChange(function(oEvent){ const bRTL = oEvent.rtl; if (bRTL !== undefined) { updateThemeUrls(Theming.getTheme()); } }); registerThemeManager((fireApplied) => { oEventing.attachEvent("applied", fireApplied); }); OwnStatics.set(ThemeManager, { /** * Returns libraryInfoObject * * @param {string} libInfoId The ID of the libraryInfo object * @returns {Map<string, object>|object|undefined} A map with all available libraryInfoObjects, a specific libraryInfoObject * or undefined in case a specific libraryInfoObject was requested but does not exists * @private * @ui5-restricted sap.ui.core.theming.Parameters */ getAllLibraryInfoObjects: (libInfoId) => { if (libInfoId) { return mAllLoadedLibraries.get(libInfoId); } const mAllInfoObjects = new Map(mAllLoadedLibraries); mAllInfoObjects.delete(CUSTOM_ID); return mAllInfoObjects; } }); return ThemeManager; });