jscape.management = jscape.management || {};

/** @class */
jscape.RestErrorDialog = Class.extend(
    /** @lends {jscape.RestErrorDialog.prototype} */
{
    TEMPLATE: "<div class='dialog'><div class='messager-body'><div class='messager-icon messager-error'></div><div class='messager-message'></div></div></div>",

    deferred: null,

    /** @constructor */
    initialize: function() {
        this.dialog = $(this.TEMPLATE).appendTo(document.body);
        this.dialog.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;
    }
});


/** @class */
jscape.management.ManagementService = Class.extend(
    /** @lends {jscape.management.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");
    },

    editDomain: function(domainName) {
        return this._submitForm("GET", "/domains/{domain}".supplant({domain: domainName}));
    },

    importLicense: function (file) {
        var request = new jscape.Request("POST", "/settings/license", this.timeout, false);
        request.csrf = this.csrf;
        return request.sendFormMultipart({data: file});
    },

    getServerParameters: function() {
        return this._request("GET", "/server/parameters").send()
                .then(function(data) { return jscape.ServerParameters.fromJSON(data); });
    },

    getServerAddresses: function() {
        return this._request("GET", "/server/addresses").send()
                .then(function(data) { return data || []; });
    },

    getServerStatistics: function() {
        return this._request("GET", "/server/statistics").options({global: false}).send()
                .then(function(data) { return jscape.ServerStatistics.fromJSON(data); });
    },

    getServerMemoryStatistics: function() {
        return this._request("GET", "/server/statistics/memory").options({global: false}).send();
    },

    getServerThreadsStatistics: function() {
        return this._request("GET", "/server/statistics/threads").options({global: false}).send();
    },

    getServerConnectionsStatistics: function() {
        return this._request("GET", "/server/statistics/connections").options({global: false}).send();
    },

    getServerUploadsStatistics: function() {
        return this._request("GET", "/server/statistics/uploads").options({global: false}).send();
    },

    getServerDownloadsStatistics: function() {
        return this._request("GET", "/server/statistics/downloads").options({global: false}).send();
    },

    getManagementServiceConfiguration: function() {
        return this._request("GET", "/manager-service/config").send()
                .then(function(data) { return jscape.ServerManagementServiceConfiguration.fromJSON(data); });
    },

    setManagementServiceConfiguration: function(config) {
        return this._request("PUT", "/manager-service/config").send(config);
    },

    getManagementLogServiceConfiguration: function() {
        return this._request("GET", "/manager-service/logs/config").send()
                .then(function(data) { return jscape.ManagementLogServiceConfiguration.fromJSON(data); });
    },

    setManagementLogServiceConfiguration: function(config) {
        return this._request("PUT", "/manager-service/logs/config").send(config);
    },

    listManagementLogRecords: function(startIndex, count, query) {
        return this._request("GET", "/manager-service/logs/records").matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.ManagementLogRecords.fromJSON); });
    },

    purgeManagementLogRecords: function() {
        return this._request("POST", "/manager-service/logs/purge").send();
    },

    exportManagementLogRecords: function(filename) {
        return this._request("GET", "/manager-service/logs/export?filename={file}").uri({file: filename}).sendDownload();
    },

    getManagementAccessServiceConfiguration: function() {
        return this._request("GET", "/manager-service/access").send()
                .then(function(data) { return jscape.ManagementAccessServiceConfiguration.fromJSON(data); });
    },

    setManagementAccessServiceConfiguration: function(config) {
        return this._request("PUT", "/manager-service/access").send(config);
    },

    getAdministrators: function() {
        return this._request("GET", "/manager-service/administrators").send()
                .then(function(data) { return $.map(data || [], jscape.Administrator.fromJSON); });
    },

    getAdministrator: function(name) {
        return this._request("GET", "/manager-service/administrators/{name}").uri({name: name}).send()
                .then(function (data) { return jscape.Administrator.fromJSON(data); });
    },

    addAdministrator: function(admin) {
        return this._request("POST", "/manager-service/administrators").send(admin)
                .then(function (data) { return jscape.Administrator.fromJSON(data); });
    },

    setAdministrator: function(name, admin) {
        return this._request("PUT", "/manager-service/administrators/{name}").uri({name: name}).send(admin);
    },

    changeAdministratorPassword: function(name, password) {
        return this._request("POST", "/manager-service/administrators/{name}/changepassword").uri({name: name}).send(password);
    },

    deleteAdministrator: function(name) {
        return this._request("DELETE", "/manager-service/administrators/{name}").uri({name: name}).send();
    },

    getManagementAuthenticationServiceConfiguration: function() {
        return this._request("GET", "/manager-service/authentication").send()
                .then(function(data) { return jscape.AuthenticationService.fromJSON(data); });
    },

    setManagementAuthenticationServiceConfiguration: function(config) {
        return this._request("PUT", "/manager-service/authentication").send(config);
    },

    testManagementAuthenticationService: function(descriptor, credentials) {
        return this._request("POST", "/manager-service/authentication/test").sendFormMultipart($.extend({descriptor: descriptor}, credentials), true);
    },

    getManagementRoles: function() {
        return this._request("GET", "/manager-service/acl/roles").send();
    },

    getManagementRole: function(roleName) {
        return this._request("GET", "/manager-service/acl/roles/{role}").uri({role: roleName}).send()
                .then(function(data) { return jscape.ManagementRole.fromJSON(data); });
    },

    addManagementRole: function(role) {
        return this._request("POST", "/manager-service/acl/roles").send(role)
                .then(function(data) { return jscape.ManagementRole.fromJSON(data); });
    },

    setManagementRole: function(roleName, role) {
        return this._request("PUT", "/manager-service/acl/roles/{role}").uri({role: roleName}).send(role);
    },

    deleteManagementRole: function(roleName) {
        return this._request("DELETE", "/manager-service/acl/roles/{role}").uri({role: roleName}).send();
    },

    getAclTags: function() {
        return this._request("GET", "/manager-service/acl/tags").send()
                .then(function(tags) { return tags || []; });
    },

    storeAclTag: function(tag) {
        return this._request("PUT", "/manager-service/acl/tags/{tag}").uri({tag: tag}).send();
    },

    deleteAclTag: function(tag) {
        return this._request("DELETE", "/manager-service/acl/tags/{tag}").uri({tag: tag}).send();
    },

    getServerDatabaseConfiguration: function() {
        return this._request("GET", "/datastore/config").send()
                .then(function(data) { return jscape.ServerDatabaseServiceConfiguration.fromJSON(data); });
    },

    setServerDatabaseConfiguration: function(config, copyData) {
        return this._request("PUT", "/datastore/config").headers({"x-js-copy-data": !!copyData}).send(config)
                .then(function(data) { return jscape.ServerDatabaseServiceConfiguration.fromJSON(data); });
    },

    testServerDatabase: function(config) {
        return this._request("POST", "/datastore/test").send(config);
    },

    createServerDatabase: function(config) {
        return this._request("POST", "/datastore/create-db").send(config);
    },

    prefetchServerDatabaseData: function(config) {
        return this._request("POST", "/datastore/prefetch-data").send(config)
                .then(function(data) { return jscape.ManagementWebServerConfiguration.fromJSON(data)});
    },

    getWebServiceConfiguration: function () {
        return this._request("GET", "/web-service/config").send()
                .then(function (data) { return jscape.WebConfiguration.fromJSON(data); });
    },

    testWebServiceConfiguration: function () {
        return this._request("GET", "/web-service/config").send({global: false})
                .then(function (data) { return jscape.WebConfiguration.fromJSON(data); });
    },

    setWebServiceConfiguration: function (config) {
        return this._request("PUT", "/web-service/config").send(config)
                .then(function(data) { return jscape.WebConfiguration.fromJSON(data); });
    },

    getWebServiceResources: function () {
        return this._request("GET", "/web-service/resources").send()
                .then(function (data) { return $.Deferred().resolveWith(this, data ? [$.map(data.resources || [], jscape.WebResources.fromJSON), data.logo] : []).promise(); });
    },

    setWebServiceResources: function (resources, logoData) {
        return this._request("PUT", "/web-service/resources").send({resources: resources, logo: logoData});
    },

    getAs2ServiceConfiguration: function () {
        return this._request("GET", "/web-service/as2/config").send()
                .then(function (data) { return jscape.AS2Configuration.fromJSON(data); });
    },

    setAs2ServiceConfiguration: function (config) {
        return this._request("PUT", "/web-service/as2/config").send(config)
                .then(function(data) { return jscape.AS2Configuration.fromJSON(data); });
    },

    getDbDrivers: function () {
        return this._request("GET", "/db-drivers").send()
                .then(function(data) { return data || []; });
    },

    setDbDrivers: function (drivers) {
        return this._request("PUT", "/db-drivers").send(drivers);
    },

    getManagementWebServerConfiguration: function () {
        return this._request("GET", "/web-management/config").send()
                .then(function (data) { return jscape.ManagementWebServerConfiguration.fromJSON(data); });
    },

    setManagementWebServerConfiguration: function (config) {
        return this._request("PUT", "/web-management/config").send(config);
    },

    testManagementWebServerConfiguration: function() {
        return this._request("GET", "/web-management/config").options({global: false}).send()
                .then(function (data) { return jscape.ManagementWebServerConfiguration.fromJSON(data); })
    },

    getRegistrationServiceConfiguration: function () {
        return this._request("GET", "/registration-service/config").send()
                .then(function (data) { return jscape.SelfRegistrationServiceDescriptor.fromJSON(data); });
    },

    setRegistrationServiceConfiguration: function (config) {
        return this._request("PUT", "/registration-service/config").send(config);
    },

    getDocumentViewerConfiguration: function () {
        return this._request("GET", "/document-viewer/config").send()
                .then(function (data) { return jscape.DocumentViewerConfiguration.fromJSON(data); });
    },

    setDocumentViewerConfiguration: function (config) {
        return this._request("PUT", "/document-viewer/config").send(config)
                .then(function (data) { return jscape.DocumentViewerConfiguration.fromJSON(data); });
    },

    getDocumentViewerAllowed: function() {
        return this._request("GET", "/document-viewer/allowed").send();
    },

    getEmailServiceConfiguration: function () {
        return this._request("GET", "/email-service/config").send()
                .then(function(data) { return jscape.EmailServiceConfiguration.fromJSON(data); });
    },

    setEmailServiceConfiguration: function (config) {
        return this._request("PUT", "/email-service/config").send(config)
                .then(function(data) { return jscape.EmailServiceConfiguration.fromJSON(data); });
    },

    testEmailServer: function (config, recipient) {
        return this._request("POST", "/email-service/test").headers({"x-js-recipient": recipient}).send(config);
    },

    getFailoverServiceConfiguration: function () {
        return this._request("GET", "/failover-service/config").send()
                .then(function (data) { return jscape.FailoverServiceConfiguration.fromJSON(data); });
    },

    setFailoverServiceConfiguration: function (config) {
        return this._request("PUT", "/failover-service/config").send(config)
                .then(function (data) { return jscape.FailoverServiceConfiguration.fromJSON(data); });
    },

    syncFailoverServerState: function(config) {
        return this._request("POST", "/failover-service/sync").send(config);
    },

    failoverSyncComplete: function() {
        return this._request("GET", "/failover-service/sync").send();
    },

    getSearchingServiceConfiguration: function () {
        return this._request("GET", "/searching-service/config").send()
                .then(function (data) { return jscape.SearchingServiceConfiguration.fromJSON(data); });
    },

    setSearchingServiceConfiguration: function (config) {
        return this._request("PUT", "/searching-service/config").send(config)
                .then(function (data) { return jscape.SearchingServiceConfiguration.fromJSON(data); });
    },

    getSearchFileIndexingStats: function (config) {
        return this._request("GET", "/searching-service/index").send(config);
    },

    startSearchFileIndexing: function (config) {
        return this._request("POST", "/searching-service/index").send(config);
    },

    getJmxServiceConfiguration: function () {
        return this._request("GET", "/jmx-service/config").send()
                .then(function (data) { return jscape.JmxServiceConfiguration.fromJSON(data); });
    },

    setJmxServiceConfiguration: function (config) {
        return this._request("PUT", "/jmx-service/config").send(config)
                .then(function (data) { return jscape.JmxServiceConfiguration.fromJSON(data); });
    },

    getDomainNames: function() {
        return this._request("GET", "/domains").send();
    },

    getDomain: function(domainName) {
        return this._request("GET", "/domains/{domain}").uri({domain: domainName}).send()
                .then(function(data) { return jscape.DomainDescriptor.fromJSON(data); });
    },

    storeDomain: function(descriptor) {
        return this._request("PUT", "/domains").send(descriptor);
    },

    deleteDomain: function(domainName) {
        return this._request("DELETE", "/domains/{domain}").uri({domain: domainName}).send();
    },

    getDomainTags: function(domainName) {
        return this._request("GET", "/domains/{domain}/tags").uri({domain: domainName}).send();
    },

    getDomainDescription: function(domainName) {
        return this._request("GET", "/domains/{domain}/desc", false).uri({domain: domainName}).send();
    },

    setDomainDescription: function(domainName, description) {
        return this._request("POST", "/domains/{domain}/desc").uri({domain: domainName}).send(description);
    },

    setDomainAutoStart: function (domainName, autoStart) {
        return this._request("POST", "/domains/{domain}/auto-start").uri({domain: domainName}).send(!!autoStart);
    },

    startDomain: function(domainName) {
        return this._request("POST", "/domains/{domain}/start").uri({domain: domainName}).send();
    },

    stopDomain: function(domainName) {
        return this._request("POST", "/domains/{domain}/stop").uri({domain: domainName}).send();
    },

    pauseDomain: function(domainName) {
        return this._request("POST", "/domains/{domain}/pause").uri({domain: domainName}).send();
    },

    resumeDomain: function(domainName) {
        return this._request("POST", "/domains/{domain}/resume").uri({domain: domainName}).send();
    },

    listFileSystem: function(path) {
        return this._request("GET", "/files/vfs?path={path}").uri({path: path || ""}).send();
    },

    createFileSystemDir: function(path, name) {
        return this._request("PUT", "/files/vfs?path={path}").uri({path: path}).send(name);
    },

    listMonitorsDir: function(path) {
        return this._request("GET", "/files/dir-monitors?path={path}").uri({path: path || ""}).send();
    },

    createMonitorsDir: function(path, name) {
        return this._request("PUT", "/files/dir-monitors?path={path}").uri({path: path}).send(name);
    },

    listUsersRestrictPath: function(path) {
        return this._request("GET", "/files/users?path={path}").uri({path: path || ""}).send();
    },

    createUsersRestrictPath: function(path, name) {
        return this._request("PUT", "/files/users?path={path}").uri({path: path}).send(name);
    },

    listKeystorePath: function(path) {
        return this._request("GET", "/files/keystore?path={path}").uri({path: path || ""}).send();
    },

    createKeystoreDir: function(path, name) {
        return this._request("PUT", "/files/keystore?path={path}").uri({path: path}).send(name);
    },

    listDebugDir: function(path) {
        return this._request("GET", "/files/debug?path={path}").uri({path: path || ""}).send();
    },

    createDebugDir: function(path, name) {
        return this._request("PUT", "/files/debug?path={path}").uri({path: path}).send(name);
    },

    listLogDir: function(path) {
        return this._request("GET", "/files/log?path={path}").uri({path: path || ""}).send();
    },

    createLogDir: function(path, name) {
        return this._request("PUT", "/files/log?path={path}").uri({path: path}).send(name);
    },

    getDomainStatistics: function(domainName) {
        return this._request("GET", "/domains/{domain}/statistics").uri({domain: domainName}).send()
                .then(function(data) { return jscape.DomainState.fromJSON(data); });
    },

    getDomainConnections: function(domainName) {
        return this._request("GET", "/domains/{domain}/connections").uri({domain: domainName}).options({global: false}).send()
    },

    closeConnection: function(domainName, stats, scope) {
        return this._request("POST", "/domains/{domain}/connections/close").uri({domain: domainName, scope: scope}).matrix({scope: scope}).send(stats);
    },

    getLogs: function(domainName, maxRecordCount) {
        return this._request("GET", "/domains/{domain}/logging/logs", false).uri({domain: domainName}).options({ifModified: true}).send({count: maxRecordCount});
    },

    getLogSearches: function(domainName) {
        return this._request("GET", "/domains/{domain}/logging/searches").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.LogSearch.fromJSON); });
    },

    searchLog: function(domainName, query) {
        return this._request("PUT", "/domains/{domain}/logging/searches").uri({domain: domainName}).send(query);
    },

    getLogSearchResult: function(domainName, searchName, maxRecords) {
        return this._request("GET", "/domains/{domain}/logging/searches/{search}", false).uri({domain: domainName, search: searchName}).send(maxRecords);
    },

    exportLogSearchResult: function(domainName, searchName, filename) {
        return this._request("GET", "/domains/{domain}/logging/searches/{search}/export").uri({domain: domainName, search: searchName}).sendDownload({filename: filename});
    },

    deleteLogSearch: function(domainName, searchName) {
        return this._request("DELETE", "/domains/{domain}/logging/searches/{search}").uri({domain: domainName, search: searchName}).send();
    },

    startLogSearch: function(domainName, searchName) {
        return this._request("POST", "/domains/{domain}/logging/searches/{search}/start").uri({domain: domainName, search: searchName}).send();
    },

    getLogService: function(domainName) {
        return this._request("GET", "/domains/{domain}/logging").uri({domain: domainName}).send()
                .then(function(data) { return jscape.LogService.fromJSON(data); });
    },

    setLogService: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/logging").uri({domain: domainName}).send(descriptor);
    },

    testLogService: function (domainName, descriptor) {
        return typeof domainName === "string"
               ? this._request("POST", "/domains/{domain}/logging/test").uri({domain: domainName}).send(descriptor)
               : this._request("POST", "/services/log/test").send(descriptor);
    },

    getSyslogService: function(domainName) {
        return this._request("GET", "/domains/{domain}/logging/syslog").uri({domain: domainName}).send()
                .then(function(data) { return jscape.SyslogService.fromJSON(data); });
    },

    setSyslogService: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/logging/syslog").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.SyslogService.fromJSON(data); });
    },

    getLogConfiguration: function(domainName) {
        return this._request("GET", "/domains/{domain}/logging/config").uri({domain: domainName}).send()
                .then(function(data) { return jscape.LogConfiguration.fromJSON(data); });
    },

    setLogConfiguration: function(domainName, config) {
        return this._request("PUT", "/domains/{domain}/logging/config").uri({domain: domainName}).send(config)
                .then(function(data) { return jscape.LogConfiguration.fromJSON(data); });
    },

    listReports: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/reports").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.Report.fromJSON); });
    },

    getReport: function(domainName, reportName) {
        return this._request("GET", "/domains/{domain}/reports/{report}").uri({domain: domainName, report: reportName}).send()
                .then(function(data) { return jscape.Report.fromJSON(data); });
    },

    addReport: function(domainName, report) {
        return this._request("POST", "/domains/{domain}/reports").uri({domain: domainName}).send(report)
                .then(function(data) { return jscape.Report.fromJSON(data); });
    },

    setReport: function(domainName, reportName, report) {
        return this._request("PUT", "/domains/{domain}/reports/{report}").uri({domain: domainName, report: reportName}).send(report);
    },

    deleteReport: function(domainName, reportName) {
        return this._request("DELETE", "/domains/{domain}/reports/{report}").uri({domain: domainName, report: reportName}).send();
    },

    reRunReport: function(domainName, reportName) {
        return this._request("POST", "/domains/{domain}/reports/{report}/rerun").uri({domain: domainName, report: reportName}).send()
                .then(function(data) { return jscape.Report.fromJSON(data); });
    },

    listAs2Messages: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/as2").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.As2Message.fromJSON); });
    },

    getAs2MessageRecordView: function(domainName, messageId) {
        return this._request("GET", "/domains/{domain}/as2/{message}").uri({domain: domainName, message: messageId}).send();
    },

    getAs2MessageRecordRawView: function(domainName, messageId) {
        return this._request("GET", "/domains/{domain}/as2/{message}?out=raw", false).uri({domain: domainName, message: messageId}).send();
    },

    resendAs2Message: function(domainName, messageId) {
        return this._request("POST", "/domains/{domain}/as2/{message}/resend").uri({domain: domainName, message: messageId}).send();
    },

    deleteAs2Message: function(domainName, messageId) {
        return this._request("DELETE", "/domains/{domain}/as2/{message}").uri({domain: domainName, message: messageId}).send();
    },

    listOftpMessages: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/oftp").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.OftpMessage.fromJSON); });
    },

    getOftpMessageRecordView: function(domainName, messageId) {
        return this._request("GET", "/domains/{domain}/oftp/{message}").uri({domain: domainName, message: messageId}).send();
    },

    sendOftpMessageReceipt: function(domainName, messageId, data) {
        return this._request("POST", "/domains/{domain}/oftp/{message}/send").uri({domain: domainName, message: messageId}).sendForm(data);
    },

    resendOftpMessage: function(domainName, messageId) {
        return this._request("POST", "/domains/{domain}/oftp/{message}/resend").uri({domain: domainName, message: messageId}).send();
    },

    deleteOftpMessage: function(domainName, messageId) {
        return this._request("DELETE", "/domains/{domain}/oftp/{message}").uri({domain: domainName, message: messageId}).send();
    },

    getIpAccessRules: function(domainName) {
        return this._request("GET", "/domains/{domain}/access-rules").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.IpAccessRule.fromJSON); });
    },

    setIpAccessRules: function(domainName, rules) {
        return this._request("PUT", "/domains/{domain}/access-rules").uri({domain: domainName}).send(rules);
    },

    getTimeAccess: function(domainName) {
        return this._request("GET", "/domains/{domain}/time-access").uri({domain: domainName}).send()
                .then(function(data) { return jscape.TimeAccess.fromJSON(data); });
    },

    setTimeAccess: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/time-access").uri({domain: domainName}).send(descriptor);
    },

    getBannedFiles: function(domainName) {
        return this._request("GET", "/domains/{domain}/banned-files").uri({domain: domainName}).send()
                .then(function(data) { return jscape.BannedFiles.fromJSON(data); });
    },

    setBannedFiles: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/banned-files").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.BannedFiles.fromJSON(data); });
    },

    testBannedFilesPattern: function(domainName, pattern) {
        return this._request("POST", "/domains/{domain}/banned-files/test-pattern").uri({domain: domainName}).sendSync(pattern);
    },

    getPasswordCompliance: function(domainName) {
        return this._request("GET", "/domains/{domain}/compliance").uri({domain: domainName}).send()
                .then(function(data) { return jscape.PasswordCompliance.fromJSON(data); });
    },

    setPasswordCompliance: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/compliance").uri({domain: domainName}).send(descriptor);
    },

    testPasswordCompliance: function(domainName, accountName, password) {
        return this._request("POST", "/domains/{domain}/compliance/test-pwd").uri({domain: domainName}).sendSync({account: accountName, password: password});
    },

    testUsernameCompliance: function(domainName, accountName) {
        return this._request("POST", "/domains/{domain}/compliance/test-username").uri({domain: domainName}).sendSync(accountName);
    },

    testUsernameCompliancePattern: function(domainName, pattern) {
        return this._request("POST", "/domains/{domain}/compliance/test-pattern").uri({domain: domainName}).sendSync(pattern);
    },

    getFipsModeState: function(domainName) {
        return this._request("GET", "/domains/{domain}/compliance/fips", false).uri({domain: domainName}).send().then(function(data) { return "true" == data; });
    },

    setFipsModeState: function(domainName, required) {
        return this._request("POST", "/domains/{domain}/compliance/fips").uri({domain: domainName}).send(!!required);
    },

    getDlpRules: function(domainName) {
        return this._request("GET", "/domains/{domain}/dlp-rules").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.DlpRule.fromJSON); });
    },

    getDlpRule: function(domainName, ruleName) {
        return this._request("GET", "/domains/{domain}/dlp-rules/{rule}").uri({domain: domainName, rule: ruleName}).send()
                .then(function(data) { return jscape.DlpRule.fromJSON(data); });
    },

    addDlpRule: function(domainName, rule) {
        return this._request("POST", "/domains/{domain}/dlp-rules").uri({domain: domainName}).send(rule)
                .then(function(data) { return jscape.DlpRule.fromJSON(data); });
    },

    setDlpRule: function(domainName, ruleName, rule) {
        return this._request("PUT", "/domains/{domain}/dlp-rules/{rule}").uri({domain: domainName, rule: ruleName}).send(rule);
    },

    deleteDlpRule: function(domainName, ruleName) {
        return this._request("DELETE", "/domains/{domain}/dlp-rules/{rule}").uri({domain: domainName, rule: ruleName}).send();
    },

    getDlpIcapConfiguration: function(domainName) {
        return this._request("GET", "/domains/{domain}/dlp-icap/config").uri({domain: domainName}).send()
                .then(function(data) { return jscape.DlpIcapConfiguration.fromJSON(data); });
    },

    setDlpIcapConfiguration: function(domainName, config) {
        return this._request("PUT", "/domains/{domain}/dlp-icap/config").uri({domain: domainName}).send(config)
                .then(function(data) { return jscape.DlpIcapConfiguration.fromJSON(data); });
    },

    testDlpIcapConfiguration: function(domainName, config) {
        return this._request("POST", "/domains/{domain}/dlp-icap/test").uri({domain: domainName}).send(config);
    },

    getDomainRestrictions: function(domainName) {
        return this._request("GET", "/domains/{domain}/restrictions").uri({domain: domainName}).send()
                .then(function(data) { return jscape.DomainRestrictions.fromJSON(data); });
    },

    setDomainRestrictions: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/restrictions").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.DomainRestrictions.fromJSON(data); });
    },

    listTriggers: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/triggers").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.Trigger.fromJSON); });
    },

    getTrigger: function(domainName, triggerName) {
        return this._request("GET", "/domains/{domain}/triggers/{trigger}").uri({domain: domainName, trigger: triggerName}).send()
                .then(function(data) { return jscape.Trigger.fromJSON(data); });
    },

    addTrigger: function(domainName, trigger) {
        return this._request("POST", "/domains/{domain}/triggers").uri({domain: domainName}).send(trigger)
                .then(function(data) { return jscape.Trigger.fromJSON(data); });
    },

    storeTrigger: function(domainName, triggerName, trigger) {
        return this._request("PUT", "/domains/{domain}/triggers/{trigger}").uri({domain: domainName, trigger: triggerName}).send(trigger)
                .then(function(data) { return jscape.Trigger.fromJSON(data); });
    },

    copyTrigger: function(domainName, sourceTriggerName, targetTriggerName) {
        return this._request("PUT", "/domains/{domain}/triggers/{trigger}").uri({domain: domainName, trigger: targetTriggerName}).headers({"x-js-copy-source": sourceTriggerName}).send()
                .then(function(data) { return jscape.Trigger.fromJSON(data); });
    },

    deleteTrigger: function(domainName, triggerName) {
        return this._request("DELETE", "/domains/{domain}/triggers/{trigger}").uri({domain: domainName, trigger: triggerName}).send();
    },

    runTrigger: function(domainName, triggerName, data) {
        return this._request("POST", "/domains/{domain}/triggers/{trigger}/run").uri({domain: domainName, trigger: triggerName}).send(data);
    },

    pauseTrigger: function(domainName, id) {
        return this._request("POST", "/domains/{domain}/triggers/{trigger}/pause").uri({domain: domainName, trigger: id}).send();
    },

    resumeTrigger: function(domainName, id) {
        return this._request("POST", "/domains/{domain}/triggers/{trigger}/resume").uri({domain: domainName, trigger: id}).send();
    },

    restartTrigger: function(domainName, id, data) {
        return this._request("POST", "/domains/{domain}/triggers/{trigger}/restart").uri({domain: domainName, trigger: id}).send(data);
    },

    killTrigger: function(domainName, id) {
        return this._request("POST", "/domains/{domain}/triggers/{trigger}/kill").uri({domain: domainName, trigger: id}).send();
    },

    importTriggers: function (domainName, file, overwrite) {
        return this._request("POST", "/domains/{domain}/trigger-service/import").uri({domain: domainName}).sendFormMultipart({file: file, overwrite: !!overwrite});
    },

    exportTriggers: function(domainName, names, filename) {
        return this._request("POST", "/domains/{domain}/trigger-service/export").uri({domain: domainName}).sendDownload({names: names, filename: filename});
    },

    promoteTriggers: function(domainName, data) {
        return this._request("POST", "/domains/{domain}/trigger-service/promote").uri({domain: domainName}).send(data);
    },

    getTriggerOrder: function(domainName, eventType) {
        return this._request("GET", "/domains/{domain}/trigger-service/order/{event}").uri({domain: domainName, event: eventType}).send()
                .then(function(data) { return jscape.TriggerOrder.fromJSON(data); });
    },

    setTriggerOrder: function(domainName, eventType, triggerOrder) {
        return this._request("PUT", "/domains/{domain}/trigger-service/order/{event}").uri({domain: domainName, event: eventType}).send(triggerOrder)
                .then(function(data) { return jscape.TriggerOrder.fromJSON(data); });
    },

    listTriggerStates: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/trigger-service/states").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.TriggerState.fromJSON); });
    },

    clearTriggerStates: function(domainName, olderThanTime) {
        return this._request("POST", "/domains/{domain}/trigger-service/states/clear").uri({domain: domainName}).send(olderThanTime);
    },

    getTriggerEvents: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/events").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.TriggerEvent.fromJSON); });
    },

    getTriggerEvent: function(domainName, eventType) {
        return this._request("GET", "/domains/{domain}/trigger-service/events/{event}").uri({domain: domainName, event: eventType}).send();
    },

    getTriggerActions: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/actions").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.TriggerAction.fromJSON); });
    },

    getTriggerAction: function(domainName, actionType) {
        return this._request("GET", "/domains/{domain}/trigger-service/actions/{action}").uri({domain: domainName, action: actionType}).send();
    },

    getTriggerFunctions: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/functions").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.TriggerFunction.fromJSON); });
    },

    getTriggerOperators: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/operators").uri({domain: domainName}).send();
    },

    validateTriggerExpression: function(domainName, expression, eventType) {
        return this._request("POST", "/domains/{domain}/trigger-service/validate-expression").uri({domain: domainName}).sendSync({expression: expression, event: eventType});
    },

    testTriggerExpression: function(domainName, expression, parameters) {
        return this._request("POST", "/domains/{domain}/trigger-service/test-expression?query={query}", false).uri({domain: domainName, query: expression}).send(parameters);
    },

    testTriggerActionContext: function(domainName, context) {
        return this._request("POST", "/domains/{domain}/trigger-service/test-action").uri({domain: domainName}).send(context);
    },

    getTriggerServiceConfiguration: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/config").uri({domain: domainName}).send()
                .then(function(data) { return jscape.TriggerServiceConfiguration.fromJSON(data); });
    },

    setTriggerServiceConfiguration: function(domainName, config) {
        return this._request("PUT", "/domains/{domain}/trigger-service/config").uri({domain: domainName}).send(config);
    },

    getRemoteTriggerServiceConfiguration: function(domainName) {
        return this._request("GET", "/domains/{domain}/trigger-service/config-remote").uri({domain: domainName}).send()
                .then(function(data) { return jscape.RemoteTriggerServiceConfiguration.fromJSON(data); });
    },

    setRemoteTriggerServiceConfiguration: function(domainName, config) {
        return this._request("PUT", "/domains/{domain}/trigger-service/config-remote").uri({domain: domainName}).send(config);
    },

    testRemoteTriggerServiceConfiguration: function(domainName, config) {
        return this._request("POST", "/domains/{domain}/trigger-service/test-remote").uri({domain: domainName}).send(config);
    },

    getAuthenticationService: function(domainName) {
        return this._request("GET", "/domains/{domain}/authentication").uri({domain: domainName}).send()
                .then(function(data) { return jscape.AuthService.fromJSON(data); });
    },

    setAuthenticationService: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/authentication").uri({domain: domainName}).send(descriptor);
    },

    testAuthenticationService: function (domainName, descriptor, username, password, clientIp) {
        return this._request("POST", "/domains/{domain}/authentication/test").uri({domain: domainName}).sendFormMultipart({descriptor: descriptor, login: username, password: password, ip: clientIp}, true);
    },

    getTwoFactorService: function(domainName) {
        return this._request("GET", "/domains/{domain}/authentication/two-factor").uri({domain: domainName}).send()
                .then(function(data) { return jscape.UserConfirmationService.fromJSON(data); });
    },

    setTwoFactorService: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/authentication/two-factor").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.UserConfirmationService.fromJSON(data); });
    },

    getWebSsoService: function(domainName) {
        return this._request("GET", "/domains/{domain}/authentication/web-sso").uri({domain: domainName}).send()
                .then(function(data) { return jscape.WebSsoService.fromJSON(data); });
    },

    setWebSsoService: function(domainName, descriptor) {
        return this._request("PUT", "/domains/{domain}/authentication/web-sso").uri({domain: domainName}).send(descriptor);
    },

    listAccounts: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/accounts").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                        .then(function(data) { return jscape.Page.fromJSON(data, jscape.AccountInfo.fromJSON); });
    },

    getAccountNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/account-names").uri({domain: domainName}).send();
    },

    getAccountAdminNames: function (domainName) {
        return this._request("GET", "/domains/{domain}/account-names").uri({domain: domainName}).matrix({admin: true}).send();
    },

    getAccount: function(domainName, accountName) {
        return this._request("GET", "/domains/{domain}/accounts/{account}").uri({domain: domainName, account: accountName}).send()
                .then(function(data) { return jscape.Account.fromJSON(data); });
    },

    addAccount: function(domainName, account) {
        return this._request("POST", "/domains/{domain}/accounts").uri({domain: domainName}).send(account)
                .then(function(data) { return jscape.AccountInfo.fromJSON(data); });
    },

    copyAccount: function(domainName, sourceAccountName, targetAccountName) {
        return this._request("PUT", "/domains/{domain}/accounts/{account}").uri({domain: domainName, account: targetAccountName}).headers({"x-js-copy-source": sourceAccountName}).send()
                .then(function(data) { return jscape.AccountInfo.fromJSON(data); });
    },

    setAccount: function(domainName, accountName, account) {
        return this._request("PUT", "/domains/{domain}/accounts/{account}").uri({domain: domainName, account: accountName}).send(account)
                .then(function(data) { return jscape.AccountInfo.fromJSON(data); });
    },

    deleteAccount: function(domainName, accountName) {
        return this._request("DELETE", "/domains/{domain}/accounts/{account}").uri({domain: domainName, account: accountName}).send();
    },

    changeAccountPassword: function(domainName, accountName, password) {
        return this._request("POST", "/domains/{domain}/accounts/{account}/changepassword").uri({domain: domainName, account: accountName}).send(password);
    },

    resetAccountTotp: function(domainName, accountName) {
        return this._request("POST", "/domains/{domain}/accounts/{account}/resetotp").uri({domain: domainName, account: accountName}).send();
    },

    getAccountQuotas: function(domainName, accountName) {
        return this._request("GET", "/domains/{domain}/accounts/{account}/quotas").uri({domain: domainName, account: accountName}).send()
                .then(function(data) { return jscape.AccountTransferQuotas.fromJSON(data); });
    },

    listAccountTemplates: function(domainName) {
        return this._request("GET", "/domains/{domain}/account-templates").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.AccountTemplateInfo.fromJSON)});
    },

    getAccountTemplateNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/account-template-names").uri({domain: domainName}).send();
    },

    getAccountTemplate: function(domainName, templateName) {
        return this._request("GET", "/domains/{domain}/account-templates/{template}").uri({domain: domainName, template: templateName}).send()
                .then(function(data) { return jscape.AccountTemplate.fromJSON(data); });
    },

    addAccountTemplate: function(domainName, template) {
        return this._request("POST", "/domains/{domain}/account-templates").uri({domain: domainName}).send(template)
                .then(function(data) { return jscape.AccountTemplateInfo.fromJSON(data); });
    },

    setTemplate: function(domainName, templateName, template) {
        return this._request("PUT", "/domains/{domain}/account-templates/{template}").uri({domain: domainName, template: templateName}).send(template)
                .then(function(data) { return jscape.AccountTemplateInfo.fromJSON(data); });
    },

    deleteTemplate: function(domainName, templateName) {
        return this._request("DELETE", "/domains/{domain}/account-templates/{template}").uri({domain: domainName, template: templateName}).send();
    },

    listGroups: function (domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/groups").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.Group.fromJSON); });
    },

    getGroupNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/group-names").uri({domain: domainName}).send()
    },

    getGroup: function (domainName, groupName) {
        return this._request("GET", "/domains/{domain}/groups/{group}").uri({domain: domainName, group: groupName}).send()
                .then(function (data) { return jscape.Group.fromJSON(data); });
    },

    addGroup: function (domainName, group) {
        return this._request("POST", "/domains/{domain}/groups").uri({domain: domainName}).send(group)
                .then(function (data) { return jscape.Group.fromJSON(data); });
    },

    setGroup: function (domainName, groupName, group) {
        return this._request("PUT", "/domains/{domain}/groups/{group}").uri({domain: domainName, group: groupName}).send(group);
    },

    copyGroup: function (domainName, sourceGroupName, targetGroupName) {
        return this._request("PUT", "/domains/{domain}/groups/{group}").uri({domain: domainName, group: targetGroupName}).headers({"x-js-copy-source": sourceGroupName}).send()
                .then(function (data) { return jscape.Group.fromJSON(data); });
    },

    deleteGroup: function (domainName, groupName) {
        return this._request("DELETE", "/domains/{domain}/groups/{group}").uri({domain: domainName, group: groupName}).send();
    },

    getGroupUsers: function (domainName, groupName) {
        return this._request("GET", "/domains/{domain}/groups/{group}/accounts").uri({domain: domainName, group: groupName}).send();
    },

    setGroupUsers: function (domainName, groupName, accountNames) {
        return this._request("PUT", "/domains/{domain}/groups/{group}/accounts").uri({domain: domainName, group: groupName}).send(accountNames);
    },

    listReverseProxies: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/reverse-proxies").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                        .then(function(data) { return jscape.Page.fromJSON(data, jscape.ResourceDescriptor.fromJSON); });
    },

    getReverseProxyNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/reverse-proxy-names").uri({domain: domainName}).send();
    },

    getReverseProxy: function(domainName, proxyName) {
        return this._request("GET", "/domains/{domain}/reverse-proxies/{proxy}").uri({domain: domainName, proxy: proxyName}).send()
                .then(function(data) { return jscape.ResourceDescriptor.fromJSON(data); });
    },

    addReverseProxy: function(domainName, descriptor) {
        return this._request("POST", "/domains/{domain}/reverse-proxies").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.ResourceDescriptor.fromJSON(data); });
    },

    setReverseProxy: function(domainName, proxyName, descriptor) {
        return this._request("PUT", "/domains/{domain}/reverse-proxies/{proxy}").uri({domain: domainName, proxy: proxyName}).send(descriptor);
    },

    deleteReverseProxy: function(domainName, proxyName) {
        return this._request("DELETE", "/domains/{domain}/reverse-proxies/{proxy}").uri({domain: domainName, proxy: proxyName}).send();
    },

    testReverseProxy: function(domainName, proxyName, descriptor) {
        return this._request("POST", "/domains/{domain}/reverse-proxies/{proxy}/test").uri({domain: domainName, proxy: proxyName}).send(descriptor);
    },

    listDirectoryMonitors: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/dir-monitors").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.DirectoryMonitor.fromJSON); });
    },

    getDirectoryMonitorNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/dir-monitor-names").uri({domain: domainName}).send();
    },

    getDirectoryMonitor: function(domainName, monitorName) {
        return this._request("GET", "/domains/{domain}/dir-monitors/{monitor}").uri({domain: domainName, monitor: monitorName}).send()
                .then(function(data) { return jscape.DirectoryMonitor.fromJSON(data); });
    },

    addDirectoryMonitor: function(domainName, descriptor) {
        return this._request("POST", "/domains/{domain}/dir-monitors").uri({domain: domainName}).send(descriptor)
                .then(function(data) { return jscape.DirectoryMonitor.fromJSON(data); });
    },

    setDirectoryMonitor: function(domainName, monitorName, descriptor) {
        return this._request("PUT", "/domains/{domain}/dir-monitors/{monitor}").uri({domain: domainName, monitor: monitorName}).send(descriptor);
    },

    deleteDirectoryMonitor: function(domainName, monitorName) {
        this._request("DELETE", "/domains/{domain}/dir-monitors/{monitor}").uri({domain: domainName, monitor: monitorName}).send();
    },

    runDirectoryMonitor: function(domainName, monitorName) {
        this._request("POST", "/domains/{domain}/dir-monitors/{monitor}/run").uri({domain: domainName, monitor: monitorName}).send();
    },

    getDirectoryMonitorServiceConfiguration: function(domainName) {
        return this._request("GET", "/domains/{domain}/dir-monitor-service/config").uri({domain: domainName}).send()
                .then(function(data) { return jscape.DirectoryMonitorServiceConfiguration.fromJSON(data); });
    },

    setDirectoryMonitorServiceConfiguration: function(domainName, config) {
        return this._request("PUT", "/domains/{domain}/dir-monitor-service/config").uri({domain: domainName}).send(config);
    },

    listDropZones: function (domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/drop-zones").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.DropZoneDescriptor.fromJSON); });
    },

    getDropZone: function (domainName, dropZoneName) {
        return this._request("GET", "/domains/{domain}/drop-zones/{zone}").uri({domain: domainName, zone: dropZoneName}).send()
                .then(function (data) { return jscape.DropZoneDescriptor.fromJSON(data); });
    },

    getDropZoneURI: function(domainName, zoneName) {
        return this._request("POST", "/domains/{domain}/drop-zones/{zone}/uri", false).uri({domain: domainName, zone: zoneName}).send();
    },

    addDropZone: function (domainName, descriptor) {
        return this._request("POST", "/domains/{domain}/drop-zones").uri({domain: domainName}).send(descriptor)
                .then(function (data) { return jscape.DropZoneDescriptor.fromJSON(data); });
    },

    setDropZone: function (domainName, dropZoneName, descriptor) {
        return this._request("PUT", "/domains/{domain}/drop-zones/{zone}").uri({domain: domainName, zone: dropZoneName}).send(descriptor);
    },

    deleteDropZone: function (domainName, zoneName) {
        return this._request("DELETE", "/domains/{domain}/drop-zones/{zone}").uri({domain: domainName, zone: zoneName}).send();
    },

    purgeDropZone: function (domainName, zoneName) {
        return this._request("POST", "/domains/{domain}/drop-zones/{zone}/purge").uri({domain: domainName, zone: zoneName}).send();
    },

    listWebThemes: function (domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/web-themes").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.WebTheme.fromJSON); });
    },

    getWebThemeNames: function(domainName) {
        return this._request("GET", "/domains/{domain}/web-theme-names").uri({domain: domainName}).send();
    },

    getWebTheme: function (domainName, themeName) {
        return this._request("GET", "/domains/{domain}/web-themes/{theme}").uri({domain: domainName, theme: themeName}).send()
                .then(function (data) { return jscape.WebTheme.fromJSON(data); });
    },

    getWebThemeURI: function(domainName, themeName) {
        return this._request("POST", "/domains/{domain}/web-themes/{theme}/uri", false).uri({domain: domainName, theme: themeName}).send();
    },

    addWebTheme: function (domainName, theme) {
        return this._request("POST", "/domains/{domain}/web-themes").uri({domain: domainName}).send(theme)
                .then(function (data) { return jscape.WebTheme.fromJSON(data); });
    },

    setWebTheme: function (domainName, themeName, theme) {
        return this._request("PUT", "/domains/{domain}/web-themes/{theme}").uri({domain: domainName, theme: themeName}).send(theme);
    },

    deleteWebTheme: function (domainName, themeName) {
        return this._request("DELETE", "/domains/{domain}/web-themes/{theme}").uri({domain: domainName, theme: themeName}).send();
    },

    listTradingPartners: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/trading-partners").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function (data) { return jscape.Page.fromJSON(data, jscape.TradingPartner.fromJSON); });
    },

    getTradingPartnerNames: function(domainName, protocols) {
        return this._request("GET", "/domains/{domain}/trading-partner-names").uri({domain: domainName}).matrix({protocol: protocols}).send();
    },

    getTradingPartner: function(domainName, partnerName) {
        return this._request("GET", "/domains/{domain}/trading-partners/{name}").uri({domain: domainName, name: partnerName}).send()
                .then(function (data) { return jscape.TradingPartner.fromJSON(data); });
    },

    addTradingPartner: function(domainName, partner) {
        return this._request("POST", "/domains/{domain}/trading-partners").uri({domain: domainName}).send(partner)
                .then(function (data) { return jscape.TradingPartner.fromJSON(data); });
    },

    setTradingPartner: function(domainName, partnerName, partner) {
        return this._request("PUT", "/domains/{domain}/trading-partners/{name}").uri({domain: domainName, name: partnerName}).send(partner);
    },

    copyTradingPartner: function(domainName, sourcePartnerName, targetPartnerName) {
        return this._request("PUT", "/domains/{domain}/trading-partners/{name}").uri({domain: domainName, name: targetPartnerName}).headers({"x-js-copy-source": sourcePartnerName}).send()
                .then(function(data) { return jscape.TradingPartner.fromJSON(data); });
    },

    deleteTradingPartner: function(domainName, partnerName) {
        return this._request("DELETE", "/domains/{domain}/trading-partners/{partner}").uri({domain: domainName, partner: partnerName}).send();
    },

    testTradingPartner: function(domainName, partner) {
        return this._request("POST", "/domains/{domain}/trading-partners/{partner}/test").uri({domain: domainName, partner: partner.name}).send(partner);
    },

    sendTradingPartnerFile: function(domainName, partnerName, data) {
        var req = this._request("POST", "/domains/{domain}/trading-partners/{partner}/send-file").uri({domain: domainName, partner: partnerName});
        return (typeof data === "string") ? req.send(data) : req.sendForm(data);
    },

    sendOftpPartnerCertificate: function(domainName, partnerName, data) {
        return this._request("POST", "/domains/{domain}/trading-partners/{partner}/send-cert", false).uri({domain: domainName, partner: partnerName}).sendForm(data);
    },

    listContacts: function(domainName, startIndex, count, query) {
        return this._request("GET", "/domains/{domain}/contacts").uri({domain: domainName}).matrix({start: startIndex, count: count}).send(query)
                .then(function(data) { return jscape.Page.fromJSON(data, jscape.Contact.fromJSON); });
    },

    getContact: function(domainName, id) {
        return this._request("GET", "/domains/{domain}/contacts/{id}").uri({domain: domainName, id: id}).send()
                .then(function(data) { return jscape.Contact.fromJSON(data); });
    },

    addContact: function(domainName, contact) {
        return this._request("POST", "/domains/{domain}/contacts").uri({domain: domainName}).send(contact)
                .then(function(data) { return jscape.Contact.fromJSON(data); });
    },

    setContact: function(domainName, id, contact) {
        return this._request("PUT", "/domains/{domain}/contacts/{id}").uri({domain: domainName, id: id}).send(contact);
    },

    deleteContact: function(domainName, id) {
        return this._request("DELETE", "/domains/{domain}/contacts/{id}").uri({domain: domainName, id: id}).send();
    },

    getServiceInfos: function(domainName) {
        return this._request("GET", "/domains/{domain}/services").uri({domain: domainName}).send()
                .then(function(data) { return $.map(data || [], jscape.ServiceInfo.fromJSON); });
    },

    addService: function(domainName, descriptor) {
        return this._request("POST", "/domains/{domain}/services").uri({domain: domainName}).send(descriptor);
    },

    updateService: function(domainName, protocol, id, newDescriptor) {
        return this._request("PUT", "/domains/{domain}/services/{protocol}").uri({domain: domainName, protocol: protocol}).matrix({id: id}).send(newDescriptor);
    },

    deleteService: function(domainName, protocol, id) {
        return this._request("DELETE", "/domains/{domain}/services/{protocol}").uri({domain: domainName, protocol: protocol}).matrix({id: id}).send();
    },

    getServiceDescriptor: function (domainName, protocol, id) {
        return this._request("GET", "/domains/{domain}/services/{protocol}").uri({domain: domainName, protocol: protocol}).matrix({id: id}).send()
                .then(function (data) { return jscape.Service.fromJSON(data); });
    },

    getServiceConfiguration: function(domainName, protocol) {
        return this._request("GET", "/domains/{domain}/services/{protocol}/config").uri({domain: domainName, protocol: protocol.toLowerCase()}).send()
                .then(function(data) { return jscape.ServiceConfiguration.fromJSON(data); });
    },

    setServiceConfiguration: function(domainName, protocol, config) {
        return this._request("PUT", "/domains/{domain}/services/{protocol}/config").uri({domain: domainName, protocol: protocol.toLocaleLowerCase()}).send(config);
    },

    getServiceWebResources: function(domainName, protocol) {
        return this._request("GET", "/domains/{domain}/services/{protocol}/web-resources").uri({domain: domainName, protocol: protocol.toLowerCase()}).send()
                .then(function(data) { return $.map(data || [], jscape.WebResources.fromJSON); });
    },

    generateCertificateRevocationList: function(signingKey, expires) {
        return this._request("POST", "/keystore/crl/generate").send({signingKeyAlias: signingKey, expirationPeriodDays: expires});
    },

    verifyCertificateRevocation: function(file, certificateType) {
        return this._request("POST", "/keystore/crl/verify").sendFormMultipart({file: file, certificateType: certificateType});
    },

    getServerKeys: function(algorithm) {
        return this._request("GET", "/keystore/keys/server").matrix({algorithm: algorithm}).send()
                .then(function(data) { return $.map(data || [], jscape.ServerKeySummary.fromJSON); })
    },

    getServerKeyView: function(alias) {
        return this._request("GET", "/keystore/keys/server/{alias}").uri({alias: alias}).send({"pretty-print": true});
    },

    importServerKey: function(alias, file, filePassword, keyAlias, keyPassword, certificateFile, certificatePassword, certificateAlias) {
        return this._request("POST", "/keystore/keys/server/{alias}/import").uri({alias: alias})
                .sendFormMultipart({file: file, filePassword: filePassword, keyAlias: keyAlias, keyPassword: keyPassword, certificateFile: certificateFile, certificateFilePassword: certificatePassword, certificateAlias: certificateAlias})
                .then(function(data) { return jscape.ServerKeySummary.fromJSON(data); });
    },

    importServerKeyCertificates: function(alias, file, filePassword, keyAlias) {
        return this._request("POST", "/keystore/keys/server/{alias}/import-crt").uri({alias: alias})
                .sendFormMultipart({file: file, filePassword: filePassword, keyAlias: keyAlias})
                .then(function(data) { return jscape.ServerKeySummary.fromJSON(data); });
    },

    generateServerKey: function(alias, data) {
        return this._request("POST", "/keystore/keys/server/{alias}/generate").uri({alias: alias}).sendForm(data)
                .then(function(data) { return jscape.ServerKeySummary.fromJSON(data); });
    },

    generateServerKeyCertificate: function(alias, data) {
        return this._request("POST", "/keystore/keys/server/{alias}/generate-cert").uri({alias: alias}).sendForm(data)
                .then(function(data) { return jscape.ServerKeySummary.fromJSON(data); });
    },

    generateServerKeyCSR: function(alias, filename) {
        return this._request("GET", "/keystore/keys/server/{alias}/generate-csr").uri({alias: alias}).sendDownload({filename: filename});
    },

    exportServerCertificate: function(alias, format, filename) {
        return this._request("GET", "/keystore/keys/server/{alias}/export-crt").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    exportServerPublicKey: function(alias, format, filename) {
        return this._request("GET", "/keystore/keys/server/{alias}/export-pub").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    exportServerPrivateKey: function(alias, format, password, filename) {
        return this._request("POST", "/keystore/keys/server/{alias}/export-prv").uri({alias: alias}).sendDownload({format: format, filename: filename, filePassword: password});
    },

    setServerKeyRevocationStatus: function(alias, revoked) {
        return this._request("POST", "/keystore/keys/server/{alias}/revoke").uri({alias: alias}).send(!!revoked);
    },

    deleteServerKey: function(alias) {
        return this._request("DELETE", "/keystore/keys/server/{alias}").uri({alias: alias}).send();
    },

    getClientPublicKeys: function() {
        return this._request("GET", "/keystore/keys/client").send()
                .then(function (data) { return $.map(data || [], jscape.PublicKeySummary.fromJSON); });
    },

    importClientKey: function(alias, file) {
        return this._request("POST", "/keystore/keys/client/{alias}/import").uri({alias: alias}).sendFormMultipart({file: file})
                .then(function(data) { return jscape.CertificateSummary.isCertificateData(data) ? jscape.CertificateSummary.fromJSON(data) : jscape.PublicKeySummary.fromJSON(data); });
    },

    importClientKeyTsl: function(url) {
        return this._request("POST", "/keystore/keys/client/{alias}/import-tsl").uri({alias: "oftp"}).send(url);
    },

    generateClientKey: function(alias, data) {
        return this._request("POST", "/keystore/keys/client/{alias}/generate").uri({alias: alias}).sendDownload(data);
    },

    exportClientPublicKey: function(alias, format, filename) {
        return this._request("GET", "/keystore/keys/client/{alias}/export").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    deleteClientPublicKey: function(alias) {
        return this._request("DELETE", "/keystore/keys/client/{alias}").uri({alias: alias}).send();
    },

    getHostPublicKeys: function() {
        return this._request("GET", "/keystore/keys/host").send()
                .then(function (data) { return $.map(data || [], jscape.PublicKeySummary.fromJSON); });
    },

    getHostCertificateView: function(alias) {
        return this._request("GET", "/keystore/certificates/host/{alias}").uri({alias: alias}).send({"pretty-print": true});
    },

    importHostKey: function(alias, file) {
        return this._request("POST", "/keystore/keys/host/{alias}/import").uri({alias: alias}).sendFormMultipart({file: file})
                .then(function(data) { return jscape.CertificateSummary.isCertificateData(data) ? jscape.CertificateSummary.fromJSON(data) : jscape.PublicKeySummary.fromJSON(data); });
    },

    importHostKeyTsl: function(url) {
        return this._request("POST", "/keystore/keys/host/{alias}/import-tsl").uri({alias: "oftp"}).send(url);
    },

    exportHostPublicKey: function(alias, format, filename) {
        return this._request("GET", "/keystore/keys/host/{alias}/export").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    setHostCertificateRevocationStatus: function(alias, revoked) {
        return this._request("POST", "/keystore/certificates/host/{alias}/revoke").uri({alias: alias}).send(!!revoked);
    },

    deleteHostPublicKey: function(alias) {
        return this._request("DELETE", "/keystore/keys/host/{alias}").uri({alias: alias}).send();
    },

    getPgpPublicKeys: function() {
        return this._request("GET", "/keystore/keys/pgp/public").send()
                .then(function (data) { return $.map(data || [], jscape.PgpKeySummary.fromJSON); });
    },

    getPgpSecretKeys: function() {
        return this._request("GET", "/keystore/keys/pgp/secret").send()
                .then(function (data) { return $.map(data || [], jscape.PgpKeySummary.fromJSON); });
    },

    importPgpPublicKey: function(file) {
        return this._request("POST", "/keystore/keys/pgp/public/import").sendFormMultipart({file: file})
                .then(function(data) { return jscape.PgpKeySummary.fromJSON(data); });
    },

    importPgpSecretKey: function(file, password) {
        return this._request("POST", "/keystore/keys/pgp/secret/import").sendFormMultipart({file: file, filePassword: password})
                .then(function(data) { return jscape.PgpKeySummary.fromJSON(data); });
    },

    generatePgpKey: function(realName, email, keyType, keyLength) {
        return this._request("POST", "/keystore/keys/pgp/generate").sendForm({realName: realName, email: email, keyType: keyType, keyLength: keyLength})
                .then(function(data) { return jscape.PgpKeySummary.fromJSON(data); });
    },

    exportPgpPublicKey: function(alias, filename) {
        return this._request("GET", "/keystore/keys/pgp/public/{alias}/export").uri({alias: alias}).sendDownload({filename: filename});
    },

    exportPgpSecretKey: function(alias, filename, password) {
        return this._request("POST", "/keystore/keys/pgp/secret/{alias}/export").uri({alias: alias}).sendDownload({filename: filename, password: password});
    },

    exportPgpSecretPublicKey: function(alias, filename) {
        return this._request("GET", "/keystore/keys/pgp/secret/{alias}/export-pub").uri({alias: alias}).sendDownload({filename: filename});
    },

    deletePgpPublicKey: function(alias) {
        return this._request("DELETE", "/keystore/keys/pgp/public/{alias}").uri({alias: alias}).send();
    },

    deletePgpSecretKey: function(alias) {
        return this._request("DELETE", "/keystore/keys/pgp/secret/{alias}").uri({alias: alias}).send();
    },

    getPgpPublicKeySignatures: function(alias) {
        return this._request("GET", "/keystore/keys/pgp/public/{alias}/signatures").uri({alias: alias}).send()
                .then(function(data) { return $.map(data || [], jscape.PgpSignatureDescriptor.fromJSON); });
    },

    getPgpSecretKeySignatures: function(alias) {
        return this._request("GET", "/keystore/keys/pgp/secret/{alias}/signatures").uri({alias: alias}).send()
                .then(function(data) { return $.map(data || [], jscape.PgpSignatureDescriptor.fromJSON); });
    },

    signPgpPublicKey: function(alias, secretKeyAlias) {
        return this._request("PUT", "/keystore/keys/pgp/public/{alias}/sign?secretKeyAlias={ska}").uri({alias: alias, ska: secretKeyAlias}).send()
                .then(function(data) { return jscape.PgpKeySummary.fromJSON(data); });
    },

    getClientCertificates: function() {
        return this._request("GET", "/keystore/certificates/client").send()
                .then(function (data) { return $.map(data || [], jscape.CertificateSummary.fromJSON); });
    },

    getClientCertificate: function(alias) {
        return this._request("GET", "/keystore/certificates/client/{alias}").uri({alias: alias}).send()
                .then(function(data) { return jscape.CertificateSummary.fromJSON(data); });
    },

    getClientCertificateView: function(alias) {
        return this._request("GET", "/keystore/certificates/client/{alias}").uri({alias: alias}).send({"pretty-print": true});
    },

    getHostCertificates: function(algorithm) {
        return this._request("GET", "/keystore/certificates/host").matrix({algorithm: algorithm}).send()
                .then(function (data) { return $.map(data || [], jscape.CertificateSummary.fromJSON); });
    },

    exportHostCertificate: function(alias, format, filename) {
        return this._request("GET", "/keystore/certificates/host/{alias}/export").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    exportHostCertificatePublicKey: function(alias, format, filename) {
        return this._request("GET", "/keystore/certificates/host/{alias}/export-pub").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    exportClientCertificate: function(alias, format, filename) {
        return this._request("GET", "/keystore/certificates/client/{alias}/export").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    exportClientCertificatePublicKey: function(alias, format, filename) {
        return this._request("GET", "/keystore/certificates/client/{alias}/export-pub").uri({alias: alias}).sendDownload({format: format, filename: filename});
    },

    setClientCertificateRevocationStatus: function(alias, revoked) {
        return this._request("POST", "/keystore/certificates/client/{alias}/revoke").uri({alias: alias}).send(!!revoked);
    },

    deleteHostCertificate: function(alias) {
        return this._request("DELETE", "/keystore/certificates/host/{alias}").uri({alias: alias}).send();
    },

    deleteClientCertificate: function(alias) {
        return this._request("DELETE", "/keystore/certificates/client/{alias}").uri({alias: alias}).send();
    },

    keyUsage: function(alias) {
        return this._request("POST", "/keystore/key-usage").send(alias);
    },

    _request: function (method, url, parse) {
        var request = new jscape.VersionAwareRequest(method, "/2/rest/management" + url, this.timeout, parse);
        request.csrf = this.csrf;
        return request;
    },

    _submitForm: function(method, url, data) {
        data = $.extend(data || {}, ("GET" !== method && this.csrf) || {});
        var html = ['<form action="' + encodeURI("/settings" + 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();
    },

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

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

    /** @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 csrf = this.csrf;
        var settings = $.extend({
            contentType: data ? "application/json" : "",
            data: data || "",
            dataType: "text",
            processData: "GET" === this.settings.type,
            statusCode: {401: function() { window.location.replace("/"); }},
            beforeSend: function(jqXhr) {
                if ($.isPlainObject(csrf)) { for (var prop in csrf) { jqXhr.setRequestHeader(prop, csrf[prop]); }}
                csrf = null;
            }
            }, this.settings);
        var self = this;
        return $.ajax(this.url, settings)
                .then(function (data, status, jqXhr) {
                    if (!self.parseResponse) { return jqXhr.responseText; }
                    try { return data ? $.parseJSON(data) : null; } catch (e) { return null; }
                })
                .fail(function (jqXhr, status, error) {
                    if (this.global) { self._handleError(jqXhr, status, error); }
                });
    },

    sendForm: function(data) {
        this.settings["contentType"] = "application/x-www-form-urlencoded";
        return this.send($.param(data, true));
    },

    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 (this.settings.type === "POST" && $.type(data) == "object") {
            data = $.extend({}, data, this.csrf);
            this.csrf = null;
        }
        return this.send(data);
    },

    sendSync: function(data) {
        if ($.isPlainObject(this.csrf)) {
            this.headers(this.csrf);
        }
        var _handleError = $.proxy(this._handleError, this);
        var response = $.ajax(this.url, $.extend(this.settings, {
            async: false,
            data: data,
            processData: true,
            error: function(jqXhr, status, error) {
                if (this.global) {
                    _handleError(jqXhr, status, error);
                }
                _handleError = null;
            }
        }));
        return response.status >= 200 && response.status < 300 ? (response.responseJSON ? response.responseJSON : response.responseText) : null;
    },

    _handleError: function(jqXhr, status, error) {
        var info;
        try {
            info = jqXhr.status == 0
                    ? {code: "NO_CONNECTION", description: "There is no Internet Connection"}
                    : $.parseJSON(jqXhr.responseText);
        } catch (e) {
            info = {code: jqXhr.statusText, description: jqXhr.responseText || ""};
        }

        if ("NO_PERMISSIONS" === info.code && "GET" === this.settings.type && "iframe" !== this.settings.dataType) {
            return;
        }

        var title = (jqXhr.status ? jqXhr.status + " " : "") + jqXhr.statusText;
        var message = info.message || "";
        var details = info.description || info.details || "";
        message = (details != message ? message + "\r\n" + details : message).trim();
        var autoLogout = "NOT_AUTHENTICATED" == info.code || "SESSION_EXPIRED" == info.code;
        if (info.code && window.R.string.contains(info.code)) {
            var tmp = window.R.string[info.code].supplant({0: message});
            message = tmp !== window.R.string[info.code] || info.code == "SESSION_EXPIRED" ? tmp : (tmp !== message ? tmp + "\r\n" + message : tmp);
        }
        if (info.code == "NO_CONNECTION") {
            $(document).triggerHandler("broadcast", [message, false]);
        } else {
            this._setupErrorDialog(title || window.R.string.APPLICATION_TITLE, message || "Internal server error!")
                    .show().always(function () { if (autoLogout) { window.location.replace("/"); }});
        }
    },

    _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.";
            }
        }
    }
});

/** @class */
jscape.VersionAwareRequest = jscape.Request.extend(
    /** @lends {jscape.VersionAwareRequest.prototype} */
{
    /** @override */
    send: function(data) {
        var url = this.url || this.settings.url;
        if (("POST" === this.settings.type || "PUT" === this.settings.type) && jQuery.etag[url]) {
            this.settings.ifModified = false;
            this.headers({"If-Match": jQuery.etag[this.url]}, false);
            this.settings.complete = function(jqXhr, status) {
                var tag = jqXhr.getResponseHeader("Etag");
                if (tag) {
                    jQuery.etag[url] = tag;
                }
            };
        } else {
            this.settings.ifModified = true;
        }
        return this._super(data)
                .then(function (data) {
                          if ($.isPlainObject(data) && data.hasOwnProperty("version")) {
                              delete data.version;
                          }
                          return data;
                      });
    }
});



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

    $.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);