UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

376 lines (328 loc) 13 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // A static class to show a busy indicator sap.ui.define([ 'sap/ui/thirdparty/jquery', '../base/EventProvider', './Popup', './BusyIndicatorUtils', 'sap/ui/core/library', 'sap/ui/core/Lib', "sap/ui/performance/trace/FESR", "sap/ui/performance/trace/Interaction", "sap/base/Log", "sap/base/assert", "sap/base/util/now" ], function( jQuery, EventProvider, Popup, BusyIndicatorUtils, coreLib, Library, FESR, Interaction, Log, assert, now ) { "use strict"; //shortcut for sap.ui.core.BusyIndicatorSize var BusyIndicatorSize = coreLib.BusyIndicatorSize; /** * Provides methods to show or hide a waiting animation covering the whole * page and blocking user interaction. * @namespace * @version 1.111.5 * @public * @alias sap.ui.core.BusyIndicator */ var BusyIndicator = Object.assign( new EventProvider(), { oPopup: null, oDomRef: null, bOpenRequested: false, iDEFAULT_DELAY_MS: 1000, sDOM_ID: "sapUiBusyIndicator" }); BusyIndicator.M_EVENTS = { Open: "Open", Close: "Close" }; // This internal property keeps track if a show() call should be performed in case the core was not initialized yet. // When show() is called and this variable is undefined: a core init event-handler is attached. // When set to true || false internally, the core-init event-handler is not attached anymore. // This is to make sure the handler is only attached once. BusyIndicator._bShowIsDelayed = undefined; /** * The <code>Open</code> event is fired, after the <code>BusyIndicator</code> * has opened. * * @name sap.ui.core.BusyIndicator#Open * @event * @param {sap.ui.base.Event} oControlEvent is the event object * @param {sap.ui.base.EventProvider} oControlEvent.getSource is the instance * that fired the event * @param {object} oControlEvent.getParameters provides all additional parameters * that are delivered with the event * @param {jQuery} oControlEvent.getParameters.$Busy is the jQuery object * of the BusyIndicator * @public */ /** * The <code>Close</code> event is fired, <strong>before</strong> the * <code>BusyIndicator</code> has closed. * * @name sap.ui.core.BusyIndicator#Close * @event * @param {sap.ui.base.Event} oControlEvent is the event object * @param {sap.ui.base.EventProvider} oControlEvent.getSource is the instance * that fired the event * @param {object} oControlEvent.getParameters provides all additional parameters * that are delivered with the event * @param {jQuery} oControlEvent.getParameters.$Busy is the jQuery object * of the BusyIndicator * @public */ /** * Sets up the BusyIndicator HTML and the Popup instance. * * @private */ BusyIndicator._init = function() { var oRootDomRef = document.createElement("div"); oRootDomRef.id = this.sDOM_ID; var oBusyContainer = document.createElement("div"); this._oResBundle = Library.get("sap.ui.core").getResourceBundle(); var sTitle = this._oResBundle.getText("BUSY_TEXT"); delete this._oResBundle; oBusyContainer.className = "sapUiBusy"; oBusyContainer.setAttribute("tabindex", "0"); oBusyContainer.setAttribute("role", "progressbar"); oBusyContainer.setAttribute("alt", ""); oBusyContainer.setAttribute("title", sTitle); oRootDomRef.appendChild(oBusyContainer); var oBusyElement = BusyIndicatorUtils.getElement(BusyIndicatorSize.Large); oBusyElement.setAttribute("title", sTitle); oRootDomRef.appendChild(oBusyElement); this.oDomRef = oRootDomRef; this.oPopup = new Popup(oRootDomRef); this.oPopup.setModal(true, "sapUiBlyBusy"); this.oPopup.setShadow(false); this.oPopup.attachOpened(function(oEvent) { this._onOpen(oEvent); }, this); }; /** * This is the event listener that is called when the Popup of the BusyIndicator * has opened * * @param {jQuery.Event} oEvent is the event that is provided by the EventProvider * @private */ BusyIndicator._onOpen = function(oEvent) { // Grab the focus once opened var oDomRef = document.getElementById(BusyIndicator.sDOM_ID); oDomRef.style.height = "100%"; oDomRef.style.width = "100%"; // setting the BusyIndicator's DOM to visible is done by the Popup var oAnimation = oDomRef.querySelector(".sapUiLocalBusyIndicator"); oAnimation.className += " sapUiLocalBusyIndicatorFade"; if (oDomRef) { oDomRef.focus(); } // allow an event handler to do something with the indicator // and fire it after everything necessary happened this.fireOpen({ $Busy: this.oPopup._$() }); }; /** * Displays the <code>BusyIndicator</code> and starts blocking all user input. * This only happens after some delay, and if, after that delay, the <code>BusyIndicator.hide()</code> * has not yet been called in the meantime. * * There is a certain default value for the delay, which can be overridden. * * @public * @param {int} [iDelay=1000] The delay in milliseconds before opening the <code>BusyIndicator</code>; * It is not opened if <code>hide()</code> is called before the delay ends. * If no delay (or no valid delay) is given, a delay of 1000 milliseconds is used. */ BusyIndicator.show = function(iDelay) { Log.debug("sap.ui.core.BusyIndicator.show (delay: " + iDelay + ") at " + new Date().getTime()); assert(iDelay === undefined || (typeof iDelay == "number" && (iDelay % 1 == 0)), "iDelay must be empty or an integer"); // If body/Core are not available yet, give them some more time and open // later if still required if (!document.body || !sap.ui.getCore().isInitialized()) { // register core init only once, when bShowIsDelayed is not set yet if (BusyIndicator._bShowIsDelayed === undefined) { sap.ui.getCore().attachInit(function () { // ignore init event, in case hide() was called in between if (BusyIndicator._bShowIsDelayed) { BusyIndicator.show(iDelay); } }); } // everytime show() is called the call has to be delayed BusyIndicator._bShowIsDelayed = true; return; } if ((iDelay === undefined) || ((iDelay != 0) && (parseInt(iDelay) == 0)) || (parseInt(iDelay) < 0)) { iDelay = this.iDEFAULT_DELAY_MS; } if (FESR.getActive()) { this._fDelayedStartTime = now() + iDelay; } // Initialize/create the BusyIndicator if this has not been done yet. // This has to be done before calling '_showNowIfRequested' because within // '_init' the BusyIndicator attaches itself to the Popup's open event and // to keep the correct order of 'show -> _showNowIfRequested -> _onOpen' // the attaching has to happen earlier. // Otherwise if an application attaches itself to the open event, this listener // will be called before the BusyIndicator's open listener. if (!this.oDomRef) { this._init(); } this.bOpenRequested = true; if (iDelay === 0) { // avoid async call when there is no delay this._showNowIfRequested(); } else { setTimeout(this["_showNowIfRequested"].bind(this), iDelay); } }; /** * Immediately displays the BusyIndicator if the application has not called * hide() yet. * * @private */ BusyIndicator._showNowIfRequested = function() { Log.debug("sap.ui.core.BusyIndicator._showNowIfRequested (bOpenRequested: " + this.bOpenRequested + ") at " + new Date().getTime()); // Do not open if the request has been canceled in the meantime if (!this.bOpenRequested) { return; } // The current scroll position of window needs to be passed as offset to Popup // to keep the scroll position of the window. // Otherwise, the BusyIndicator may be placed partically off-screen initially // and the browser scrolls itself up to shift the whole BusyIndicator into the // viewport which result as that the window loses its scroll position var iOffsetX = (window.scrollX === undefined ? window.pageXOffset : window.scrollX); var iOffsetY = (window.scrollY === undefined ? window.pageYOffset : window.scrollY); var sOffset = iOffsetX + " " + iOffsetY; this.bOpenRequested = false; // opening request is handled // Actually open the popup this.oPopup.open(0, Popup.Dock.LeftTop, Popup.Dock.LeftTop, document, sOffset); }; /** * Removes the BusyIndicator from the screen. * * @public */ BusyIndicator.hide = function() { Log.debug("sap.ui.core.BusyIndicator.hide at " + new Date().getTime()); if (this._fDelayedStartTime) { // Implies fesr header active // The busy indicator shown duration d is calculated with: // d = "time busy indicator was hidden" - "time busy indicator was requested" - "busy indicator delay" var fBusyIndicatorShownDuration = now() - this._fDelayedStartTime; Interaction.addBusyDuration((fBusyIndicatorShownDuration > 0) ? fBusyIndicatorShownDuration : 0); delete this._fDelayedStartTime; } var bi = BusyIndicator; // Restore scope in case we are called with setTimeout or so... // When hide() is called, a potential delayed show-call has to be ignored. // Since there is no Core.detachInit(), this flag is used to reject an existing core-init handler if (BusyIndicator._bShowIsDelayed === true) { BusyIndicator._bShowIsDelayed = false; } bi.bOpenRequested = false; if (bi.oDomRef) { // only if the BusyIndicator was shown before! // setting the BusyIndicator's DOM to invisible is not // necessary here - it will be done by the Popup in 'oPopup.close(0)' var oAnimation = bi.oDomRef.querySelector(".sapUiLocalBusyIndicator"); jQuery(oAnimation).removeClass("sapUiLocalBusyIndicatorFade"); // allow an event handler to do something with the indicator this.fireClose({ $Busy: this.oPopup._$() }); bi.oPopup.close(0); } }; /* EVENT HANDLING */ /** * Registers a handler for the {@link #event:Open Open} event. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to <code>sap.ui.core.BusyIndicator</code>. * * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener] Context object to call the event handler with; defaults to * <code>sap.ui.core.BusyIndicator</code> * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ BusyIndicator.attachOpen = function(fnFunction, oListener) { this.attachEvent(BusyIndicator.M_EVENTS.Open, fnFunction, oListener); return this; }; /** * Unregisters a handler from the {@link #event:Open Open} event. * * @param {function} * fnFunction The callback function to unregister * @param {object} * [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ BusyIndicator.detachOpen = function(fnFunction, oListener) { this.detachEvent(BusyIndicator.M_EVENTS.Open, fnFunction, oListener); return this; }; /** * Registers a handler for the {@link #event:Close Close} event. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to <code>sap.ui.core.BusyIndicator</code>. * * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener] Context object to call the event handler with; defaults to * <code>sap.ui.core.BusyIndicator</code> * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ BusyIndicator.attachClose = function(fnFunction, oListener) { this.attachEvent(BusyIndicator.M_EVENTS.Close, fnFunction, oListener); return this; }; /** * Unregisters a handler from the {@link #event:Close Close} event. * * @param {function} * fnFunction The callback function to unregister * @param {object} * [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ BusyIndicator.detachClose = function(fnFunction, oListener) { this.detachEvent(BusyIndicator.M_EVENTS.Close, fnFunction, oListener); return this; }; BusyIndicator.fireOpen = function(mParameters) { this.fireEvent(BusyIndicator.M_EVENTS.Open, mParameters); }; BusyIndicator.fireClose = function(mParameters) { this.fireEvent(BusyIndicator.M_EVENTS.Close, mParameters); }; return BusyIndicator; }, /* bExport= */ true);