window.jscape = window.jscape || {};

/** @class */
jscape.ManagementService = Class.extend(
    /** @lends {jscape.ManagementService.prototype} */
{
    /** @const */
    DEFAULT_TIMEOUT: 30 * 60 * 1000,

    config: null,
    timeout: null,

    /** @constructor */
    initialize: function (config) {
        this.config = config;
        this.timeout = config && config.timeout || this.DEFAULT_TIMEOUT;
        this.csrf = this._csrfToken();
    },

    logout: function () {
        return this._submitForm("POST", "/logout");
    },

    _request: function (method, url, parse) {
        var req = new jscape.Request(method, url, this.timeout, parse);
        req.csrf = this.csrf;
        return $.isPlainObject(this.csrf) ? req.headers(this.csrf) : req;
    },

    _csrfToken: function () {
        var csrf = {};
        var token = $("meta[name=csrf-token]");
        if (token.length) {
            csrf[token.attr("http-equiv")] = token.attr("content");
        }
        return csrf;
    },

    _submitForm: function(method, url, data) {
        data = $.extend(data || {}, ("GET" !== method && this.csrf) || {});
        var html = ['<form action="' + encodeURI(url) + '" method="' + (method || "post") + '" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">']
                .concat($.map(data, function (value, key) { return '<input type="hidden" name="' + encodeURIComponent(key) + '" value="' + encodeURIComponent(value) + '" />'; }));
        html.push("</form>");
        $(html.join("")).appendTo(document.body).submit();
        return $.Deferred().resolve().promise();
    }
});

