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;