PMA.UI Documentation by Pathomation

components/js/context.js

import { checkBrowserCompatibility } from '../../view/helpers';
import { Events, ApiMethods, callApiMethod, parseJson, _sessionList } from './components';
import { PromptLogin } from './promptlogin';
import { Resources } from '../../resources/resources';

let PmaStartUrl = "http://127.0.0.1:54001/";

/**
 * The Context class glues component instances together. It provides API method implementations and simplifies the interaction with PMA.core by automatically managing authentication and sessionID handling, via the authentication provider classes.
 * @param  {Object} options
 * @param  {string} options.caller
 * @tutorial 03-gallery
 * @tutorial 04-tree
 * @tutorial 05-annotations
 */

export class Context {
    constructor(options) {
        if (!checkBrowserCompatibility()) {
            return;
        }

        if (!options || typeof options.caller !== "string") {
            throw "Caller parameter not supplied";
        }

        this.options = options;

        // create event listeners object and add one array for each event type
        this.listeners = {};
        for (var ev in Events) {
            if (Events.hasOwnProperty(ev)) {
                this.listeners[Events[ev]] = [];
            }
        }

        // list of components that can authenticate against a PMA.core server
        this.authenticationProviders = [];
    }
    /**
     * Gets the caller value
     * @return {string}
     */
    getCaller() {
        return this.options.caller;
    }
    /**
     * Adds an authentication provider to the list of available authentication methods
     * @param  {AutoLogin|PromptLogin|SessionLogin} provider
     */
    registerAuthenticationProvider(provider) {
        if (typeof provider.authenticate !== "function") {
            console.error("Invalid authentication provider");
        }
        else {
            this.authenticationProviders.push(provider);
        }
    }
    /**
     * Removes an authentication provider from the list of available authentication methods
     * @param  {AutoLogin|PromptLogin|SessionLogin} provider - The provider to remove
     * @param {bool} clearCache - Clears the session ids cache
     */
    removeAuthenticationProvider(provider, clearCache) {
        if (typeof provider.authenticate !== "function") {
            console.error("Invalid authentication provider");
        }
        else {
            for (var i = 0; i < this.authenticationProviders.length; i++) {
                if (this.authenticationProviders[i] === provider) {
                    this.authenticationProviders.splice(i, 1);

                    // Clear all cache
                    if (clearCache !== false) {
                        _sessionList.clear();
                    }
                    break;
                }
            }
        }
    }
    /**
     * Gets the list of available authentication methods
     * @returns  [{PMA.UI.Authentication.AutoLogin|PMA.UI.Authentication.PromptLogin|PMA.UI.Authentication.SessionLogin}] providers
     */
    getAuthenticationProviders() {
        return this.authenticationProviders;
    }
    /**
    * PMA.core authentication response
    * @typedef {Object} Context~authenticationResponse
    * @property {string} SessionId - The session ID
    * @property {string} Username - The username
    * @property {string} Email - The user's email
    * @property {string} FirstName - The user's first name
    * @property {string} LastName - The user's last name
    */
    /**
     * A function called after successfully pinging a list of servers
     * @callback Context~pingServersDoneCallback
     * @param {String[]} servers - A sorted list of server url's from fastest to slowest
     * @param {Object[]} detailInfo - An array of detailed information from pinging the servers (not sorted)
     * @param {string} detailInfo.serverUrl - The server url
     * @param {bool} detailInfo.success - Whether the server responded to any pinging
     * @param {Number[]} detailInfo.times - An array of all the times the server took to respond (in miliseconds)
     * @param {Number} detailInfo.avgTime - The average time the server took to respond (in miliseconds)
     * @param {Number} detailInfo.attempts - The number of attempted pings to the server
     */
    /**
     * Pings a list of servers to find the fastests
     * @param {string[]} servers - An array of server url to ping
     * @param {Context~pingServersDoneCallback} done - The done callback to run
     * @param {Number} [maxAttempts=5] - The number of attempts for each server
     * */
    pingServers(servers, done, maxAttempts) {
        if (maxAttempts <= 1 || !maxAttempts || maxAttempts === undefined) {
            maxAttempts = 6;
        }

        var instances = servers.map(function (s) { return { serverUrl: s, success: false, times: [], avgTime: null, attempts: 0 }; });

        var cb = function (startTime, instanceIndex, success) {
            instances[instanceIndex].attempts++;
            if (success && instances[instanceIndex].attempts > 1) {
                // We ignore first attempt for warm up
                var time = performance.now() - startTime;
                instances[instanceIndex].times.push(time);
                instances[instanceIndex].avgTime = instances[instanceIndex].avgTime != null ?
                    ((instances[instanceIndex].avgTime * instances[instanceIndex].times.length) + time) / (instances[instanceIndex].times.length + 1) : time;
                instances[instanceIndex].success = true;
            }

            if (instances[instanceIndex].attempts >= maxAttempts) {
                instanceIndex++;
            }

            if (instanceIndex >= instances.length) {
                // done testing
                if (typeof done === "function") {
                    done(instances.sort(function (a, b) {
                        if (a.avgTime != null && b.avgTime != null) {
                            return a.avgTime - b.avgTime;
                        }
                        else if (a.avgTime != null) {
                            return -10000000;
                        }
                        else {
                            return 10000000;
                        }
                    }).map(function (s) { return s.serverUrl; }), instances);
                }
            }
            else {
                // Run next test
                var nowTime = performance.now();
                callApiMethod({
                    serverUrl: instances[instanceIndex].serverUrl,
                    method: ApiMethods.GetVersionInfo,
                    success: cb.bind(this, nowTime, instanceIndex, true),
                    failure: cb.bind(this, nowTime, instanceIndex, false)
                });
            }
        };

        //Start first test
        var nowTime = performance.now();
        callApiMethod({
            serverUrl: instances[0].serverUrl,
            method: ApiMethods.GetVersionInfo,
            success: cb.bind(this, nowTime, 0, true),
            failure: cb.bind(this, nowTime, 0, false)
        });
    }
    /**
     * Gets the user information associated with a server
     * @param {string} serverUrl - The URL of the PMA.core for which to fetch user information
     * @returns {Context~authenticationResponse} If no authentication has taken place for the particular server, null is returned, otherwise an object.
     */
    getUserInfo(serverUrl) {
        for (var url in _sessionList.get()) {
            if (_sessionList.get().hasOwnProperty(url) && url === serverUrl && _sessionList.get()[url]) {
                return _sessionList.get()[url];
            }
        }

        return null;
    }
    /**
     * Called when a session ID was successfully obtained
     * @callback Context~getSessionCallback
     * @param {string} sessionID
     */
    /**
     * Finds a session ID for the requested server, either by scanning the already cached session ids or by invoking one by one the available authentication providers, until a valid session ID is found
     * @param  {string} serverUrl - The URL of the PMA.core for which to fetch a session ID
     * @param  {Context~getSessionCallback} success
     * @param  {function} [failure]
     */
    getSession(serverUrl, success, failure) {
        // scan cached session IDs
        for (var url in _sessionList.get()) {
            if (_sessionList.get().hasOwnProperty(url) && url === serverUrl && _sessionList.get()[url]) {
                success(_sessionList.get()[url].SessionId);
                return;
            }
        }

        // no session ID found in cached list
        // fire all providers until one succeeds or all failed
        authenticateWithProvider.call(this, serverUrl, success, failure);
    }
    getImageInfo(serverUrl, pathOrUid, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetImageInfo,
            data: {
                pathOrUid: pathOrUid
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    getFiles(serverUrl, path, success, failure) {
        console.warn("Context.getFiles is deprecated please use Context.getSlides instead");
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetFiles,
            data: {
                path: path
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    /**
    * Gets all slides in a specified path
    * @param {Object} options - Parameters to pass to the GetSlides request
    * @param {string} options.serverUrl - The server url to get slides from
    * @param {string} options.path - The path to get slides from
    * @param {GetSlidesScope} options.scope - The search scope to use
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    getSlides(options) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.GetFiles,
            data: {
                path: options.path,
                scope: options.scope ? options.scope : 0
            },
            httpMethod: "GET",
            success: options.success,
            failure: options.failure
        });
    }
    getDirectories(serverUrl, path, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetDirectories,
            data: {
                path: path
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    getAnnotations(serverUrl, path, currentUserOnly, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetAnnotations,
            data: {
                pathOrUid: path,
                currentUserOnly: currentUserOnly
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    addAnnotation(serverUrl, path, classification, layerID, notes, geometry, color, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.AddAnnotation,
            data: {
                pathOrUid: path,
                classification: classification,
                layerID: layerID,
                notes: notes,
                geometry: geometry,
                color: color
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: success,
            failure: failure
        });
    }
    updateAnnotation(serverUrl, path, layerID, annotationID, notes, geometry, color, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.UpdateAnnotation,
            data: {
                pathOrUid: path,
                layerID: layerID,
                annotationID: annotationID,
                notes: notes,
                geometry: geometry,
                color: color
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: success,
            failure: failure
        });
    }
    saveAnnotations(serverUrl, path, added, updated, deleted, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.SaveAnnotations,
            data: {
                pathOrUid: path,
                added: added,
                updated: updated,
                deleted: deleted
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: success,
            failure: failure
        });
    }
    deleteAnnotation(serverUrl, path, layerID, annotationID, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.DeleteAnnotation,
            data: {
                pathOrUid: path,
                layerID: layerID,
                annotationID: annotationID
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: success,
            failure: failure
        });
    }
    getFormDefinitions(serverUrl, formIDs, rootDirectoryAlias, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetFormDefinitions,
            data: {
                formIDs: formIDs instanceof Array ? formIDs.join(",") : formIDs,
                rootDirectoryAlias: rootDirectoryAlias
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    getFormSubmissions(serverUrl, pathOrUids, formIDs, currentUserOnly, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.GetFormSubmissions,
            data: {
                pathOrUids: pathOrUids instanceof Array ? pathOrUids : [],
                formIDs: formIDs instanceof Array ? formIDs : [],
                currentUserOnly: currentUserOnly
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: success,
            failure: failure
        });
    }
    saveFormDefinition(serverUrl, definition, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.SaveFormDefinition,
            apiPath: "admin",
            contentType: "application/json",
            data: {
                definition: definition
            },
            httpMethod: "POST",
            success: success,
            failure: failure
        });
    }
    deleteFormDefinition(serverUrl, formID, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.DeleteFormDefinition,
            apiPath: "admin",
            data: {
                formID: formID
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    getVersionInfo(serverUrl, success, failure) {
        callApiMethod({
            serverUrl: serverUrl,
            method: ApiMethods.GetVersionInfo,
            success: function (http) {
                if (typeof success === "function") {
                    var response = parseJson(http.responseText);
                    success(response);
                }
            },
            failure: function (http) {
                if (typeof failure === "function") {
                    if (http.responseText && http.responseText.length !== 0) {
                        var response = parseJson(http.responseText);
                        failure(response);
                    }
                    else {
                        failure({ Message: Resources.translate("Get Version Info failed") });
                    }
                }
            }
        });
    }
    /**
    * Gets the events log from the server
    * @param {Object} options - Parameters to pass to the GetEvents request
    * @param {string} options.serverUrl - The server url to get events log
    * @param {number} options.page - The page to fetch
    * @param {number} options.pageSize - The page size to fetch
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    getEvents(options, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.GetEvents,
            apiPath: "admin",
            data: {
                page: options.page,
                pageSize: options.pageSize
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    deAuthenticate(serverUrl, success, failure) {
        var sessionId = null;
        if (_sessionList.get().hasOwnProperty(serverUrl) && _sessionList.get()[serverUrl]) {
            sessionId = _sessionList.get()[serverUrl].SessionId;
            if (!sessionId) {
                // nothing to do, no session ID cached, call success
                if (typeof success === "function") {
                    success();
                    return;
                }
            }
        }
        else {
            var cbFn = function (sId) {
                sessionId = sId;
            };

            // there is no cached sessionId for this server check for a sessionLogin provider
            for (var i = 0; i < this.authenticationProviders.length; i++) {
                if (this.authenticationProviders[i] instanceof PMA.UI.Authentication.SessionLogin) {
                    // check that this sessionLogin provider can handle the requested serverUrl
                    if (this.authenticationProviders[i].authenticate(serverUrl, cbFn, null)) {
                        break;
                    }

                    continue;
                }
            }
        }

        if (sessionId == null) {
            // cannot handle this serverUrl just return
            return;
        }

        _sessionList.set(serverUrl, null);

        callApiMethod({
            serverUrl: serverUrl,
            method: ApiMethods.DeAuthenticate,
            data: { sessionID: sessionId },
            success: function (http) {
                if (typeof success === "function") {
                    success();
                }
            },
            failure: function (http) {
                if (typeof failure === "function") {
                    if (http.responseText && http.responseText.length !== 0) {
                        var response = parseJson(http.responseText);
                        failure(response);
                    }
                    else {
                        failure();
                    }
                }
            }
        });
    }
    queryFilename(serverUrl, path, pattern, success, failure) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: serverUrl,
            method: ApiMethods.QueryFilename,
            apiPath: "query",
            data: {
                path: path,
                pattern: pattern
            },
            httpMethod: "GET",
            success: success,
            failure: failure
        });
    }
    /**
    * Gets all distinct values for a field in aform
    * @param {Object} options - Parameters to pass to the "distinct values" request
    * @param {string} options.serverUrl - The server url to use
    * @param {string} options.formId - The Form Id to use
    * @param {string} options.fieldId - The Field Id to get distinct values for
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    distinctValues(options) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.DistinctValues,
            apiPath: "query",
            data: {
                formID: options.formId,
                fieldID: options.fieldId
            },
            httpMethod: "GET",
            success: options.success,
            failure: options.failure
        });
    }
    /**
    * Gets information for all slides specified
    * @param {Object} options - Parameters to pass to the GetSlides request
    * @param {string} options.serverUrl - The server url to get slides from
    * @param {string[]} options.images - An array of image paths or uids to fetch information for
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    getImagesInfo(options) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.GetImagesInfo,
            apiPath: "api",
            data: {
                pathOrUids: options.images
            },
            contentType: "application/json",
            httpMethod: "POST",
            success: options.success,
            failure: options.failure
        });
    }
    /**
    * Gets slides that satisfy the specified expressions
    * @param {Object} options - Parameters to pass to the "distinct values" request
    * @param {string} options.serverUrl - The server url to use
    * @param {Object[]} options.expressions - The Expressions to use
    * @param {Number} options.expressions.FormID - The form Id for this expression
    * @param {Number} options.expressions.FieldID - The field Id for this expression
    * @param {Number} options.expressions.Operator - The Operator for this expression ( Equals = 0, LessThan = 1, LessThanOrEquals = 2, GreaterThan = 3, GreaterThanOrEquals = 4)
    * @param {Number} options.expressions.Value - The value to compare for this expression
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    metadata(options) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.Metadata,
            apiPath: "query",
            data: {
                expressions: options.expressions
            },
            httpMethod: "POST",
            contentType: "application/json",
            success: options.success,
            failure: options.failure
        });
    }

    /**
    * Checks if a PMA.start server is available
    * @param {function} success - The function to call when check succeded
    * @param {function} failure - The function to call when check failed
    **/
    checkPmaStartServer(success, failure) {

        callApiMethod({
            method: ApiMethods.GetVersionInfo,
            httpMethod: "GET",
            serverUrl: PmaStartUrl,
            success: function () {
                if (typeof callback === "function") {
                    success.call(this, true);
                }
            },
            failure: function () {
                if (typeof callback === "function") {
                    failure.call(this, false);
                }
            }
        });
    };

    /**
    * Returns the url to PMA.start CORS allow page
    * @return {string}
    **/
    getPmaStartCorsUrl() {
        var url = PmaStartUrl + "home/allowdomain/?host=" + document.location.origin;
        return url;
    };

    /**
    * Gets slides that satisfy the specified expressions
    * @param {Object} options - Parameters to pass to the "distinct values" request
    * @param {string} options.serverUrl - The server url to use
    * @param {string} options.name - The script name to run
    * @param {object} [options.params] - Optional script parameters to pass
    * @param {function} [options.success] - Called upon success
    * @param {function} [options.failure] - Called upon failure
    **/
    runScript(options) {
        callApiMethodWithAuthentication.call(
            this, {
            attemptCount: 0,
            serverUrl: options.serverUrl,
            method: ApiMethods.RunScripts,
            apiPath: "scripts",
            webapi: true,
            data: Object.assign({
                name: options.name,
            }, options.params),
            httpMethod: "GET",
            success: options.success,
            failure: options.failure
        });
    }

    // registers an event listener
    listen(eventName, callback) {
        if (!this.listeners.hasOwnProperty(eventName)) {
            console.error(eventName + " is not a valid event");
        }

        this.listeners[eventName].push(callback);
    }
    fireEvent(eventName, eventArgs) {
        if (!this.listeners.hasOwnProperty(eventName)) {
            console.error(eventName + " does not exist");
            return;
        }

        for (var i = 0, max = this.listeners[eventName].length; i < max; i++) {
            this.listeners[eventName][i](eventArgs);
        }
    }


    /**
    * A function called for progress on an upload
    * @callback Context~uploadProgressCallback
    * @param {String} name - The file name uploaded
    * @param {Number} progress - The progress percentage
    */

    /**
     * Uploads a file to the pma.core server
     * @param {Object} options 
     * @param {string} options.serverUrl - The server url to use
     * @param {string} options.targetPath - The virtual path to upload to
     * @param {File} options.file - The file object to upload
     * @param {Context~uploadProgressCallback} options.progress - A progress callback
     * @returns A promise to get the result of the upload
     */
    uploadFile(options) {
        let that = this;
        return new Promise(function (resolve, reject) {
            callApiMethodWithAuthentication.call(
                that, {
                attemptCount: 0,
                serverUrl: options.serverUrl,
                method: ApiMethods.Upload,
                apiPath: "transfer",
                webapi: true,
                data: {
                    "path": options.targetPath,
                    "files": [{
                        "isMain": true,
                        "length": options.file.size,
                        "path": options.file.name
                    }]
                },
                httpMethod: "POST",
                contentType: "application/json",
                success: function (sessionId, uploadContent) {
                    let method = "PUT";
                    if (!uploadContent.Urls) {
                        uploadContent.Urls = [];
                    }

                    if (uploadContent.Urls.length === 0) {
                        method = "POST";
                        uploadContent.Urls.push(`${options.serverUrl}transfer/Upload/${uploadContent.Id}?sessionId=${sessionId}&path=${encodeURIComponent(options.file.name)}`);
                    }

                    var fd = new FormData();
                    fd.append("file", options.file);
                    var xhr = new XMLHttpRequest();
                    xhr.open(method, uploadContent.Urls[0], true);

                    xhr.upload.onprogress = function (e) {
                        let progress = Math.ceil((e.loaded / e.total) * 100);
                        if (typeof options.progress === "function") {
                            options.progress(options.file.name, progress);
                        }
                    };

                    xhr.onload = function (evt) {
                        resolve(true);
                    };

                    xhr.onerror = function (evt) {
                        reject();
                    }

                    if (method === "POST") {
                        xhr.send(fd);
                    }
                    else {
                        xhr.send(options.file);
                    }
                },
                failure: function (f) { reject(f); }
            });
        });
    }
}

// private methods

// finds the first authentication provider that handles the requested server
// and calls it's authenticate method 
function authenticateWithProvider(serverUrl, success, failure) {
    // search all authentication providers apart from PromptLogin
    var i = 0;
    for (i = 0; i < this.authenticationProviders.length; i++) {
        if (this.authenticationProviders[i] instanceof PromptLogin) {
            continue;
        }

        if (this.authenticationProviders[i].authenticate(serverUrl, success, failure)) {
            return;
        }
    }

    // if none of the previous providers were able to provide a session, search for a PromptLogin and attempt to authenticate with it
    for (i = 0; i < this.authenticationProviders.length; i++) {
        if (this.authenticationProviders[i] instanceof PromptLogin) {
            if (this.authenticationProviders[i].authenticate(serverUrl, success, failure)) {
                return;
            }

            break;
        }
    }

    // no provider found, call failure
    if (typeof failure === "function") {
        failure({ Message: Resources.translate("Authentication failed at server {serverUrl}.", { serverUrl: serverUrl }) });
    }
}

/**
 * calls an API method that requires a session ID. This method will first attempt to acquire 
 * a session ID (possibly a cached one) and then call the requested method. 
 * If the method call fails because the call was unauthorized (so possible the session ID was not good),
 * the method will be called again for a second time, this time forcing an authentication before the
 * actual call.
 * @param  {object} options - The parameters to pass to the ajax request
 * @param  {number} options.attemptCount - Current attempt count
 * @param  {string} options.serverUrl - The URL of the server to send the request to
 * @param  {string} options.method - The API method to call
 * @param  {object} options.data - The data to send
 * @param  {string} options.httpMethod - The HTTP method to use
 * @param  {string} options.contentType - The content type of the request
 * @param  {function} options.success - Called upon success
 * @param  {function} options.failure - Called upon failure
 * @param {string} [options.apiPath="api"] - The API path to append to the server URL
 * @param {boolean} [options.webapi=false] - Whether the api call is a webapi call
 * @fires PMA.UI.Components.Events#SessionIdLoginFailed
 * @ignore
 */
function callApiMethodWithAuthentication(options) {
    var _this = this;

    if (!options.apiPath) {
        options.apiPath = "api";
    }

    _this.getSession(options.serverUrl,
        function (sessionId) {
            // we have a session ID, try to call the actual method
            options.data.sessionID = sessionId;

            callApiMethod({
                serverUrl: options.serverUrl,
                method: options.method,
                data: options.data,
                contentType: options.contentType,
                httpMethod: options.httpMethod,
                apiPath: options.apiPath,
                webapi: options.webapi,
                success: function (http) {
                    // the call succeeded, parse data and call success
                    if (typeof options.success === "function") {
                        var response = parseJson(http.responseText);
                        options.success(options.data.sessionID, response);
                    }
                },
                failure: function (http) {
                    // failed, clean up the session ID that was possibly cached for this server
                    _sessionList.set(options.serverUrl, null);

                    // if it's the first attempt, try again, otherwise fail for good
                    if (http.status == 0 && options.attemptCount === 0) {
                        options.attemptCount = 1;
                        callApiMethodWithAuthentication.call(_this, options);
                    }
                    else {
                        if (typeof options.failure === "function") {
                            if (http.responseText && http.responseText.length !== 0) {
                                try {
                                    var response = parseJson(http.responseText);
                                    options.failure(response);
                                    _this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
                                }
                                catch (ex) {
                                    options.failure(http.responseText);
                                    _this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
                                }
                            }
                            else {
                                options.failure({ Message: Resources.translate("Authentication failed") });
                                _this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
                            }
                        }
                    }
                }
            });
        },
        function (error) {
            if (!error.Message && error.Reason) {
                error.Message = error.Reason;
            }

            // session acquisition failed in the first place, so fail
            if (typeof options.failure === "function") {
                options.failure(error);
            }

            _this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
        });
}

// legacy alias because of typo
Context.prototype.GetImagesInfo = Context.prototype.getImagesInfo;