/**
* @fileoverview Client layer for Blds - Ballingslöv Digital Showroom.
The VDS 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 Kim Simmons (kim.simmons@spark-vision.com)
*/
goog.provide('spv.blds.BldsClient');
goog.require('spv.assert');
goog.require('spv.ds.assert');
goog.require('spv.ds.Config');
goog.require('spv.ds.DsClient');
goog.require('spv.ds.MenuItem');
goog.require('spv.ds.ImageSerieInfo');
goog.require('spv.ds.Summary');
/**
* @export
* @constructor
* @extends {spv.ds.DsClient}
* @param {string} service_url
* @param {spv.ds.DsClientSettings=} in_settings
*/
spv.blds.BldsClient = function(service_url, in_settings)
{
spv.assert.isNonEmptyString(service_url, 'service_url');
// Default settings.
var default_settings = new spv.ds.DsClientSettings();
default_settings.use_jsonp = false;
default_settings.use_nova_protocol = true;
default_settings.debug_print = false;
default_settings.initial_category = "SdsMain";
/** @type {!spv.ds.DsClientSettings} */
var settings = in_settings ? default_settings.overloadWith( in_settings ) : default_settings;
spv.ds.DsClient.call( this, service_url, settings );
/**
* No specialized protocol for this client as of yet.
*/
};
goog.inherits(spv.blds.BldsClient, spv.ds.DsClient);
/**
* 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.
*
* This override makes sure the initial session state is updated.
*
* Preconditions: None.
*
* @export
* @override
* @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.blds.BldsClient.prototype.sessionInit = function(
menu_query,
readmore_mode,
on_success,
on_failure)
{
var that = this;
/**
* @param {spv.ds.IntroPage} intro_page
*/
var on_session_inited = function(intro_page)
{
var on_update_bop = function()
{
on_success(intro_page);
}
var session_id = that.getSessionId();
that.sessionUpdateBop(on_update_bop, on_failure);
}
var state_update = goog.base(
this,
'sessionInit',
menu_query,
readmore_mode,
on_session_inited,
on_failure);
};
/**
* Get the last state update. Call this function on successful load of configs
* or on successful sessionToggleItem.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @override
* @return {spv.ds.SessionStateUpdate}
*/
spv.blds.BldsClient.prototype.sessionGetStateUpdate = function()
{
/* Filter away root menus from incoming bops. */
/** @type {spv.ds.SessionStateUpdate} */
var state_update = goog.base( this, 'sessionGetStateUpdate' );
/** @type {Array.<spv.ds.MenuItem>} */
var root_menus = spv.ds.DsClient.prototype.sessionGetRootMenuItems.call( this );
/** @type {Object.<string, boolean>} */
var is_root_menu = {};
for( var i=0; i < root_menus.length; ++i )
is_root_menu[ root_menus[i].id ] = true;
/** @type {Array.<spv.ds.MenuItem>} */
var filtered_items = [];
// If a menu is encountered, then this collection of menu items should be menus only.
/** @type {Array.<spv.ds.MenuItem>} */
var filtered_menus = [];
for( var i=0; i < state_update.menu_items.length; ++i )
{
var item = state_update.menu_items[i];
if( !is_root_menu[ item.id ] )
{
if( spv.blds.BldsClient.isMenu( item.id ) )
filtered_menus.push( item );
else
filtered_items.push( item );
}
}
state_update.menu_items =
filtered_menus.length > 0 ?
filtered_menus : filtered_items;
return state_update;
};
/**
* Get the summary for the current session and configuration.
* This override excludes the item summaries with no text.
*
* Preconditions: Initialized session, loaded configuration.
*
* @export
* @override
* @param {function(spv.ds.Summary)} on_success
* @param {function(Error)} on_failure
*/
spv.blds.BldsClient.prototype.sessionGetSummary = function(on_success, on_failure)
{
spv.assert.isFunction(on_success, 'on_success');
/**
* @param {spv.ds.Summary} summary
*/
var on_success_wrapped = function(summary)
{
var filtered = [];
for(var i = 0; i < summary.item_summaries.length; i++)
{
var item_summary = summary.item_summaries[i];
if(item_summary.item_text)
filtered.push(item_summary);
}
summary.item_summaries = filtered;
on_success(summary);
}
goog.base( this, 'sessionGetSummary', on_success_wrapped, on_failure );
};
/**
* Finds the article number for the items in a summary.
* Use BldsClient.sessionGetSummary() to fetch the configuration's summary.
* @export
* @param {spv.ds.Summary} summary
* @return {Array.<string>}
*/
spv.blds.BldsClient.prototype.sessionGetSummaryArticleNumbers = function( summary )
{
spv.assert(
summary.constructor === spv.ds.Summary,
"spv.ds.Summary object required for argument 'summary'." );
var summaries = summary.item_summaries;
/** @type {Array.<string>} */
var arts = [];
for( var i=0, ii=summaries.length; i < ii; ++i )
{
var additionals = summaries[i].aux_data['additional_item_data'];
var payload = additionals ? additionals['Payload'] : null;
var art = payload ? payload['art_no'] : null;
if( art ) arts.push(art);
}
return arts;
};
/**
* @export
* @param {spv.ds.ItemSummary} item_summary
* @return {?string}
*/
spv.blds.BldsClient.prototype.sessionGetItemSummaryArticleNumber = function( item_summary )
{
spv.assert(
item_summary.constructor === spv.ds.ItemSummary,
"spv.ds.SummaryItem object required for argument 'item_summary'." );
var result = spv.blds.BldsClient.extractArticleNumberFromAux( item_summary.aux_data );
if( result.state === "FOUND" )
return result.value;
else if( result.state === "NO_AUX_DATA" )
return null;
else
throw Error( "Unexpected aux data layout for item: " + item_summary.item_id + "\n" + result.state );
};
/**
*
* @param {Object} item_aux_obj
* @return {Object.<string,string>}
*/
spv.blds.BldsClient.extractArticleNumberFromAux = function( item_aux_obj )
{
if( item_aux_obj === undefined )
return {state: "NO_AUX_DATA"};
var additionals = item_aux_obj['additional_item_data'];
if( additionals === undefined )
return {state: "NO_AUX_DATA"};
var payload = additionals['Payload'];
if( payload === undefined )
return {state: "NO_AUX_DATA"};
var art_no = payload['art_no'];
if( art_no === undefined )
return {state: "NO_ARTNO_AUX_DATA"};
return {state: "FOUND", value: art_no};
};
/**
* Makes a lookup for the given item id to see if it has an article number.
* An object is returned with {state: somestate, value: thearticlenumber}.
* Possible state values:
* "FOUND": An article number was found and is returned in the value field.
* "NO_AUX_DATA": There's no additional data for this item, what so ever.
* "NO_SPECIFIC_AUX_DATA": There's no Blds specific aux data for this item.
* "NO_ARTNO_AUX_DATA": It has Blds aux data, but no article number in it.
*
* Only if state === "FOUND" will the value contain a string with the article number.
* .value will be undefined in all other cases.
* @export
* @param {string} item_id
* @return {Object.<string,string>}
*/
spv.blds.BldsClient.prototype.sessionCheckForArticleNumber = function( item_id )
{
spv.assert.isNonEmptyString( item_id, "item_id" );
var aux = this.sessionGetAuxiliaryData();
var item_aux = aux.item_aux[ item_id ];
return spv.blds.BldsClient.extractArticleNumberFromAux( item_aux );
};
/**
* Gets the article number for an item or null if it doesn't have one.
* @export
* @param {string} item_id
* @return {?string}
*/
spv.blds.BldsClient.prototype.sessionGetArticleNumber = function( item_id )
{
spv.assert.isNonEmptyString( item_id, "item_id" );
var result = this.sessionCheckForArticleNumber( item_id );
if( result.state === "FOUND" )
return result.value;
else if( result.state === "NO_AUX_DATA" )
return null;
else
throw Error( "Unexpected aux data layout for item: " + item_id + "\n" + result.state );
};
/**
* Gets the current configuration.
*
* Preconditions: Initialized session, loaded configuration.
* This requires the CurrentConfigAuxPlugin to work.
*
* @export
* @return {spv.ds.Config}
*/
spv.blds.BldsClient.prototype.sessionGetCurrentConfig = function()
{
var aux = this.sessionGetAuxiliaryData();
if(!aux)
throw new Error("Auxiliary data missing");
var config_aux = aux.bop_aux["CurrentConfig"];
if(!config_aux)
throw new Error("CurrentConfig auxiliary not found");
var encoding = config_aux["encoding"] || "";
var config_string = config_aux["config_string"];
if(!config_string)
throw new Error("Current config missing from auxiliary data");
return new spv.ds.Config(encoding, config_string);
};
/**
* 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.
*
* 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
* @override
* @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.blds.BldsClient.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_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_failure_wrapper =
this.constructFailureWrapperForQueue( this.errorHandler(on_failure) );
var session_request = function()
{
try
{
that._protocol.sessionBip(
that.getSessionId(),
category_id,
[item_id],
that.getLanguage(),
on_success_wrapper,
on_failure_wrapper);
}
catch (err)
{
on_failure_wrapper(err);
}
};
this.sessionEnqueueRequest(session_request);
};
/**
* @private
* @param {string} item_id
* @return {boolean}
*/
spv.blds.BldsClient.isMenu = function( item_id )
{
spv.assert.isNonEmptyString( item_id, "item_id" );
return /^SUBMENU|^MENU/.test( item_id );
};