Updated WoltLab/WCF/Ajax implementation
authorAlexander Ebert <ebert@woltlab.com>
Sun, 24 May 2015 10:55:50 +0000 (12:55 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 24 May 2015 10:55:50 +0000 (12:55 +0200)
wcfsetup/install/files/js/WoltLab/WCF/Ajax.js
wcfsetup/install/files/js/WoltLab/WCF/Ajax/Request.js
wcfsetup/install/files/js/WoltLab/WCF/Ajax/Status.js
wcfsetup/install/files/js/WoltLab/WCF/BootstrapFrontend.js
wcfsetup/install/files/js/WoltLab/WCF/Controller/Notice/Dismiss.js
wcfsetup/install/files/js/WoltLab/WCF/Controller/Popover.js
wcfsetup/install/files/js/WoltLab/WCF/Controller/Style/Changer.js
wcfsetup/install/files/js/WoltLab/WCF/Core.js
wcfsetup/install/files/style/dialog.less

index 9b1a14c29b0975fefd2b48d68c463ae0a0d19827..d7337037dbe80f6af3732ce3486e87a64e71c68c 100644 (file)
@@ -24,10 +24,27 @@ define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectM
                 * @return      {AjaxRequest}
                 */
                api: function(callbackObject, data) {
+                       return this.apiProxy(callbackObject, data);
+               },
+               
+               /**
+                * Shorthand function to perform a request against the WCF-API with overrides
+                * for success and failure callbacks.
+                * 
+                * @param       {object}                callbackObject  callback object
+                * @param       {object<string, *>=}    data            request data
+                * @param       {function=}             success         success callback
+                * @param       {function=}             failure         failure callback
+                * @return      {AjaxRequest}
+                */
+               apiProxy: function(callbackObject, data, success, failure) {
                        var request = _requests.get(callbackObject);
                        if (request !== undefined) {
                                data = data || {};
                                
+                               if (typeof success === 'function') request.setOption('success', success);
+                               if (typeof failure === 'function') request.setOption('failure', failure);
+                               
                                request.setData(data || {});
                                request.sendRequest();
                                
@@ -49,6 +66,10 @@ define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectM
                        if (!options.url) options.url = 'index.php/AJAXProxy/?t=' + SECURITY_TOKEN;
                        
                        request = new AjaxRequest(options);
+                       
+                       if (typeof success === 'function') request.setOption('success', success);
+                       if (typeof failure === 'function') request.setOption('failure', failure);
+                       
                        request.sendRequest();
                        
                        _requests.set(callbackObject, request);
index d90c66088f3a99531265c3cb69d767d5b29524f6..aa5c0b37a0a08e054a8b3c8268c47cc22f2e7243 100644 (file)
@@ -46,9 +46,9 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                        }
                        
                        if (this._options.callbackObject !== null) {
-                               this._options.failure = (typeof this._options.callbackObject._ajaxFailure === 'function') ? this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject) : null;
-                               this._options.finalize = (typeof this._options.callbackObject._ajaxFinalize === 'function') ? this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject) : null;
-                               this._options.success = (typeof this._options.callbackObject._ajaxSuccess === 'function') ? this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject) : null;
+                               if (typeof this._options.callbackObject._ajaxFailure === 'function') this._options.failure = this._options.callbackObject._ajaxFailure.bind(this._options.callbackObject);
+                               if (typeof this._options.callbackObject._ajaxFinalize === 'function') this._options.finalize = this._options.callbackObject._ajaxFinalize.bind(this._options.callbackObject);
+                               if (typeof this._options.callbackObject._ajaxSuccess === 'function') this._options.success = this._options.callbackObject._ajaxSuccess.bind(this._options.callbackObject);
                        }
                        
                        if (_didInit === false) {
@@ -82,18 +82,20 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                        this._xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                        
                        var self = this;
+                       
+                       var options = Core.clone(this._options);
                        this._xhr.onload = function() {
                                if (this.readyState === XMLHttpRequest.DONE) {
                                        if (this.status >= 200 && this.status < 300 || this.status === 304) {
-                                               self._success(this);
+                                               self._success(this, options);
                                        }
                                        else {
-                                               self._failure(this);
+                                               self._failure(this, options);
                                        }
                                }
                        };
                        this._xhr.onerror = function() {
-                               self._failure(this);
+                               self._failure(this, options);
                        };
                        
                        if (this._options.type === 'POST') {
@@ -156,14 +158,15 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                /**
                 * Handles a successful request.
                 * 
-                * @param       {XMLHttpRequest}        xhr     request object
+                * @param       {XMLHttpRequest}        xhr             request object
+                * @param       {object<string, *>}     options         request options
                 */
-               _success: function(xhr) {
-                       if (!this._options.silent) {
+               _success: function(xhr, options) {
+                       if (!options.silent) {
                                AjaxStatus.hide();
                        }
                        
-                       if (typeof this._options.success === 'function') {
+                       if (typeof options.success === 'function') {
                                var data = null;
                                if (xhr.getResponseHeader('Content-Type') === 'application/json') {
                                        try {
@@ -182,24 +185,25 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                                        }
                                }
                                
-                               this._options.success(data, xhr.responseText, xhr);
+                               options.success(data, xhr.responseText, xhr);
                        }
                        
-                       this._finalize();
+                       this._finalize(options);
                },
                
                /**
                 * Handles failed requests, this can be both a successful request with
                 * a non-success status code or an entirely failed request.
                 * 
-                * @param       {XMLHttpRequest}        xhr     request object
+                * @param       {XMLHttpRequest}        xhr             request object
+                * @param       {object<string, *>}     options         request options
                 */
-               _failure: function (xhr) {
+               _failure: function (xhr, options) {
                        if (_ignoreAllErrors) {
                                return;
                        }
                        
-                       if (!this._options.silent) {
+                       if (!options.silent) {
                                AjaxStatus.hide();
                        }
                        
@@ -210,11 +214,11 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                        catch (e) {}
                        
                        var showError = true;
-                       if (typeof this._options.failure === 'function') {
-                               showError = this._options.failure(data, xhr);
+                       if (typeof options.failure === 'function') {
+                               showError = options.failure(data, xhr);
                        }
                        
-                       if (this._options.ignoreError !== true && showError !== false) {
+                       if (options.ignoreError !== true && showError !== false) {
                                var details = '';
                                var message = '';
                                
@@ -239,15 +243,17 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                                });
                        }
                        
-                       this._finalize();
+                       this._finalize(options);
                },
                
                /**
                 * Finalizes a request.
+                * 
+                * @param       {object<string, *>}     options         request options
                 */
-               _finalize: function() {
-                       if (typeof this._options.finalize === 'function') {
-                               this._options.finalize(this._xhr);
+               _finalize: function(options) {
+                       if (typeof options.finalize === 'function') {
+                               options.finalize(this._xhr);
                        }
                        
                        this._previousXhr = null;
index 7eedcc80ac6ccb63b1b800483f3dd5834d1a9e6b..2827404d73d0c3d932ceac211209c54cdc119dad 100644 (file)
@@ -68,8 +68,12 @@ define(['Language'], function(Language) {
                                        window.clearTimeout(_timeoutShow);
                                }
                                
+                               console.debug("Removing 'active'!");
+                               window.dt = _overlay;
                                _overlay.classList.remove('active');
                        }
+                       
+                       console.debug(_activeRequests);
                }
        };
        
index ad046fd6631e76170474c9a6cbeceaa72554c699..20dfb362878b8891bf038f99f38f2d3833195e30 100644 (file)
@@ -40,19 +40,15 @@ define(['Ajax', 'WoltLab/WCF/Bootstrap', 'WoltLab/WCF/Controller/Sitemap', 'Wolt
                                className: 'userLink',
                                identifier: 'com.woltlab.wcf.user',
                                loadCallback: function(objectId, popover) {
-                                       Ajax.api({
-                                               data: {
-                                                       actionName: 'getUserProfile',
-                                                       className: 'wcf\\data\\user\\UserProfileAction',
-                                                       objectIDs: [ objectId ]
-                                               },
-                                               success: (function(data) {
-                                                       popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
-                                               }).bind(this),
-                                               failure: (function(data) {
-                                                       popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
-                                               }).bind(this)
-                                       });
+                                       var callback = function(data) {
+                                               popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
+                                       };
+                                       
+                                       popover.ajaxApi({
+                                               actionName: 'getUserProfile',
+                                               className: 'wcf\\data\\user\\UserProfileAction',
+                                               objectIDs: [ objectId ]
+                                       }, callback, callback);
                                }
                        });
                }
index 757db6e2467060b79fc0a6cbc7dc3f9c2e646974..2a0d58645a3b137b53f47ad6a6dbfd6ab6c38f78 100644 (file)
@@ -34,7 +34,7 @@ define(['Ajax'], function(Ajax) {
                _click: function(event) {
                        var button = event.currentTarget;
                        
-                       Ajax.api({
+                       Ajax.apiOnce({
                                data: {
                                        actionName: 'dismiss',
                                        className: 'wcf\\data\\notice\\NoticeAction',
index a54801875f993d3fb2f8b52eda0bd74da6dfa2b5..fec8f529e962b8f2d544d5493cd5a1718c026a54 100644 (file)
@@ -6,7 +6,7 @@
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module     WoltLab/WCF/Controller/Popover
  */
-define(['Dictionary', 'Environment', 'DOM/ChangeListener', 'DOM/Util', 'UI/Alignment'], function(Dictionary, Environment, DOMChangeListener, DOMUtil, UIAlignment) {
+define(['Ajax', 'Dictionary', 'Environment', 'DOM/ChangeListener', 'DOM/Util', 'UI/Alignment'], function(Ajax, Dictionary, Environment, DOMChangeListener, DOMUtil, UIAlignment) {
        "use strict";
        
        var _activeId = null;
@@ -399,6 +399,26 @@ define(['Dictionary', 'Environment', 'DOM/ChangeListener', 'DOM/Util', 'UI/Align
                                vertical: 'top',
                                verticalOffset: 3
                        });
+               },
+               
+               _ajaxSetup: function() {
+                       // does nothing
+                       return {};
+               },
+               
+               /**
+                * Sends an AJAX requests to the server, simple wrapper to reuse the request object.
+                * 
+                * @param       {object<string, *>}     data            request data
+                * @param       {function<object>}      success         success callback
+                * @param       {function<object>=}     failure         error callback
+                */
+               ajaxApi: function(data, success, failure) {
+                       if (typeof success !== 'function') {
+                               throw new TypeError("Expected a valid callback for parameter 'success'.");
+                       }
+                       
+                       Ajax.apiProxy(this, data, success, failure);
                }
        };
        
index d966ea3ef3a6e9b6d9fd08c20aa66ccdeaf12cec..9ea7a3ce823a6eb4363db6ba053b419569eaac79 100644 (file)
@@ -44,7 +44,7 @@ define(['Ajax', 'Language', 'UI/Dialog'], function(Ajax, Language, UIDialog) {
                        event.preventDefault();
                        
                        if (UIDialog.getDialog('styleChanger') === undefined) {
-                               Ajax.api({
+                               Ajax.apiOnce({
                                        data: {
                                                actionName: 'getStyleChooser',
                                                className: 'wcf\\data\\style\\StyleAction'
@@ -78,7 +78,7 @@ define(['Ajax', 'Language', 'UI/Dialog'], function(Ajax, Language, UIDialog) {
                _click: function(event) {
                        event.preventDefault();
                        
-                       Ajax.api({
+                       Ajax.apiOnce({
                                data: {
                                        actionName: 'changeStyle',
                                        className: 'wcf\\data\\style\\StyleAction',
index 249c7e97648c99360fc81cb56161015f1b7282a8..811a7189e8a05810263b9eccb6216af5d7a9417b 100644 (file)
@@ -9,11 +9,59 @@
 define([], function() {
        "use strict";
        
+       var _clone = function(variable) {
+               if (typeof variable === 'object' && variable !== null) {
+                       return _cloneObject(variable);
+               }
+               
+               return variable;
+       };
+       
+       var _cloneArray = function(oldArray) {
+               var i = oldArray.length;
+               var newArray = new Array(i);
+               
+               while (i--) {
+                       newArray[i] = oldArray[i];
+               }
+               
+               return newArray;
+       };
+       
+       var _cloneObject = function(obj) {
+               if (!obj) {
+                       return null;
+               }
+               
+               if (Array.isArray(obj)) {
+                       return _cloneArray(obj);
+               }
+               
+               var newObj = {};
+               for (var key in obj) {
+                       if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
+                               newObj[key] = _clone(obj[key]);
+                       }
+               }
+               
+               return newObj;
+       };
+       
        /**
         * @constructor
         */
        function Core() {};
        Core.prototype = {
+               /**
+                * Deep clones an object.
+                * 
+                * @param       {object}        obj     source object
+                * @return      {object}        cloned object
+                */
+               clone: function(obj) {
+                       return _clone(obj);
+               },
+               
                /**
                 * Converts WCF 2.0-style URLs into the default URL layout.
                 * 
index d3ef29033759e418729682ffb39865450555057d..2d7b98d94b73be0a9be06fa030cccd8013f08b06 100644 (file)
        box-sizing: border-box;
        color: #fff;
        left: 50%;
+       opacity: 0;
        padding: 7px;
        position: fixed;
        text-align: center;
        top: 200px;
+       visibility: hidden;
        z-index: 401;
        
        transform: translateX(-50%);
        .boxShadow(0, 1px, rgba(0, 0, 0, .5), 7px);
        .linearGradient(rgba(0, 0, 0, .5), rgba(0, 0, 0, 0), rgba(0, 0, 0, .7));
        
+       transition: visibility 0s linear .1s, opacity .1s linear;
+       
+       &.active {
+               opacity: 1;
+               visibility: visible;
+               
+               transition-delay: 0s;
+       }
+       
        > .icon {
                color: #fff;
        }