/** @class */
jscape.Request = Class.extend(
    /** @lends {jscape.Request.prototype} */
{
    url: null,
    settings: {},

    /** @constructor */
    initialize: function(method, url, timeout, parseResponse) {
        this.url = url;
        this.settings = {type: method, cache: false, timeout: timeout};
        this.parseResponse = parseResponse != undefined ? !!parseResponse : true;
    },

    uri: function(params) {
        params = $.extend({}, params);
        $.each(params, function(key, value) { params[key] = encodeURIComponent(value); });
        this.url = this.url.supplant(params);
        return this;
    },

    matrix: function(params) {
        function keyValuePair(key, value, appendTo) {
            if (value) { appendTo[appendTo.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value); }
        }
        var entries = [];
        $.each(params, function(key, value) {
            if ($.isArray(value)) {
                $.each(value, function(_, val) { keyValuePair(key, val, entries); });
            } else {
                keyValuePair(key, value, entries);
            }
        });
        if (this.url.indexOf(";") == -1 && entries.length) {
            this.url += ";";
        }
        this.url += entries.join(";");
        return this;
    },

    headers: function(headers, encode) {
        if (encode !== false) {
            $.each(headers, function (key, value) { headers[key] = encodeURIComponent(value); });
        }
        this.options({headers: $.extend(this.settings["headers"] || {}, headers)});
        return this;
    },

    options: function(options) {
        options && $.extend(true, this.settings, options);
        return this;
    },

    send: function (data) {
        if (!(window.FormData && data instanceof FormData)
            && "GET" !== this.settings.type && "iframe" !== this.settings.dataType
            && typeof data !== "string" && !$.isNumeric(data)) {
            data = JSON.stringify(data);
        }
        var settings = $.extend({
            contentType: data ? "application/json" : "",
            data: data || "",
            dataType: this.parseResponse ? "*" : "text",
            dataFilter: !this.parseResponse ? function(data) { return /^\s*<!doctype html>/i.test(data) ? "" : data; } : undefined,
            processData: "GET" === this.settings.type,
            statusCode: {401: function() { jscape.API.logout(); }}
            }, this.settings);
        var self = this;
        return $.ajax(this.url, settings)
                .fail(function (jqXhr, status, error) {
                    if (this.global && "abort" != status) { self._handleError.call(self, jqXhr, status, error); }
                });
    },

    sendForm: function(data) {
        data = $.map(data || [], function(value, key) { return key + "=" + ($.type(value) === "object" ? JSON.stringify(value) : encodeURIComponent(value))}).join("&");
        this.settings["contentType"] = "application/x-www-form-urlencoded";
        return this.send(data);
    },

    sendFormMultipart: function (data, fallback) {
        if (window.FormData === undefined) {
            return this.sendForm(data);
        }
        try {
            var fd = new FormData();
            for (var key in data) {
                if (data.hasOwnProperty(key)) {
                    var value = data[key];
                    if ("FileList" in window && value instanceof FileList) {
                        for (var i = 0; i < value.length; i++) {
                            fd.append(key, value[i]);
                        }
                    } else if (("File" in window && value instanceof File) || ("Blob" in window && value instanceof Blob)) {
                        fd.append(key, value)
                    } else {
                        var type = $.type(value);
                        if (type === "object") {
                            fd.append(key, this._toBlob(JSON.stringify(value), "application/json"));
                        } else if (type !== "undefined") {
                            fd.append(key, value);
                        }
                    }
                }
            }
            this.settings["contentType"] = false; // append boundary to multipart/form-data type
            return this.send(fd);
        } catch (e) {
            if (fallback === true) {
                return this.sendForm(data);
            } else {
                throw e;
            }
        }
    },

    sendDownload: function (data) {
        this.settings["dataType"] = "iframe";
        this.settings["loadCompleteToken"] = {"x-js-dload": "_" + Math.random().toString(36).substr(2)};
        if ($.isPlainObject(this.csrf)) {
            data = $.extend({}, data, this.csrf);
            this.csrf = null;
        }
        return this.send(data);
    },

    sendDownloadAndCheck: function(data, checkRequest, attempts) {
        attempts = attempts || 1;
        return this.options({
            beforeSend: function (jqXhr, settings) {
                function ping(request, delay) {
                    settings.pingId = setTimeout(function () {
                        settings.pingRequest = request.options({
                            global: false,
                            beforeSend: function () { --attempts; }
                        }).send(settings["loadCompleteToken"])
                                .done(function (data) {
                                          if (/complete|error/ig.test(data && data.state ? data.state : data)) {
                                              clearTimeout(settings.pingId);
                                          } else {
                                              ping(request, delay);
                                          }
                                      })
                                .fail(function () {
                                          if (attempts >= 0) {
                                              ping(request, delay);
                                          }
                                      });
                    }, delay || 0);
                }

                ping(checkRequest, 30000);
            },
            complete: function (jqXhr, status) {
                if ("abort" === status) {
                    clearTimeout(this.pingId);
                    this.pingRequest && this.pingRequest.abort();
                }
            }
        }).sendDownload(data);
    },

    sendSync: function(data) {
        if ($.isPlainObject(this.csrf)) {
            this.headers(this.csrf);
        }
        var self = this;
        var response = $.ajax(this.url, $.extend(this.settings, {
            async: false,
            data: data,
            statusCode: {401: function() { jscape.API.logout(); }},
            error: function(jqXhr, status, error) {
                if (this.global) { self._handleError.call(self, jqXhr, status, error); }
            }
        }));
        return response.responseJSON || response.responseText || null;
    },

    _handleError: function (jqXhr, status, error) {
        if (jqXhr.status == 0) {
            $(document).triggerHandler("broadcast", [window.R.string["APPLICATION_ERROR_CONNECTION_REFUSED"] || "There is no Internet Connection", false]);
            return;
        }
        var info;
        try {
            info = $.parseJSON(jqXhr.responseText);
        } catch (e) {
            info = {code: jqXhr.statusText, description: jqXhr.responseText || ""};
        }
        var title = (jqXhr.status ? jqXhr.status + " " : "") + jqXhr.statusText;
        var autoLogout = "NOT_AUTHENTICATED" == info.code;
        var message = info.code && window.R.string.contains(info.code)
                      ? window.R.string[info.code].supplant({0: info.description || ""})
                      : info.description || "Internal server error!";
        this._setupErrorDialog(title || window.R.string.APPLICATION_TITLE, message).show()
                .always(function () { if (autoLogout) { jscape.API.logout(); }});
    },

    _setupErrorDialog: function (title, message) {
        var dialog = new jscape.RestErrorDialog();
        dialog.setTitle(title);
        dialog.setMessage(message);
        return dialog;
    },

    _toBlob: function(data, type) {
        try {
            return new Blob([data], {type: type});
        } catch (e) {
            window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
            if (window.BlobBuilder) {
                var b = new BlobBuilder();
                b.append(data);
                return b.getBlob(type);
            } else {
                throw "Blob not supported.";
            }
        }
    }
});
(function($) {
    $.ajaxTransport("iframe", function(s) {
        if (!("GET" === s.type || "POST" === s.type) || !s.async) {
            throw "GET/POST methods supported only.";
        }
        var iframe;
        var form;
        var timerId;

        function clearCookie(name) {
            document.cookie = name + "=; expires=" + new Date(0).toUTCString() + "; path=/";
        }

        function createIframe() {
            var f = document.createElement("iframe");
            f.id = f.name = "_" + Math.random().toString(36).substr(2);
            f.src = "javascript:false";
            $(f).css({width: 0, height: 0, border: "none", visibility: "hidden"});
            return f;
        }

        function createForm(action, data) {
            var f = document.createElement("form");
            f.style.display = "none";
            f.action = action;
            f.method = "post";
            f.enctype = "application/x-www-form-urlencoded";
            if ($.type(data) == "object") {
                for (var key in data) {
                    if (data.hasOwnProperty(key) && typeof data[key] != "function") {
                        if ($.isArray(data[key])) {
                            var values = data[key];
                            for (var i = 0; i < values.length; i++) {
                                f.appendChild(createFormInput(key, values[i]));
                            }
                        } else {
                            f.appendChild(createFormInput(key, data[key]));
                        }
                    }
                }
            }
            return f;
        }

        function createFormInput(name, value) {
            var input = document.createElement("input");
            input.type = "hidden";
            input.name = name;
            input.value = value;
            return input;
        }

        function removeNode(node) {
            if (node && node.parentNode) {
                node.parentNode.removeChild(node);
            }
        }

        return {
            send: function(headers, callback) {
                iframe = createIframe();
                iframe.onload = function(_, abort) {
                    var success = document.cookie.indexOf($.param(s.loadCompleteToken)) != -1;
                    var doc = iframe && (iframe.contentDocument || iframe.contentWindow.document);
                    var content = doc && doc.body ? doc.body.textContent || doc.body.innerText : null;
                    var error = content && content.length != 0;

                    if (abort || success || error) {
                        var responses = error ? {text: content} : {};

                        clearTimeout(timerId);
                        timerId = undefined;
                        iframe.onload = null;
                        setTimeout(function() {
                            removeNode(form);
                            form = undefined;
                            removeNode(iframe);
                            iframe = undefined;
                        }, 50); // FF bug fix

                        if (success) {
                            for (var name in s.loadCompleteToken) {
                                clearCookie(name);
                            }
                            callback(200, "Success", responses);
                        } else if (error) {
                            callback(500, "Error", responses);
                        }
                    } else {
                        if (iframe) {
                            timerId = setTimeout(iframe.onload, 1000);
                        }
                    }
                };
                s.url += (s.url.indexOf("?") == -1 ? "?" : "&") + $.param(s.loadCompleteToken);
                if ("POST" === s.type) {
                    form = createForm(s.url, s.data);
                    form.target = iframe.name;
                    document.body.appendChild(form);
                    document.body.appendChild(iframe);
                    form.submit();
                } else {
                    iframe.src = s.url + ($.isPlainObject(s.data) ? "&" + $.param(s.data) : "");
                    document.body.appendChild(iframe);
                }
                timerId = setTimeout(iframe.onload, 100);
            },

            abort: function() {
                if (iframe) {
                    iframe.onload(0, true);
                }
            }
        };
    });
})(jQuery);

