diff --git a/src/event/Cvv2DialogEventHandler.js b/src/event/Cvv2DialogEventHandler.js new file mode 100644 index 0000000..16aceba --- /dev/null +++ b/src/event/Cvv2DialogEventHandler.js @@ -0,0 +1,77 @@ +/** + * Cvv2DialogEventHandler + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * TODO: This can be generalized. + */ + +var Class = require( 'easejs' ).Class, + EventHandler = require( './EventHandler' ); + + +/** + * Shows Cvv2 Dialog + */ +module.exports = Class( 'Cvv2DialogEventHandler' ) + .implement( EventHandler ) + .extend( +{ + /** + * jQuery instance + * + * @type {jQuery} + */ + 'private _jquery': null, + + + /** + * Initializes with client that will delegate the event + * + * @param {jQuery} jquery jquery instance + */ + __construct: function( jquery ) + { + this._jquery = jquery; + }, + + + /** + * Handles kick-back + * + * @param {string} type event id; ignored + * + * @param {function(*,Object)} continuation to invoke on completion + * + * @return {StatusEventHandler} self + */ + 'public handle': function( type, c, data ) + { + var $dialog = this._jquery( '#' + data.ref ).dialog( { + title: "CVV/CSV Verification Information", + width: "500px", + resizable: false, + buttons: { + 'Close': function() + { + $dialog.dialog( 'close' ); + } + } + } ); + } +} ); diff --git a/src/event/DelegateEventHandler.js b/src/event/DelegateEventHandler.js new file mode 100644 index 0000000..0ca9859 --- /dev/null +++ b/src/event/DelegateEventHandler.js @@ -0,0 +1,124 @@ +/** + * Event handler proxy + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + EventHandler = require( './EventHandler' ), + UnknownEventError = require( './UnknownEventError' ); + + +/** + * Delegates events to an apropriate handler + * + * Handlers must be registered to recgonize an event. + */ +module.exports = Class( 'DelegateEventHandler' ) + .implement( EventHandler ) + .extend( +{ + /** + * Hash of event handlers to delegate to for various events + * @type {Object} + */ + 'private _handlers': {}, + + + /** + * Initialize delegate with handlers to delegate requests to for each + * supported event type + * + * @param {Object} handlers events as keys, handlers as values + */ + __construct: function( handlers ) + { + // register each provided handler + for ( var type in handlers ) + { + this._addHandler( type, handlers[ type ] ); + } + }, + + + /** + * Determines if a handler has been registered for the given type + * + * @param {string} type event id + * + * @return {boolean} whether a handler exists for the given type + */ + 'public hasHandler': function( type ) + { + return ( this._handlers[ type ] !== undefined ); + }, + + + /** + * Handle an event of the given type + * + * An exception will be thrown if the event cannot be handled. + * + * The handler should always return itself; if a return value is needed to + * the caller, then a callback should be provided as an argument to the + * handler. + * + * Additional arguments will be passed to the appropriate handler. + * + * @param {string} type event id + * + * @return {EventHandler} self + */ + 'public handle': function( type ) + { + var handler = this._handlers[ type ]; + + // fail if we do not have a handler for this particular event + if ( !handler ) + { + throw UnknownEventError( "Unsupported event type: " + type ); + } + + // delegate + handler.handle.apply( handler, arguments ); + + return this; + }, + + + /** + * Add an EventHandler for a given event type + * + * Warning: this will overwrite existing handlers for this type. + * + * @param {string} type event id + * @param {EventHandler} handler handler for event + * + * @return {ClientEventHandler} self + */ + 'private _addHandler': function( type, handler ) + { + if ( !( Class.isA( EventHandler, handler ) ) ) + { + throw TypeError( "Expected EventHandler for event type " + type ); + } + + this._handlers[ type ] = handler; + return this; + } +} ); diff --git a/src/event/EventHandler.js b/src/event/EventHandler.js new file mode 100644 index 0000000..41c8552 --- /dev/null +++ b/src/event/EventHandler.js @@ -0,0 +1,41 @@ +/** + * Event handling interface + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Interface = require( 'easejs' ).Interface; + + +module.exports = Interface( 'EventHandler', +{ + /** + * Handle an event of the given type + * + * An exception should be thrown if the event cannot be handled. + * + * The handler should always return itself; if a return value is needed to + * the caller, then a callback should be provided as an argument to the + * handler. + * + * @param {string} type event id + * + * @return {EventHandler} self + */ + 'public handle': [ 'type' /*, ... */ ] +} ); diff --git a/src/event/IndvRateEventHandler.js b/src/event/IndvRateEventHandler.js new file mode 100644 index 0000000..cc95f8e --- /dev/null +++ b/src/event/IndvRateEventHandler.js @@ -0,0 +1,53 @@ +/** + * Invidiual rate request event handler + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + RateEventHandler = require( './RateEventHandler' ); + + +/** + * Performs rate requests + */ +module.exports = Class( 'IndvRateEventHandler' ) + .extend( RateEventHandler, +{ + 'override protected postRate': function( err, data, client, quote ) + { + if ( err ) + { + var inelig = {}; + inelig[ action.id + '_ineligible' ] = 'Error calculating premium.'; + + // uh oh. notify the user that there was a problem. + quote.setData( inelig ).save(); + } + + // we must force a screen update (TODO: make this unnecessary) + client.getUi().getCurrentStep().emptyBucket(); + }, + + + 'override protected queueProgressDialog': function( after_ms ) + { + // no dialog. + return function() {}; + } +} ); diff --git a/src/event/KickbackEventHandler.js b/src/event/KickbackEventHandler.js new file mode 100644 index 0000000..21b52d8 --- /dev/null +++ b/src/event/KickbackEventHandler.js @@ -0,0 +1,82 @@ +/** + * Kickback event handler + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + EventHandler = require( './EventHandler' ); + + +/** + * Performs rate requests + */ +module.exports = Class( 'KickbackEventHandler' ) + .implement( EventHandler ) + .extend( +{ + /** + * Client that will perform requests on this handler + * @type {Client} + */ + 'private _client': null, + + + /** + * Initializes with client that will delegate the event + * + * @param {Client} client client object + */ + __construct: function( client ) + { + this._client = client; + }, + + + /** + * Handles kick-back + * + * @param {string} type event id; ignored + * + * @param {function(*,Object)} continuation to invoke on completion + * + * @return {KickbackEventHandler} self + */ + 'public handle': function( type, c, data ) + { + var step_id = +data.stepId, + quote = this._client.getQuote(), + nav = this._client.nav, + ui = this._client.getUi(); + + if ( quote.getTopVisitedStepId() > step_id ) + { + quote.setTopVisitedStepId( step_id ); + nav.setTopVisitedStepId( step_id ); + + if ( quote.getCurrentStepId() > step_id ) + { + nav.navigateToStep( step_id ); + } + + ui.redrawNav(); + } + + c(); + } +} ); diff --git a/src/event/RateEventHandler.js b/src/event/RateEventHandler.js new file mode 100644 index 0000000..ef233da --- /dev/null +++ b/src/event/RateEventHandler.js @@ -0,0 +1,233 @@ +/** + * Rate event handler + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + EventHandler = require( './EventHandler' ); + + +/** + * Performs rate requests + */ +module.exports = Class( 'RateEventHandler' ) + .implement( EventHandler ) + .extend( +{ + /** + * Number of milliseconds to delay rating progress dialog display + * @type {number} + */ + 'private const _DIALOG_DELAY_MS': 500, + + /** + * Client that will perform requests on this handler + * @type {Client} + */ + 'private _client': null, + + /** + * Data proxy used for requests + * @type {ClientDataProxy} + */ + 'private _dataProxy': null, + + + /** + * Initializes event handler with a data proxy that may be used to + * communicate with a remote server for rate requests + * + * @param {Client} client client object + * @param {ClientDataProxy} data proxy used for rate requests + */ + __construct: function( client, data_proxy ) + { + this._client = client; + this._dataProxy = data_proxy; + }, + + + /** + * Handle rating request and performs rating + * + * If the quote is locked, no rating request will be made and the + * continuation will be immediately invoked with no error and null rate + * data. + * + * If the client is in the process of saving, then rating will be deferred + * until all save operations have completed, after which rating will proceed + * as normal. + * + * After rating is complete, the coninuation will be invoked. If there is an + * error, it will be provided as the first argument and the data argument + * will be null. Otherwise, the error argument will be null and the data + * argument will contain the result of the rate call. Note that the quote + * will be automatically filled with the return data. + * + * @param {string} type event id; ignored + * + * @param {function(*,Object)} continuation to invoke on completion + * + * @return {RateEventHandler} self + */ + 'public handle': function( type, c, data ) + { + var _self = this, + quote = this._client.getQuote(), + qstep = quote.getCurrentStepId(); + + // do not perform rating if quote is locked; use existing rates, if + // available (stored in bucket) + if ( quote.isLocked() + || ( qstep <= quote.getExplicitLockStep() ) + ) + { + // no error, no data. + c( null, null ); + return; + } + + // if we're in the process of saving, then wait until all saves are + // complete before continuing + if ( this._client.isSaving() ) + { + // defer rating until after saving is complete + this._client.once( 'postSaveAll', function() + { + dorate(); + } ); + + return; + } + + function dorate() + { + _self._performRating( quote, c, data.indv, data.stepId ); + } + + // perform rating immediately + dorate(); + + return this; + }, + + + 'private _performRating': function( quote, c, indv, dest_step_id ) + { + var _self = this; + + // queue display of "rating in progress" dialog + var dialog_close = this.queueProgressDialog( + this.__self.$( '_DIALOG_DELAY_MS' ) + ); + + // grab the rates from the server for the already posted quote data + this._dataProxy.get( this._genRateUrl( quote, indv ), + function( response, err ) + { + var data = ( response.content.data || {} ); + + if ( err ) + { + // error; do not provide rate data + c( err, null ); + return; + } + + // fill the bucket with the response data and save (client-side + // save only; no transport is specified) + quote.refreshData( data ); + + // let subtypes handle additional processing + _self.postRate( err, data, _self._client, quote ); + + // close rating dialog after rates are displayed + dialog_close(); + + // invalidate the step to force emptying of the bucket, ensuring + // that data is updated on the screen when it's re-rated (will + // be undefined if the step hasn't been loaded yet, in which + // case it doesn't need to be invalidated) + var stepui = _self._client.getUi().getStep( dest_step_id ); + if ( stepui !== undefined ) + { + stepui.invalidate(); + } + + c( null, response.content.data ); + } + ); + }, + + + 'virtual protected postRate': function( err, data, client, quote ) + { + // reserved for subtypes + }, + + + /** + * Generate the rate request URL + * + * @param {Quote} quote to get premium for + * @param {string} indv optional individual supplier to request + * + * @return {string} request URL + */ + 'private _genRateUrl': function( quote, indv ) + { + return quote.getId() + '/rate' + + ( ( indv ) + ? '/' + indv + : '' + ); + }, + + + /** + * Queue "rating in progress" dialog for display after a short period of + * time + * + * The dialog may be closed/cancelled by invoking the returned function. + * + * @param {number} after_ms display dialog after this number of millisecs + * + * @return {function()} function to close dialog + */ + 'virtual protected queueProgressDialog': function( after_ms ) + { + var _self = this, + $dialog = null; + + // only display the dialog if the rating time exceeds 500ms + var timeout = setTimeout( function() + { + $dialog = _self._client.uiDialog.showRatingInProgressDialog(); + }, after_ms ); + + // return a function that may be used to close the dialog + return function() + { + // prevent the dialog from being displayed and close it if it has + // already been displayed + clearTimeout( timeout ); + $dialog && $dialog.close(); + } + } +} ); diff --git a/src/event/StatusEventHandler.js b/src/event/StatusEventHandler.js new file mode 100644 index 0000000..95d3431 --- /dev/null +++ b/src/event/StatusEventHandler.js @@ -0,0 +1,81 @@ +/** + * Field status event handler + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + EventHandler = require( './EventHandler' ); + + +/** + * Performs rate requests + */ +module.exports = Class( 'StatusEventHandler' ) + .implement( EventHandler ) + .extend( +{ + /** + * Styler used to manipulate the DOM + * + * TODO: deprecate + * + * @type {ElementStyler} + */ + 'private _styler': null, + + + /** + * Initializes with client that will delegate the event + * + * @param {ElementStyler} styler element styler + */ + __construct: function( styler ) + { + this._styler = styler; + }, + + + /** + * Handles kick-back + * + * @param {string} type event id; ignored + * + * @param {function(*,Object)} continuation to invoke on completion + * + * @return {StatusEventHandler} self + */ + 'public handle': function( type, c, data ) + { + var indexes = data.indexes || [], + value = data.value || '', + name = data.elementName; + + for ( var i in indexes ) + { + // string means a static value; otherwise, an array + // represents bucket values, of which we should take the + // associated index + var value = ( typeof value === 'string' ) + ? value + : value[ i ]; + + this._styler.setStatus( name, indexes[ i ], value ); + } + } +} ); diff --git a/src/event/UnknownEventError.js b/src/event/UnknownEventError.js new file mode 100644 index 0000000..da346c8 --- /dev/null +++ b/src/event/UnknownEventError.js @@ -0,0 +1,37 @@ +/** + * Contains UnknownEventError + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of the Liza Data Collection Framework + * + * Liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module.exports = function UnknownEventError( msg ) +{ + // 'new' keyword optional + if ( !( this instanceof module.exports ) ) + { + return new module.exports( msg ); + } + + Error.prototype.constructor.apply( this, arguments ); +}; + +// extends TypeError +module.exports.constructor = module.exports; +module.exports.prototype = TypeError(); +module.exports.prototype.name = 'UnknownEventError'; +