@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
397 lines (347 loc) • 13.3 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"sap/base/Log",
"./library",
'sap/ui/core/Core',
"sap/ui/thirdparty/jquery",
"sap/ui/core/StaticArea"
],
function(Log, mLibrary, Core, jQuery, StaticArea) {
"use strict";
/**
* STATIC MEMBERS
*/
var SAP_ILLUSTRATION_POOL_ID = 'sap-ui-illustration-pool',
SAP_ILLUSTRATION_PATTERNS_NAME = '-Patterns',
SAP_ILLUSTRATION_SET_NAME = 'sapIllus',
SAP_ILLUSTRATION_SET_PATH = sap.ui.require.toUrl("sap/m/themes/base/illustrations/"),
SAP_ILLUSTRATION_SET_SYMBOLS = Object.keys(mLibrary.IllustratedMessageType);
/*
* A map of registered sets
* key: set name
* value: configuration object containing
* key: sPath
* value: string path of the illustration set
* key: aSymbols
* value: array of the symbols names which the illustration set supports
* key: bIsPending
* value: boolean of whether or not the Illustration Set is loading
*/
var oSetRegistry = Object.create(null);
oSetRegistry[SAP_ILLUSTRATION_SET_NAME] = {
sPath: SAP_ILLUSTRATION_SET_PATH,
aSymbols: SAP_ILLUSTRATION_SET_SYMBOLS,
bIsPending: false
};
var aSymbolsInDOM = [], // Array with IDs of the symbols which are already in the DOM
oAssetRegistry = Object.create(null), // Object with IDs of the assets which are loaded or being loaded
oLoadedSymbols = Object.create(null), // Object which stores the loaded HTML raw data for a given asset for future reuse (might not be in the DOM yet)
oRequiredAssets = Object.create(null); // Object which stores the last required asset ID for a given instance
/**
* <code>IllustrationPool</code> loads the illustration assets (SVGs) via XMLHttpRequest requests.
*
* The successfully loaded data is kept in the DOM (div with ID <code>sap-illustration-pool</code>)
* in the <code>sap-ui-static</code> DOM element.
*
* To load a given asset, register its illustration set through the
* {@link sap.m.IllustrationPool.registerIllustrationSet registerIllustrationSet} API of <code>IllustrationPool</code>.
* The exception being the <code>sapIllus</code>, which is the default illustration set
* that is registered by default.
*
* The default behavior of <code>IllustrationPool</code> is to load/require an asset only
* when it's needed by using the {@link sap.m.IllustrationPool.loadAsset} API.
* When registering the new illustration set, you are given the option to load all of its assets.
*
* If some of the assets are not loaded initially, you can load the rest of them on
* a later state with the {@link sap.m.IllustrationPool.loadRestOfTheAssets} API.
*
* @namespace
* @since 1.98
* @public
* @alias sap.m.IllustrationPool
*/
var IllustrationPool = {};
/**
* PUBLIC METHODS
*/
/**
* Loads an SVG asset depending on the input asset ID.
*
* @param {string} sAssetId The string ID of the asset being loaded
* @param {string} sInstanceId the ID of the Illustration instance which is requiring the asset
* @static
* @public
*/
IllustrationPool.loadAsset = function (sAssetId, sInstanceId) {
var sSet;
if (sAssetId === "") {
Log.error("ID of the asset can not be blank/empty.");
return;
}
// if the the asset is required by an instance - cache its ID
if (sInstanceId) {
oRequiredAssets[sInstanceId] = sAssetId;
}
if (oAssetRegistry[sAssetId]) {
Log.info("The asset with ID '" + sAssetId + "' is either loaded or being loaded.");
// if the the asset is required by an instance and it's loaded - update the DOM pool
if (sInstanceId && oLoadedSymbols[sAssetId]) {
IllustrationPool._updateDOMPool();
}
return;
}
sSet = sAssetId.split("-")[0];
if (!oSetRegistry[sSet]) {
Log.error("The illustration set '" + sSet + "' is not registered. Please register it before requiring one of its assets.");
return;
}
oAssetRegistry[sAssetId] = true;
IllustrationPool._requireSVG(sSet, sAssetId, sInstanceId);
};
/**
* Loads the rest of the SVG assets for a given illustration set.
*
* @param {string} sIllustrationSet The illustration set, the rest of the assets should be loaded for
* @static
* @public
*/
IllustrationPool.loadRestOfTheAssets = function(sIllustrationSet) {
var aSymbols;
if (!oSetRegistry[sIllustrationSet]) {
throw new Error("The illustration set '" + sIllustrationSet + "' is not registered. Please register it before requiring rest of its assets.");
}
aSymbols = oSetRegistry[sIllustrationSet].aSymbols;
if (Array.isArray(aSymbols)) {
aSymbols.forEach(function(sSymbol) {
IllustrationPool.loadAsset(sIllustrationSet + "-Dot-" + sSymbol);
IllustrationPool.loadAsset(sIllustrationSet + "-Spot-" + sSymbol);
IllustrationPool.loadAsset(sIllustrationSet + "-Dialog-" + sSymbol);
IllustrationPool.loadAsset(sIllustrationSet + "-Scene-" + sSymbol);
});
}
};
/**
* Registers an illustration set, which is needed before loading any of its assets.
*
* @param {object} oConfig object containing the name and the path of the Illustration Set
* @param {string} oConfig.setFamily Name of the Illustration Set
* @param {string} oConfig.setURI URL Path of the Illustration Set
* @param {boolean} bLoadAllResources whether or not all of the assets for the Illustration Set
* should be loaded once the metadata is loaded
* @static
* @public
*/
IllustrationPool.registerIllustrationSet = function(oConfig, bLoadAllResources) {
var sName = oConfig.setFamily,
sPath = oConfig.setURI;
if (oSetRegistry[sName]) {
if (oSetRegistry[sName].bIsPending) {
Log.warning("Illustration Set is currently being loaded.");
} else {
Log.warning("Illustration Set already registered.");
}
return;
}
// add trailing slash if necessary for more convenience
if (sPath.substr(sPath.length - 1) !== "/") {
sPath += "/";
}
oSetRegistry[sName] = Object.create(null);
oSetRegistry[sName].sPath = sPath;
oSetRegistry[sName].bIsPending = true;
IllustrationPool._loadMetadata(sName, sPath, bLoadAllResources);
};
/**
* PRIVATE METHODS
*/
/**
* Adds an asset to the Illustration Pool's DOM element inner HTML.
*
* @param {string} sHTML string containing the raw HTML of an asset
* @param {string} sId string containing the ID of an asset
* @static
* @private
*/
IllustrationPool._addAssetToDOMPool = function(sHTML, sId) {
IllustrationPool._getDOMPool().insertAdjacentHTML("beforeend", sHTML);
if (sId) {
aSymbolsInDOM.push(sId);
}
};
/**
* Returns the DOM for the Illustration Pool.
* It also loads the mandatory patterns for the default Illustration Set once the DOM is created.
*
* @return {Object} DOM reference of the Illustration Pool
* @static
* @private
*/
IllustrationPool._getDOMPool = function() {
var oDOMPool = document.getElementById(SAP_ILLUSTRATION_POOL_ID);
if (oDOMPool === null) {
oDOMPool = document.createElement("div");
oDOMPool.id = SAP_ILLUSTRATION_POOL_ID;
oDOMPool.setAttribute("aria-hidden", "true");
StaticArea.getDomRef().appendChild(oDOMPool);
// Load the patterns for the default illustration set after the DOM Pool is created
IllustrationPool.loadAsset(SAP_ILLUSTRATION_SET_NAME + SAP_ILLUSTRATION_PATTERNS_NAME);
}
return oDOMPool;
};
/**
* Loads the metadata of an Illustration Set.
*
* @param {string} sName the name of the Illustration Set for which the metadata is being loaded
* @param {string} sPath the path of the Illustration Set for which the metadata is being loaded
* @param {boolean} bLoadAllResources whether or not all of the assets for the Illustration Set should be loaded
* @return {Promise} Promise which resolves when the metadata is loaded
* @static
* @private
*/
IllustrationPool._loadMetadata = function(sName, sPath, bLoadAllResources) {
var sMetadataPath = sPath + "metadata.json";
return new Promise(function (fnResolve) {
jQuery.ajax(sMetadataPath, {
type: "GET",
dataType: "json",
success: function (oMetadataJSON) {
Log.info("Metadata for illustration set (" + sName + ") successfully loaded");
IllustrationPool._metadataLoaded(sName, oMetadataJSON, bLoadAllResources);
fnResolve(oMetadataJSON); // passing the json in the resolve for testing purposes
},
error: function (jqXHR, sStatus) {
if (sStatus !== "abort") { // log an error if it isn't aborted
Log.error("Metadata from: " + sMetadataPath + " file path could not be loaded");
delete oSetRegistry[sName];
fnResolve();
}
}
});
});
};
/**
* Hook which is triggered when the metadata for an Illustration Set is loaded.
*
* @param {string} sName the name of the Illustration Set for which the metadata is loaded
* @param {object} oMetadataJSON the loaded metadata object
* @param {boolean} bLoadAllResources whether or not all of the assets for the Illustration Set should be loaded
* @static
* @private
*/
IllustrationPool._metadataLoaded = function(sName, oMetadataJSON, bLoadAllResources) {
var aSymbols = oMetadataJSON.symbols,
bHasPatterns = oMetadataJSON.requireCustomPatterns;
oSetRegistry[sName].aSymbols = aSymbols;
// Load the patterns (if available) as soon as possible, since they can be used in any of the symbols.
if (bHasPatterns) {
IllustrationPool.loadAsset(sName + SAP_ILLUSTRATION_PATTERNS_NAME);
}
if (bLoadAllResources) {
IllustrationPool.loadRestOfTheAssets(sName);
}
oSetRegistry[sName].bIsPending = false;
};
/**
* Removes an asset from the Illustration Pool's DOM element inner HTML.
*
* @param {string} sId string containing the ID of the asset which should be removed from the Illustration Pool's DOM
* @static
* @private
*/
IllustrationPool._removeAssetFromDOMPool = function(sId) {
var oDOMPool = document.getElementById(SAP_ILLUSTRATION_POOL_ID),
oAssetDOM;
if (oDOMPool !== null) {
oAssetDOM = document.getElementById(sId);
if (oAssetDOM !== null) {
oDOMPool.removeChild(oAssetDOM);
aSymbolsInDOM.splice(aSymbolsInDOM.indexOf(sId), 1);
}
}
};
/**
* Loads an SVG asset.
*
* @param {string} sSet the name of the Illustration Set for which the asset is being loaded
* @param {string} sId the ID of the asset being loaded
* @param {string} sInstanceId the ID of the Illustration instance which is requiring the asset
* @return {Promise} Promise which resolves when the SVG asset is loaded
* @static
* @private
*/
IllustrationPool._requireSVG = function(sSet, sId, sInstanceId) {
return new Promise(function (fnResolve) {
jQuery.ajax(oSetRegistry[sSet].sPath + sId + ".svg", {
type: "GET",
dataType: "html",
success: function (sHTML) {
// if asset is a symbol, and not a pattern
if (sId.indexOf(SAP_ILLUSTRATION_PATTERNS_NAME) === -1) {
// cache the loaded symbol
oLoadedSymbols[sId] = sHTML;
// update the DOM if the asset is required by an instance
if (sInstanceId) {
IllustrationPool._updateDOMPool();
}
} else {
// add the pattern immediately
IllustrationPool._addAssetToDOMPool(sHTML);
}
fnResolve(sHTML);
},
error: function (jqXHR, sStatus) {
if (sStatus !== "abort") { // log an error if it isn't aborted
delete oAssetRegistry[sId];
Core.getEventBus().publish("sapMIllusPool-assetLdgFailed");
Log.error(sId + " asset could not be loaded");
fnResolve();
}
}
});
});
};
/**
* Updates the IllustrationPool's DOM node. Adds the currently required assets (if they are
* not already there) and removes the ones which are not needed.
*
* @static
* @private
*/
IllustrationPool._updateDOMPool = function() {
var oAssetsToKeep = Object.create(null),
oAssetsToAdd = Object.create(null),
sCurrAsset,
iCurrIndexInDOM;
for (var sCurrInstance in oRequiredAssets) {
sCurrAsset = oRequiredAssets[sCurrInstance];
iCurrIndexInDOM = aSymbolsInDOM.indexOf(sCurrAsset);
// if the asset is not already in the DOM, remember its ID and add it later, if it's loaded
if (iCurrIndexInDOM === -1) {
oAssetsToAdd[sCurrAsset] = true;
} else {
// if the asset is already in the DOM, remember its ID to help remove the not needed ones later
oAssetsToKeep[sCurrAsset] = true;
}
}
// remove the symbols which are not used and are not required
for (var i = 0; i < aSymbolsInDOM.length; i++) {
sCurrAsset = aSymbolsInDOM[i];
if (!oAssetsToKeep[sCurrAsset]) {
IllustrationPool._removeAssetFromDOMPool(sCurrAsset);
i--;
}
}
for (var sId in oAssetsToAdd) {
sCurrAsset = oLoadedSymbols[sId];
// add the missing required asset to the DOM only if it's loaded
if (sCurrAsset) {
IllustrationPool._addAssetToDOMPool(sCurrAsset, sId);
}
}
};
return IllustrationPool;
}, /* bExport= */ true);