From b3991cb3b5a42398f0d95ab0cf84b58280fb396d Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 21 Sep 2011 18:50:13 +0200 Subject: [PATCH] Modified overlays MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The 'loading …'-overlay now stays for at least one second to provide a smoother behavior. Additionally implemented a global event handler 'WCF.CloseOverlayHandler' to provide a consistent handler to close overlays once they've lost focus (click event bubbled to body-tag). --- wcfsetup/install/files/acp/style/testing.css | 8 +- wcfsetup/install/files/js/WCF.js | 171 ++++++++++++++++++- 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/wcfsetup/install/files/acp/style/testing.css b/wcfsetup/install/files/acp/style/testing.css index 903f5c2d5d..749dbe7b24 100644 --- a/wcfsetup/install/files/acp/style/testing.css +++ b/wcfsetup/install/files/acp/style/testing.css @@ -2249,7 +2249,13 @@ div#ajaxExceptionStacktrace { padding: 7px 21px 7px; position: fixed; top: 0; - left: 43%; + right: 20px; + z-index: 1000; +} + +#actionProxyLoading img { + height: 24px; + width: 24px; } div.wcfDimensions { diff --git a/wcfsetup/install/files/js/WCF.js b/wcfsetup/install/files/js/WCF.js index 69baf8245f..cdeab028c4 100644 --- a/wcfsetup/install/files/js/WCF.js +++ b/wcfsetup/install/files/js/WCF.js @@ -198,6 +198,20 @@ $.fn.extend({ enable: function() { return this.removeAttr('disabled'); }, + + /** + * Returns the element's id. If none is set, a random unique + * ID will be assigned. + * + * @return string + */ + wcfIdentify: function() { + if (!this.attr('id')) { + this.attr('id', WCF.getRandomID()); + } + + return this.attr('id'); + }, /** * Applies a grow-effect by resizing element while moving the @@ -684,6 +698,9 @@ WCF.Clipboard = { $containers[$typeName] = $container; } + var $containerID = $container.wcfIdentify(); + WCF.CloseOverlayHandler.removeCallback($containerID); + $container.empty(); }); @@ -719,8 +736,23 @@ WCF.Clipboard = { // bind event $listItem.click($.proxy(this._executeAction, this)); } + + // block click event + $container.click(function(event) { + event.stopPropagation(); + }); + + // register event handler + var $containerID = $container.wcfIdentify(); + WCF.CloseOverlayHandler.addCallback($containerID, $.proxy(this._closeLists, this)); } }, + + _closeLists: function() { + $('.clipboardEditor ul ol').each(function(index, list) { + $(this).hide(); + }); + }, /** * Executes a clipboard editor item action. @@ -809,6 +841,30 @@ WCF.Action = {}; */ WCF.Action.Proxy = function(options) { this.init(options); }; WCF.Action.Proxy.prototype = { + /** + * count of active requests + * @var integer + */ + _activeRequests: 0, + + /** + * loading overlay + * @var jQuery + */ + _loadingOverlay: null, + + /** + * loading overlay state + * @var boolean + */ + _loadingOverlayVisible: false, + + /** + * timer for overlay activity + * @var integer + */ + _loadingOverlayVisibleTimer: 0, + /** * Initializes AJAXProxy. * @@ -860,9 +916,43 @@ WCF.Action.Proxy.prototype = { this.options.init(this); } - $('').appendTo($('body')); - this.loading = $('#actionProxyLoading'); - this.loading.wcfDropIn(); + this._activeRequests++; + this._showLoadingOverlay(); + }, + + /** + * Displays the loading overlay if not already visible due to an active request. + */ + _showLoadingOverlay: function() { + // create loading overlay on first run + if (this._loadingOverlay === null) { + this._loadingOverlay = $('
' + WCF.Language.get('wcf.global.loading') + '
').hide().appendTo($('body')); + } + + // fade in overlay + if (!this._loadingOverlayVisible) { + this._loadingOverlayVisible = true; + this._loadingOverlay.stop(true, true).fadeIn(200, $.proxy(function() { + new WCF.PeriodicalExecuter($.proxy(this._hideLoadingOverlay, this), 100); + }, this)); + } + }, + + /** + * Hides loading overlay if no requests are active and the timer reached at least 1 second. + * + * @param object pe + */ + _hideLoadingOverlay: function(pe) { + this._loadingOverlayVisibleTimer += 100; + + if (this._activeRequests == 0 && this._loadingOverlayVisibleTimer >= 1000) { + this._loadingOverlayVisible = false; + this._loadingOverlayVisibleTimer = 0; + pe.stop(); + + this._loadingOverlay.fadeOut(200); + } }, /** @@ -916,8 +1006,8 @@ WCF.Action.Proxy.prototype = { if ($.isFunction(this.options.after)) { this.options.after(); } - - this.loading.wcfDropOut('up'); + + this._activeRequests--; }, /** @@ -2091,6 +2181,77 @@ WCF.Effect.BalloonTooltip.prototype = { } }; +/** + * Handles clicks outside an overlay, hitting body-tag through bubbling. + * + * You should always remove callbacks before disposing the attached element, + * preventing errors from blocking the iteration. Furthermore you should + * always handle clicks on your overlay's container and return 'false' to + * prevent bubbling. + */ +WCF.CloseOverlayHandler = { + /** + * list of callbacks + * @var WCF.Dictionary + */ + _callbacks: new WCF.Dictionary(), + + /** + * indicates that overlay handler is listening to click events on body-tag + * @var boolean + */ + _isListening: false, + + /** + * Adds a new callback. + * + * @param string identifier + * @param object callback + */ + addCallback: function(identifier, callback) { + this._bindListener(); + + if (this._callbacks.isset(identifier)) { + cosole.debug("[WCF.CloseOverlayHandler] identifier '" + identifier + "' is already bound to a callback"); + return false; + } + + this._callbacks.add(identifier, callback); + }, + + /** + * Removes a callback from list. + * + * @param string identifier + */ + removeCallback: function(identifier) { + if (this._callbacks.isset(identifier)) { + this._callbacks.remove(identifier); + } + }, + + /** + * Binds click event handler. + */ + _bindListener: function() { + if (this._isListening) return; + + $('body').click($.proxy(this._executeCallbacks, this)); + + this._isListening = true; + }, + + /** + * Executes callbacks on click. + */ + _executeCallbacks: function() { + this._callbacks.each(function(pair) { + // execute callback + pair.value(); + }); + } +}; + /** * Basic implementation for WCF dialogs. */ -- 2.20.1