/** @class */
jscape.RestErrorDialog = Class.extend(
    /** @lends {jscape.RestErrorDialog.prototype} */
{
    deferred: null,

    /** @constructor */
    initialize: function() {
        this.dialog = $("<div class='dialog'><div class='messager-body'><div class='messager-icon messager-error'></div><div class='messager-message'></div></div></div>")
                .appendTo(document.body)
                .dialog({
                            width: 470, height: "auto",
                            modal: true, resizable: true, closable: true, closed: true,
                            buttons: [{text: "Ok", handler: $.proxy(this.hide, this)}],
                            onClose: $.proxy(function () { this.hide(); }, this)
                        }).show();

        this.message = $("div.messager-message", this.dialog).css({maxHeight: "170px", overflow: "auto"});
    },

    setTitle: function(value) {
        this.dialog.dialog("setTitle", value || "");
    },

    setMessage: function(value) {
        this.message.html((value || "").replace(/[\r\n]/g, "<br/>"));
    },

    show: function () {
        this.deferred = $.Deferred();
        this.dialog.dialog("center").dialog("open");
        return this.deferred.promise();
    },

    hide: function () {
        this.dialog.dialog("destroy");
        this.deferred.resolve();
        this.deferred = null;
    }
});

$(document).ajaxSend(function() { this.body.style.cursor = "wait"; }).ajaxComplete(function() { this.body.style.cursor = "default"; });