/**
* @fileoverview Client layer for DS. The DS stack will roughly consist of
* Layer 1: GUI, Layer 2: Client, Layer 3: Server
*
* NOTE: All public/exported functions in this module should implement runtime
* type checking for arguments to allow easier debugging when calling
* from uncompiled code.
*
* @author anders.rejdebrant@spark-vision.com (Anders Rejdebrant)
*/
goog.provide('spv.ds.DsClient');
goog.require('spv.assert');
goog.require('spv.ds.Config');
goog.require('spv.ds.ConfigLoadResult');
goog.require('spv.ds.Disclaimer')
goog.require('spv.ds.DsClientSettings');
goog.require('spv.ds.FacebookShareData');
goog.require('spv.ds.FreshConfig');
goog.require('spv.ds.ImageParams');
goog.require('spv.ds.ImageResult');
goog.require('spv.ds.ImageSerieInfo');
goog.require('spv.ds.IntroPage');
goog.require('spv.ds.MenuQuery');
goog.require('spv.ds.PresentationStructureQuery');
goog.require('spv.ds.SavedConfig');
goog.require('spv.ds.SessionStateUpdate');
goog.require('spv.ds.Suggestion');
goog.require('spv.ds.Summary');
goog.require('spv.ds.assert');
goog.require('spv.ds.impl.GoalStateResolver');
goog.require('spv.ds.impl.IPPNovaAdaptor');
goog.require('spv.ds.impl.IDsProtocol');
goog.require('spv.ds.impl.SessionInitData');
goog.require('spv.ds.impl.SessionState');
goog.require('spv.ds.ipprot_nova.input.BopReadMoreMode');
/**
* DsClient provides a layer of abstraction between GUI code and
* session-/protocol-handling.
*
* The configurator system is stateful. Session state is shared between server
* and client, this is a source of complexity and can hopefully be removed at
* some point in the future. DsClient provides synchronization of session state
* between server and client by centralizing management of the shared state
* inside this class.
*
* Member functions are stateful if the member function name begins with
* "session". Other member functions are state- and session-less and can be
* called at any time, in any order.
*
* Most session dependent functions also require a loaded configuration.
* Preconditions should be documented for each member function in this class.
*
* The configurator system can be simplified as the following three phases:
* A. The user finds the configurator application on the web or in the showroom.
* B. The user choose and change the product components (called items) until
* the user is satisfied or leaved the application.
* C. The user saves the configuration for a later time or choose to place an
* order or request a sales contact for the built configuration.
*
* Technical usage example 1, Starting a new configuration session and
* interacting with the user. (Step-by-step example of phase B):
*
* 1. Construct an instance of spv.ds.Client
* 2. Call sessionInit
* 3. Call getIntroPage to retrieve the available start configurations
* 4. Generate images for the start configurations using getConfigImage
* 5. Display the available start configuration images to the user using the
* appropriate techniques for the GUI implementation.
* 6. The user chooses a start configuration
* 7. Send that start configuration to sessionLoadConfig
* 8. On success, call sessionGetRootMenuItems and sessionGetImage
* 9. Populate the GUI root menu with menu items and wait for the image response
* 10. On image success, load and display the configuration image
* 11. The user makes a choice about which menu to open
* 12. Call sessionToggleItem for the user chosen MenuItem
* (both menus and product components are MenuItems)
* 13. On success, call sessionGetStateUpdate to populate the currently open
* menu with menu items.
* 14. The user clicks an item in the currently open menu, send that MenuItem to
* sessionToggleItem and handle response.
* 15. On success, update the currently open menu with new MenuItems (most menu
* items will usually be unchanged, but some may change state and some may
* disappear while others may be added). Call sessionGetImage to get the new
* configuration image and wait for response.
* 16. On image success, load and display the configuration image
* 17. The user clicks a new item and the sessionToggleItem cycle continues
* until the user closes the application or decides to place an order for
* the built configuration. (this step is customized action for each
* configurator application, eg. cars and bathroom products require
* different types of shopping cart solutions)
*
*
*
*
* @export
* @constructor
* @param {!string} service_url
* @param {!spv.ds.DsClientSettings=} in_settings
*/
spv.ds.DsClient = function(service_url, in_settings)
{
// Default settings.
var default_settings = new spv.ds.DsClientSettings();
default_settings.use_jsonp = false;
default_settings.debug_print = false;
/** @type {!spv.ds.DsClientSettings} */
var settings = in_settings ? default_settings.overloadWith( in_settings ) : default_settings;
var valid_settings = new spv.ds.DsClientValidatedSettings( settings );
/**
* @protected
* @type {spv.ds.impl.IDsProtocol}
*/
this._protocol = new spv.ds.impl.IPPNovaAdaptor(service_url, valid_settings.use_jsonp);
/**
* @private
* @type {boolean}
*/
this._debug_print = valid_settings.debug_print;
/**
* @private
* @type {boolean}
*/
this._session_is_initialized = false;
/**
* @private
* @type {string}
*/
this._language = "";
/**
* @private
* @type {null|string}
*/
this._session_id = null;
/**
* @protected
* @type {spv.ds.impl.SessionState}
*/
this._session_state = null;
/**
* @private
* @type {Array.<Function>}
*/
this._session_request_queue = [];
/**
* @private
* @type {boolean}
*/
this._session_request_in_progress = false;
/**
* @private
* @type {?function( Error )}
*/
this._on_unauthorized_session = valid_settings.on_unauthorized_session;
/**
* @private
* @type {?function( Error )}
*/
this._on_service_unavailable = valid_settings.on_service_unavailable;
/**
* @private
* @type {?function( Error )}
*/
this._on_service_failure = valid_settings.on_service_failure;
/**
* @private
* @type {number}
*/
this._session_ttl = valid_settings.session_ttl;
/**
* @private
* @type {string}
*/
this._origin = valid_settings.origin;
/**
* @private
* @type {string}
*/
this._initial_category = valid_settings.initial_category;
/**
* @private
* @type {string}
*/
this._item_interpreter_options = valid_settings.item_interpreter_options;
};
/**
* Get the session id or throw if null
*
* @export
* @return {string}
*/
spv.ds.DsClient.prototype.getSessionId = function()
{
if (null == this._session_id)
{
throw new Error('Session id is null, session is not initialized.');
}
return this._session_id;
};
/**
* Create a ImageParams instance with default values, however, defaults are
* likely not good enough for most contexts.
*
* Preconditions: None.
*
* @export
* @return {spv.ds.ImageParams}
*/
spv.ds.DsClient.prototype.constructDefaultImageParams = function()
{
return new spv.ds.ImageParams();
};
/**
* Requests an image for a serialized configuration.
*
* Preconditions: None.
*
* @export
* @param {spv.ds.Config} conf
* @param {spv.ds.ImageParams} img_params
* @param {function(spv.ds.ImageResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getConfigImage = function(
conf,
img_params,
on_success,
on_failure)
{
spv.ds.assert.isConfig(conf, 'conf');
spv.ds.assert.isImageParams(img_params, 'img_params');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.getConfigImage(
conf,
img_params,
on_success,
this.errorHandler(on_failure) );
};
/**
* Requests the "Intro page" data including available start-configurations,
* typically one config for each car model.
*
* Preconditions: None.
*
* @export
* @param {function(spv.ds.IntroPage)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getIntroPage = function(on_success, on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.getIntroPage(
on_success,
this.errorHandler(on_failure) );
};
/**
* @param {string} item_id
* @param {string} model
* @param {string} category
* @param {function(Array.<spv.ds.ReadMoreResource>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getReadMore = function(
item_id,
model,
category,
language,
on_success,
on_failure)
{
this._protocol.getReadMore(
item_id,
model,
category,
language,
on_success,
on_failure );
};
/**
* @param {Array<string>} item_ids
* @param {Array<string>} resource_keys
* @param {string} category
* @param {string} model
* @param {string} language
* @param {function(Object<string,spv.ds.ItemResources>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getItemResources = function(
item_ids,
resource_keys,
category,
model,
language,
on_success,
on_failure)
{
spv.assert.isArray(item_ids, 'item_ids');
spv.assert.isArray(resource_keys, 'resource_keys');
spv.assert.isString(category, 'category');
spv.assert.isString(model, 'model');
spv.assert.isString(language, 'language');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.getItemResources(
item_ids,
resource_keys,
category,
model,
language,
on_success,
on_failure);
}
/**
* Gets all configurations stored in the database by the given username.
* This is a heavy operation and is included here for utility purposes.
*
* @export
* @param {string} username
* @param {function(Array.<spv.ds.SavedConfig>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getUserConfigList = function(
username,
on_success,
on_failure )
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.getUserConfigList(
username,
on_success,
this.errorHandler(on_failure) );
};
/**
* @export
* @param {Array.<string>} group_ids
* @param {function()} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionAddGuiGroupSubscriptions = function(
group_ids,
on_success,
on_failure )
{
var client = this;
var on_failure_wrapper = this.constructFailureWrapperForQueue(
this.errorHandler(on_failure) );
function on_success_wrapper()
{
on_success();
client.sessionDequeueNextRequest();
}
function session_request()
{
client._protocol.sessionAddGuiGroupSubscriptions(
client.getSessionId(),
group_ids,
on_success_wrapper,
on_failure_wrapper );
}
this.sessionEnqueueRequest(session_request);
};
/**
* @export
* @param {function()} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionClearGuiGroupSubscriptions = function(
on_success,
on_failure )
{
var client = this;
var on_failure_wrapper = this.constructFailureWrapperForQueue(
this.errorHandler(on_failure) );
function on_success_wrapper()
{
on_success();
client.sessionDequeueNextRequest();
}
function session_request()
{
client._protocol.sessionClearGuiGroupSubscriptions(
client.getSessionId(),
on_success_wrapper,
on_failure_wrapper );
}
this.sessionEnqueueRequest(session_request);
};
/**
* Apply a configuration change suggestion on the current configuration.
*
* Preconditions: Initialized session, loaded configuration, caller code has a
* valid instance of spv.ds.Suggestion retrieved by calling
* sessionToggleItem, no requests that modify the session
* configuration has been dispatched between suggestion
* construction and the call to this function.
*
* @export
* @param {spv.ds.Suggestion} suggestion
* The suggestion to be applied to the current configuration.
* @param {Function} on_success
* Called when the suggestion has been successfully applied.
* @param {function(Error)} on_failure
* Called on any kind of failure.
*/
spv.ds.DsClient.prototype.sessionApplySuggestion = function(
suggestion,
on_success,
on_failure)
{
spv.ds.assert.isSuggestion(suggestion, 'suggestion');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSessionWithConfig();
var that = this;
// TODO: DSClient sessionBip (@protected), since there's a bit of code duplication here.
/** @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data */
var on_success_wrapper = function(ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that._protocol.sessionBip(
that.getSessionId(),
'',
suggestion.internal_data,
that.getLanguage(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Create a pdf using current session state and returns the pdf url through a
* callback if a pdf was generated successfully.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {function(string)} on_success Callback taking the generated pdf url
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGeneratePdf = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/** @param {string} pdf_url */
var on_success_wrapper = function(pdf_url)
{
on_success(pdf_url);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSessionWithConfig();
that._protocol.sessionGeneratePdf(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Get the root menu items for the current configuration
*
* Preconditions: Initialized session, loaded config.
*
* @export
* @return {Array.<spv.ds.MenuItem>}
*/
spv.ds.DsClient.prototype.sessionGetRootMenuItems = function()
{
this.assertInitializedSessionWithConfig();
return this._session_state.getRootMenuItems();
};
/**
* Requests the current session config
*
* Available encodings are:
* "" : internal encoding, only for debug
*
* Preconditions: Initialized session, loaded config.
*
* @export
* @param {string} encoding
* @param {function(spv.ds.Config)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetConfig = function(
encoding,
on_success,
on_failure)
{
spv.assert.isString(encoding, 'encoding');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/** @param {spv.ds.Config} config */
var on_success_wrapper = function(config)
{
on_success(config);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSessionWithConfig();
that._protocol.sessionGetConfig(
that.getSessionId(),
encoding,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Get the saved configs for the logged in user
*
* Preconditions: Initialized session, logged in user.
*
* @export
* @param {function(Array.<string>)} on_success
* A callback taking an array of names of saved configurations.
* The configuration names can be used to remove or load configs for the
* logged in user by sessionRemoveConfigByName and
* sessionLoadConfigByName.
*
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetConfigNames = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/** @param {Array.<string>} config_names */
var on_success_wrapper = function(config_names)
{
on_success(config_names);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionGetConfigNames(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Get 'short_text' text for item_id or empty string on failure
*
* Texts are configuration dependent.
* In practice updating texts is only needed after loading a configuration, not
* when simply toggling an item in the configuration.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {string} item_id
* @return {string}
*/
spv.ds.DsClient.prototype.sessionGetShortText = function(item_id)
{
spv.assert.isNonEmptyString(item_id, 'item_id');
this.assertInitializedSessionWithConfig();
return this._session_state.getShortText(item_id);
};
/**
* Get 'medium_text' text for item_id or empty string on failure
*
* Texts are configuration dependent.
* In practice updating texts is only needed after loading a configuration, not
* when simply toggling an item in the configuration.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {string} item_id
* @return {string}
*/
spv.ds.DsClient.prototype.sessionGetMediumText = function(item_id)
{
spv.assert.isNonEmptyString(item_id, 'item_id');
this.assertInitializedSessionWithConfig();
return this._session_state.getMediumText(item_id);
};
/**
* Get 'long_text' text for item_id or empty string on failure
*
* Texts are configuration dependent.
* In practice updating texts is only needed after loading a configuration, not
* when simply toggling an item in the configuration.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {string} item_id
* @return {string}
*/
spv.ds.DsClient.prototype.sessionGetLongText = function(item_id)
{
spv.assert.isNonEmptyString(item_id, 'item_id');
this.assertInitializedSessionWithConfig();
return this._session_state.getLongText(item_id);
};
/**
* Gets the disclaimer to be displayed when the configurator is started.
*
* @export
* @return {spv.ds.Disclaimer}
*/
spv.ds.DsClient.prototype.sessionGetIntroDisclaimer = function()
{
var state_update = this.sessionGetStateUpdate();
var disclaimer_id = "INTRO_DISCLAIMER";
return new spv.ds.Disclaimer( this.sessionGetShortText( disclaimer_id ) );
};
/**
* Get the disclaimer specific for a model.
*
* If no model id is passed, it'll default to the active configuration's model.
*
* @export
* @param {string=} model_id
* @return {spv.ds.Disclaimer}
*/
spv.ds.DsClient.prototype.sessionGetModelDisclaimer = function( model_id )
{
var id = model_id || this.sessionGetStateUpdate().model_id;
var disclaimer_id = id + "_DISCLAIMER";
return new spv.ds.Disclaimer( this.sessionGetShortText( disclaimer_id ) );
};
/**
* Get base urls needed for most HTTP GET paths to images
*
* Preconditions: Initialized session.
*
* @export
* @return {spv.ds.UrlSettings}
*/
spv.ds.DsClient.prototype.sessionGetUrlSettings = function()
{
this.assertInitializedSession();
return this._session_state.getUrlSettings();
};
/**
* Get image serie info for the current configuration
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @return {Array.<spv.ds.ImageSerieInfo>}
*/
spv.ds.DsClient.prototype.sessionGetImageSerieInfos = function()
{
this.assertInitializedSessionWithConfig();
return this._session_state.getImageSerieInfos();
};
/**
* Get complete item info for items by item id
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {Array.<string>} item_ids
* @param {function(Array.<spv.ds.MenuItem>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetItemInfo = function(
item_ids,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSessionWithConfig();
this._protocol.sessionGetItemInfo(
this.getSessionId(),
item_ids,
this.getLanguage(),
on_success,
this.errorHandler(on_failure) );
};
/**
* Gets an URL to a zipfile containing the images.
*
* @export
* @param {number} serie_index
* @param {number} frame_index
* @param {string} file_type
* @param {boolean} separate_background
* @param {boolean} separate_shadow
* @param {boolean} all_frames
* @param {boolean} use_hd
* @param {number} image_width
* @param {number} image_height
* @param {function(string)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetMediaKit = function(
serie_index,
frame_index,
file_type,
separate_background,
separate_shadow,
all_frames,
use_hd,
image_width,
image_height,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.sessionGetMediaKit(
this.getSessionId(),
serie_index,
frame_index,
file_type,
separate_background,
separate_shadow,
all_frames,
use_hd,
image_width,
image_height,
on_success,
this.errorHandler(on_failure));
};
/**
* Requests the text, images etc used for facebook sharing.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {function(spv.ds.FacebookShareData)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetFacebookShareData = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSessionWithConfig();
this._protocol.sessionGetFacebookShareData(
this.getSessionId(),
on_success,
this.errorHandler(on_failure) );
};
/**
* Requests the most recently built car configurations.
*
* @export
* @param {number} limit Max number of configs in result
* @param {number} scale_width
* @param {number} scale_height
* @param {function(Array.<spv.ds.FreshConfig>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.getFreshConfigs = function(
limit,
scale_width,
scale_height,
on_success,
on_failure)
{
spv.assert.isNumber(limit, 'limit');
spv.assert.isNumber(scale_width, 'scale_width');
spv.assert.isNumber(scale_height, 'scale_height');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.getFreshConfigs(
limit,
scale_width,
scale_height,
on_success,
this.errorHandler(on_failure) );
};
/**
* Requests the most recently built car configurations.
*
* @deprecated
* @export
* @param {number} limit Max number of configs in result
* @param {number} scale_width
* @param {number} scale_height
* @param {function(Array.<spv.ds.FreshConfig>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetFreshConfigs = function(
limit,
scale_width,
scale_height,
on_success,
on_failure)
{
spv.assert.isNumber(limit, 'limit');
spv.assert.isNumber(scale_width, 'scale_width');
spv.assert.isNumber(scale_height, 'scale_height');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
this._protocol.sessionGetFreshConfigs(
this.getSessionId(),
limit,
scale_width,
scale_height,
on_success,
this.errorHandler(on_failure));
};
/**
* Builds an image for the current configuration state in this session.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {spv.ds.ImageParams} img_params
* @param {function(spv.ds.ImageResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetImage = function(
img_params,
on_success,
on_failure)
{
spv.ds.assert.isImageParams(img_params, 'img_params');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/** @param {spv.ds.ImageResult} img_result */
var on_success_wrapper = function(img_result)
{
on_success(img_result);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSessionWithConfig();
that._protocol.sessionGetImage(
that.getSessionId(),
img_params,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Get the summary for the current session and configuration
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @param {function(spv.ds.Summary)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetSummary = function(on_success, on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSessionWithConfig();
this._protocol.sessionGetSummary(
this.getSessionId(),
on_success,
this.errorHandler(on_failure) );
};
/**
* Returns the auxiliary data arriving with the latest bop.
*
* Preconditions: Initialized session, loaded configuration.
*
* @protected
* @return {Object.<string, Object>}
*/
spv.ds.DsClient.prototype.sessionGetAuxiliaryData = function()
{
this.assertInitializedSessionWithConfig();
return this._session_state.getAuxiliaryData();
};
/**
* Get the last state update. Call this function on successful load of configs
* or on successful sessionToggleItem.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @return {spv.ds.SessionStateUpdate}
*/
spv.ds.DsClient.prototype.sessionGetStateUpdate = function()
{
this.assertInitializedSessionWithConfig();
return this._session_state.getUpdate();
};
/**
* Requests an image for a saved configuration
*
* Preconditions: Initialized session, logged in user.
*
* @export
* @param {string} config_name
* @param {spv.ds.ImageParams} img_params
* @param {function(spv.ds.ImageResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetUserConfigImage = function(
config_name,
img_params,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(config_name, 'config_name');
spv.ds.assert.isImageParams(img_params, 'img_params');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/** @param {spv.ds.ImageResult} img_result */
var on_success_wrapper = function(img_result)
{
on_success(img_result);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionGetUserConfigImage(
that.getSessionId(),
config_name,
img_params,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* @param {string} filter_name
* @param {spv.ds.PresentationStructureQuery} query
* @param {function(spv.ds.PresentationStructure)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetPresentationStructure = function(
filter_name,
query,
on_success,
on_failure )
{
var that = this;
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
/** @param {spv.ds.PresentationStructure} presentation_structure */
function on_success_wrapper( presentation_structure )
{
that.sessionDequeueNextRequest();
on_success( presentation_structure );
}
this.sessionEnqueueRequest(session_request);
function session_request()
{
that._protocol.sessionGetPresentationStructure(
that.getSessionId(),
filter_name,
query,
on_success_wrapper,
on_failure_wrapper );
}
}
/**
* Initialize session.
* This must be called before calling any other member function with "session".
* For most other session calls a configuration needs to be loaded as well.
*
* This is not done in the constructor in order to allow state-/session-less
* calls without initializing a session.
*
* Preconditions: None.
*
* @export
* @param {string} menu_query
* @param {spv.ds.ipprot_nova.input.BopReadMoreMode} readmore_mode
* @param {function(spv.ds.IntroPage)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionInit = function(
menu_query,
readmore_mode,
on_success,
on_failure)
{
spv.assert.isString(menu_query, 'menu_query');
spv.assert.isNonEmptyString(readmore_mode, 'readmore_mode');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/**
* @param {spv.ds.impl.SessionInitData} init_data
*/
var on_success_wrapper = function(init_data)
{
that._session_state = new spv.ds.impl.SessionState(init_data);
that._session_is_initialized = true;
that._session_id = init_data.session_id;
on_success(init_data.intro_page);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.sessionResetRequestQueue();
that._protocol.sessionCreate(
menu_query,
readmore_mode,
that.getLanguage(),
that._session_ttl,
that._origin,
that._initial_category,
that._item_interpreter_options,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Initialize with existing session.
* Used instead of sessionInit.
*
* Preconditions: Client not initialized.
*
* @export
* @param {string} session_id
* @param {function(spv.ds.IntroPage)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionResume = function(
session_id,
on_success,
on_failure)
{
spv.assert.isString( session_id, 'session_id' );
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
if (this._session_is_initialized)
{
throw new Error("Cannot resume session when the client is already initialized.");
}
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.sessionResetRequestQueue();
that._protocol.sessionResume(
session_id,
that.getLanguage(),
on_success_wrapper,
on_failure_wrapper );
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
/**
* @param {spv.ds.impl.SessionInitData} init_data
*/
function on_success_wrapper(init_data)
{
that._session_state = new spv.ds.impl.SessionState(init_data);
that._session_is_initialized = true;
that._session_id = init_data.session_id;
on_success(init_data.intro_page);
that.sessionDequeueNextRequest();
}
};
/**
* Loads a configuration into the current session and replaces to current
* configuration state.
*
* Preconditions: Initialized session.
*
* @export
* @param {spv.ds.Config} config
* @param {function(spv.ds.ConfigLoadResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionLoadConfig = function(
config,
on_success,
on_failure)
{
spv.ds.assert.isConfig(config, 'config');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/**
* @param {spv.ds.ConfigLoadResult} result
* @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data
*/
var on_success_wrapper = function(result, ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success(result);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionLoadConfig(
that.getSessionId(),
config,
that.getLanguage(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Loads a configuration into the current session and replaces to current
* configuration state. The config is referenced by it's name retrieved from
* sessionGetConfigNames.
*
* Preconditions: Initialized session, logged in user.
*
* @export
* @param {string} config_name
* @param {function(spv.ds.ConfigLoadResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionLoadConfigByName = function(
config_name,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(config_name, 'config_name');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/**
* @param {spv.ds.ConfigLoadResult} result
* @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data
*/
var on_success_wrapper = function(result, ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success(result);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionLoadConfigByName(
that.getSessionId(),
config_name,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Loads a configuration from the "public" database into the current session
* replacing the current configuration state.
*
* "Public Configs" are anonymous configurations saved without any user
* information that can be loaded by a unique key.
*
* Preconditions: Initialized session.
*
* @export
* @param {string} public_config_id
* @param {function(spv.ds.ConfigLoadResult)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionLoadPublicConfig = function(
public_config_id,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(public_config_id, 'public_config_id');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
/**
* @param {spv.ds.ConfigLoadResult} result
* @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data
*/
var on_success_wrapper = function(result, ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success(result);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionLoadPublicConfig(
that.getSessionId(),
public_config_id,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Save the current configuration for the logged in user as a public config.
* The generated name is returned as a string to the on_success function.
*
* Preconditions: Initialized session, loaded configuration, logged in user.
*
* @export
* @param {function(string)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.generatePublicConfig = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function(conf_name)
{
on_success(conf_name);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSessionWithConfig();
that._protocol.sessionGeneratePublicConfig(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* User login
* Password may be required depending on backend settings.
*
* May create the user if it doesn't exist, depending on backend settings.
*
* success and failure callbacks are always at the end.
*
* sessionLogin( session_id, name, password, success, failure );
* sessionLogin( session_id, name, success, failure );
*
* Preconditions: Initialized session.
*
* @export
* @param {string} user_name
* @param {string|Function} password_or_success
* @param {Function|function(Error)} success_or_failure
* @param {function(Error)=} failure
*/
spv.ds.DsClient.prototype.sessionLogin = function(
user_name,
password_or_success,
success_or_failure,
failure )
{
/** @type {Function} **/
var on_success;
/** @type {function(Error)} **/
var on_failure;
/** @type {string} **/
var password;
/* Argument swapping to make password an optional parameter.
* (For backwards compatibility)
*/
if( arguments.length === 3 )
{
password = "";
on_success = /** @type {Function} */ (password_or_success);
on_failure = /** @type {function(Error)} */ (success_or_failure);
}
else if( arguments.length === 4 )
{
password = /** @type {string} */ (password_or_success);
on_success = /** @type {Function} */ (success_or_failure);
on_failure = /** @type {function(Error)} */ (failure);
}
spv.assert.isNonEmptyString(user_name, 'user_name');
spv.assert.isString(password, 'password');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionLogin(
that.getSessionId(),
user_name,
password,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Logout the user from the session.
*
* @export
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionLogout = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionLogout(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Create a new user in the user database.
*
* May not be required to create a user, depending on backend settings.
*
* @export
* @param {string} user_name
* @param {string} password
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionCreateUser = function(
user_name,
password,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(user_name, 'user_name');
spv.assert.isNonEmptyString(password, 'password');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that.assertInitializedSession();
that._protocol.sessionCreateUser(
that.getSessionId(),
user_name,
password,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
}
}
/**
* Check with backend if the session has a logged in user.
*
* Preconditions: Initialized session.
*
* @export
* @param {function(boolean)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionHasLoggedInUser = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function(b)
{
on_success(b);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that.assertInitializedSession();
that._protocol.sessionHasLoggedInUser(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
}
};
/**
* Precondition: Initialized session
*
* @export
* @param {string} password_form_url
* @param {string} user_name
* @param {string} language
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionSendResetPasswordMail = function(
password_form_url,
user_name,
language,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(password_form_url, 'password_form_url');
spv.assert.isNonEmptyString(user_name, 'user_name');
spv.assert.isNonEmptyString(language, 'language');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionSendResetPasswordMail(
that.getSessionId(),
password_form_url,
user_name,
language,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
}
/**
* Resets user password.
*
* Token is retrieved via email, sent by sessionSendResetPasswordMail.
*
* @export
* @param {string} token
* @param {string} new_password
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.resetPassword = function(
token,
new_password,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(token, 'token');
spv.assert.isNonEmptyString(new_password, 'new_password');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this._protocol.resetPassword( token, new_password, on_success, on_failure );
}
/**
* Remove a saved configuration for the logged in user
*
* Preconditions: Initialized session, logged in user.
*
* @export
* @param {string} config_name Name retrieved by sessionGetConfigNames
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionRemoveConfigByName = function(
config_name,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(config_name, 'config_name');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionRemoveConfigByName(
that.getSessionId(),
config_name,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Save the current configuration for the logged in user
*
* Preconditions: Initialized session, loaded configuration, logged in user.
*
* @export
* @param {string} config_name
* @param {boolean} allow_overwrite
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionSaveConfigByName = function(
config_name,
allow_overwrite,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(config_name, 'config_name');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSessionWithConfig();
that._protocol.sessionSaveConfigByName(
that.getSessionId(),
config_name,
allow_overwrite,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Choose which collection of price rules to use for the current session
*
* Preconditions: Initialized session.
*
* @export
* @param {string} price_localization_id
* @param {Function} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionSetPriceLocalization = function(
price_localization_id,
on_success,
on_failure)
{
spv.assert.isNonEmptyString(price_localization_id, 'price_localization_id');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that.assertInitializedSession();
that._protocol.sessionSetPriceLocalization(
that.getSessionId(),
price_localization_id,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* @param {string} language
* @param {function()} on_success
* @param {function()} on_failure
*/
spv.ds.DsClient.prototype.setLanguage = function( language, on_success, on_failure )
{
this._language = language;
on_success();
};
/**
* @export
* @return {string}
*/
spv.ds.DsClient.prototype.getLanguage = function()
{
return this._language;
}
/**
* Try to change item state for one item in the current configuration.
* An item has two states, On or Off, eg. an item is part of the current
* configuration or it is not. The result of this operation can only be
* determined by the server. Any call to this function may result in suggestions
* being returned through the callback on_suggestions. If suggestions
*
* Use DsClient.setLanguage in order to specify language. Leaving it undefined
* will cause the backend to resolve a preferred instead.
*
* Preconditions: Initialized session, loaded configuration, caller code has a
* valid instance of spv.ds.MenuItem.
*
* @export
* @param {spv.ds.MenuItem} menu_item
* Menu item to toggle on or off. User code should not modify or
* construct instances of MenuItem, only pass MenuItem instances returned
* by this class.
*
* @param {Function} on_success
* Will be called on toggle success if toggle has no important side
* effects. Caller code should call sessionGetStateUpdate on this object
* to get the latest session state update.
*
* @param {function(Array.<spv.ds.Suggestion>, spv.ds.BipAttemptResultCode, string)} on_suggestions
* Will be called if item toggle has important side effects or if
* relevant alternative configuration states has been found.
*
* @param {function(Error)} on_failure
* Called on any kind of failure.
*/
spv.ds.DsClient.prototype.sessionToggleItem = function(
menu_item,
on_success,
on_suggestions,
on_failure)
{
spv.ds.assert.isMenuItem(menu_item, 'menu_item');
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_suggestions, 'on_suggestions');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSessionWithConfig();
var that = this;
var category_id = menu_item.category;
var item_id = menu_item.id;
/** @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data */
var on_success_wrapper = function(ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success();
that.sessionDequeueNextRequest();
};
var on_resolvers = function(goal_resolvers, attempt_code, resolvers_display_text)
{
var suggestions = that.constructSuggestions(goal_resolvers);
on_suggestions(suggestions, attempt_code, resolvers_display_text);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that._protocol.sessionToggleItem(
that.getSessionId(),
item_id,
category_id,
that.getLanguage(),
on_success_wrapper,
on_resolvers,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
*
* @param {function(Array.<spv.ds.Suggestion>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionGetInitialConsequenceOfChange = function(
on_success,
on_failure)
{
var that = this;
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request() {
that._protocol.sessionGetInitialConsequenceOfChange(
that.getSessionId(),
that.getLanguage(),
wrapped_on_success,
on_failure_wrapper
);
}
/** @param {Array.<spv.ds.impl.GoalStateResolver>} goal_resolvers */
function wrapped_on_success(goal_resolvers) {
var suggestions = that.constructSuggestions(goal_resolvers);
on_success(suggestions);
that.sessionDequeueNextRequest();
}
};
/**
* Call this function to keep the session alive
*
* Preconditions: Initialized session.
*
* @export
* @param {function()} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionSendHeartBeat = function(on_success, on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
/** @param {string} unimportant_data */
var on_success_wrapper = function(unimportant_data)
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that._protocol.sessionSendHeartBeat(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Preconditions: Initialized session.
*
* @param {number} config_id
* @param {string} config_description
* @param {Object.<string,string>} user_config_aux
* @param {Object.<string,string>} config_storage_aux
* @param {function()} on_success
* @param {function(Error)} on_failure
*
* @export
*/
spv.ds.DsClient.prototype.userConfigV2Save = function(
config_id,
config_description,
user_config_aux,
config_storage_aux,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.userConfigV2Save(
that.getSessionId(),
config_id,
config_description,
user_config_aux,
config_storage_aux,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
}
/**
* Preconditions: Initialized session.
*
* @param {string} config_name
* @param {string} config_description
* @param {Object.<string,string>} user_config_aux
* @param {Object.<string,string>} config_storage_aux
* @param {function(number)} on_success
* @param {function(Error)} on_failure
*
* @export
*/
spv.ds.DsClient.prototype.userConfigV2SaveAs = function(
config_name,
config_description,
user_config_aux,
config_storage_aux,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_success_wrapper = function(configId)
{
on_success(configId);
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.userConfigV2SaveAs(
that.getSessionId(),
config_name,
config_description,
user_config_aux,
config_storage_aux,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
}
/**
* Preconditions: Initialized session.
*
* @param {number} config_id
* @param {function()} on_success
* @param {function(Error)} on_failure
*
* @export
*/
spv.ds.DsClient.prototype.userConfigV2Delete = function(
config_id,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_success_wrapper = function()
{
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.userConfigV2Delete(
that.getSessionId(),
config_id,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
}
/**
* Preconditions: Initialized session.
*
* @export
*
* @param {function(Array<spv.ds.UserConfigInfo>)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.userConfigV2List = function(
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
/**
* @param {Array<spv.ds.UserConfigInfo>} list
*/
var on_success_wrapper = function( list )
{
on_success( list );
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.userConfigV2List(
that.getSessionId(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
}
/**
* Preconditions: Initialized session.
*
* The callback on_coc is optional. If provided, it will be called independently
* of on_success and on_failure. I.e. if there is coc and the load was successful,
* both on_success and on_coc will be called.
*
* @export
*
* @param {number} config_id
* @param {function()} on_success
* @param {function(Error)} on_failure
* @param {function(Array.<spv.ds.Suggestion>=)} on_coc (optional)
*/
spv.ds.DsClient.prototype.userConfigV2Load = function(
config_id,
on_success,
on_failure,
on_coc)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.userConfigV2Load(
that.getSessionId(),
config_id,
on_config_loaded,
on_failure_wrapper
);
}
catch (err)
{
on_failure_wrapper(err);
}
}
function on_config_loaded()
{
if(on_coc) {
that.sessionGetInitialConsequenceOfChange(
function(suggestions) {
bopit(function() {
on_coc(suggestions);
on_success();
});
},
on_failure_wrapper
);
}
else {
bopit(on_success);
}
that.sessionDequeueNextRequest();
}
function bopit(on_success) {
that.sessionUpdateBop(
on_success,
on_failure_wrapper
);
}
};
/**
* @param {string} public_config_id
* @param {function()} on_success
* @param {function(Error)} on_failure
* @param {function(Array.<spv.ds.Suggestion>=)} on_coc (optional)
*/
spv.ds.DsClient.prototype.publicConfigV2Load = function(
public_config_id,
on_success,
on_failure,
on_coc
){
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.publicConfigV2Load(
that.getSessionId(),
public_config_id,
on_config_loaded,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
}
function on_config_loaded()
{
if(on_coc) {
that.sessionGetInitialConsequenceOfChange(
function(suggestions) {
bopit(function() {
on_coc(suggestions);
on_success();
});
},
on_failure_wrapper
);
}
else {
bopit(on_success);
}
that.sessionDequeueNextRequest();
}
function bopit(on_success) {
that.sessionUpdateBop(
on_success,
on_failure_wrapper
);
}
};
/**
* @param {string} description
* @param {Object.<string, string>} config_storage_aux
* @param {function(spv.ds.PublicConfigInfo)} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.publicConfigV2Save = function(
description,
config_storage_aux,
on_success,
on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
spv.assert.isFunction(on_failure, 'on_failure');
this.assertInitializedSession();
var that = this;
var on_success_wrapper = function( result )
{
on_success( result );
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
this.sessionEnqueueRequest(session_request);
function session_request()
{
try
{
that._protocol.publicConfigV2Save(
that.getSessionId(),
description,
config_storage_aux,
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
}
}
/**
* @protected
* @param {function()} on_heartbeat
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionAutomaticHeartbeat = function(on_heartbeat, on_failure) {
var that = this;
this.stopAutomaticHeartbeat();
if( 0 < this._automatic_heartbeat_interval && this._session_is_initialized )
{
this.sessionSendHeartBeat( repeat, handleFailure );
}
function repeat() {
spv.assert( !that._heartbeat_timeout_id );
that._heartbeat_timeout_id = setTimeout(
function() {
that.sessionAutomaticHeartbeat(on_heartbeat, handleFailure);
},
that._automatic_heartbeat_interval );
on_heartbeat();
}
function handleFailure( err )
{
/** Keep heartbeating if no connection is made with the server.
* User's internet connection may be temporarily down. */
if( err.status === 'ServiceUnavailable' || err.connectionStatusCode === 0 )
repeat();
else
on_failure(err);
}
};
/**
* Starts the automatic heartbeat for the session.
* Don't forget to call stopAutomaticHeartbeat if you attempt to destroy or
* replace the DsClient object.
* If you don't, there'll be an orphaned DsClient object heartbeating forever.
* (Well, until the browser session ends. )
* @public
* @param {number} interval How often the heartbeat is triggered, in milliseconds.
* @param {function()} on_heartbeat This callback is called for every heartbeat.
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.startAutomaticHeartbeat = function(
interval,
on_heartbeat,
on_failure )
{
spv.assert( 0 <= interval );
this._automatic_heartbeat_interval = interval;
this.sessionAutomaticHeartbeat( on_heartbeat, on_failure );
};
/**
* @public
*/
spv.ds.DsClient.prototype.stopAutomaticHeartbeat = function()
{
if( this._heartbeat_timeout_id ) {
clearTimeout( this._heartbeat_timeout_id );
this._heartbeat_timeout_id = undefined;
}
};
/**
* @protected
* @param {spv.ds.impl.GoalStateResolver} goal_resolver
* @return {spv.ds.Suggestion}
*/
spv.ds.DsClient.prototype.constructSuggestion = function(goal_resolver)
{
var items_to_add = goal_resolver.items_to_add;
var items_to_remove = goal_resolver.items_to_remove;
var internal_data = gatherItemIdsForActivation(goal_resolver);
var result = new spv.ds.Suggestion(
items_to_add,
items_to_remove,
internal_data);
return result;
};
/**
* Convert state resolvers to suggestions for user presentation and bind each
* suggestion to a .activate function to allow hiding of implemention.
*
* @protected
* @param {Array.<spv.ds.impl.GoalStateResolver>} goal_resolvers
* @return {Array.<spv.ds.Suggestion>}
*/
spv.ds.DsClient.prototype.constructSuggestions = function(goal_resolvers)
{
/** @type {Array.<spv.ds.Suggestion>} */
var result = [];
for (var i = 0; i < goal_resolvers.length; i++)
{
var goal_resolver = goal_resolvers[i];
result.push(this.constructSuggestion(goal_resolver));
}
return result;
};
/**
* Requests a fresh bop and stores the new state in the client.
*
* @protected
* @param {function()} on_success
* @param {function(Error)} on_failure
*/
spv.ds.DsClient.prototype.sessionUpdateBop = function( on_success, on_failure )
{
var that = this;
/** @param {spv.ds.SessionStateUpdate} ssu
* @param {spv.ds.impl.AuxData} aux_data */
var on_success_wrapper = function(ssu, aux_data)
{
that._session_state.update(ssu, aux_data);
on_success();
that.sessionDequeueNextRequest();
};
var on_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that._protocol.sessionUpdateBop(
that.getSessionId(),
that.getLanguage(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* Constructs the standard failure callback wrapper for queued session requests
*
* @protected
* @param {function(Error)} wrap_me The outer failure handler to wrap
* @return {function(Error)}
*/
spv.ds.DsClient.prototype.constructFailureWrapperForQueue = function(wrap_me)
{
var that = this;
/** @param {Error} err */
var on_failure_wrapper = function(err)
{
try
{
wrap_me(err);
}
finally
{
that.sessionDequeueNextRequest();
}
};
return on_failure_wrapper;
};
/**
* Session requests that require synchronization must use this function
* This function is not allowed to throw exceptions.
*
* @protected
*/
spv.ds.DsClient.prototype.sessionDequeueNextRequest = function()
{
var next_request = this._session_request_queue.shift();
if (!next_request)
{
this._session_request_in_progress = false;
return;
}
this._session_request_in_progress = true;
setTimeout(next_request, 0);
};
/**
* Session requests that require synchronization must use this function
*
* @protected
* @param {Function} request
*/
spv.ds.DsClient.prototype.sessionEnqueueRequest = function(request)
{
this._session_request_queue.push(request);
if (this._session_request_in_progress)
{
return;
}
this.sessionDequeueNextRequest();
};
/**
* Resets session request queue
*
* @protected
*/
spv.ds.DsClient.prototype.sessionResetRequestQueue = function()
{
this._session_request_queue.length = 0;
this._session_request_in_progress = false;
};
/**
* Throw exception if session is not initialized.
*
* @protected
*/
spv.ds.DsClient.prototype.assertInitializedSession = function()
{
if (!this._session_is_initialized)
{
throw new Error('Session is not initialized');
}
};
/**
* Throw exception if session is not initialized or is missing a configuration.
*
* @protected
*/
spv.ds.DsClient.prototype.assertInitializedSessionWithConfig = function()
{
this.assertInitializedSession();
this._session_state.assertHasLoadedConfig();
};
/**
* @param {!string} str
* @private
*/
spv.ds.DsClient.prototype.debugPrint = function( str )
{
console.log( "DsClient: " + str );
};
/**
* @param {function(Error)} on_failure
* @protected
* @return {function(Error)}
*/
spv.ds.DsClient.prototype.errorHandler = function( on_failure )
{
var client = this;
return function client_error_handler( err )
{
if( err )
{
if( err.status === 'UnauthorizedSession' && client._on_unauthorized_session )
{
client._on_unauthorized_session( err );
}
else if( err.status === 'ServiceUnavailable' && client._on_service_unavailable )
{
client._on_service_unavailable( err );
}
else if( err.status === 'ServiceFailure' && client._on_service_failure )
{
client._on_service_failure( err );
}
}
return on_failure( err );
}
};
// -----------------------------------------------------------------------------
// Private utility functions.
//
// These look like members of global scope but they are not as long as you
// compile with an outer function wrapper. /AR
/**
* @private
* @param {spv.ds.impl.GoalStateResolver} goal_resolver
* @return {Array.<string>}
*/
var gatherItemIdsForActivation = function(goal_resolver)
{
var menu_items = goal_resolver.items_to_add;
/** @type {Array.<string>} */
var result = [];
for (var i = 0; i < menu_items.length; i++)
{
var menu_item = menu_items[i];
result.push(menu_item.id);
}
return result;
};