1
0
Fork 0

Liberated StepUiBuilder

master
Mike Gerwitz 2015-12-01 10:28:20 -05:00
parent 9a1dd337eb
commit 44323a0b59
1 changed files with 269 additions and 0 deletions

View File

@ -0,0 +1,269 @@
/**
* Builds UI from template
*
* Copyright (C) 2015 LoVullo Associates, Inc.
*
* This file is part of liza.
*
* 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 <http://www.gnu.org/licenses/>.
*
* @needsLove
* - Global references to jQuery must be removed.
* - Dependencies need to be liberated:
* - ElementStyler;
* - UI.
* - This may not be needed, may be able to be handled differently, and
* really should load from data rather than a pre-generated template (?)
* @end needsLove
*/
var Class = require( 'easejs' ).Class,
EventEmitter = require( 'events' ).EventEmitter;
module.exports = Class( 'StepUiBuilder' )
.extend( EventEmitter,
{
/**
* Used to style elements
* @type {ElementStyler}
*/
'private _elementStyler': null,
/**
* Used for building groups
* @type {function()}
*/
'private _groupBuilder': null,
/**
* Retrieves step data
* @type {function( step_id: number )}
*/
'private _dataGet': null,
/**
* Step that the StepUi is being modeled after
* @type {Step}
*/
'private _step': null,
/**
* Format bucket data for display
* @type {BucketDataValidator}
*/
'private _formatter': null,
'public __construct': function(
element_styler,
formatter,
groupBuilder,
dataGet
)
{
this._elementStyler = element_styler;
this._formatter = formatter;
this._groupBuilder = groupBuilder;
this._dataGet = dataGet;
},
/**
* Sets the underlying step
*
* @param {Step} step
*
* @return {StepUiBuilder} self
*/
'public setStep': function( step )
{
this._step = step;
return this;
},
'public build': function( StepUi, callback )
{
var _self = this;
if ( !( this._step ) )
{
throw Error( 'No step provided' );
}
// create a new StepUi
var ui = StepUi(
this._step,
this._elementStyler,
this._formatter
);
// retrieve and process the step data (this kick-starts the entire
// process)
this._getData( function( data )
{
_self._processData( data, ui );
// build is complete
callback.call( null, ui );
});
return this;
},
/**
* Retrieves step data using the previously provided function
*
* This process may be asynchronous.
*
* @param {function( data: Object )} callback function to call with data
*
* @return {undefined}
*/
'private _getData': function( callback )
{
this._dataGet.call( this, this._step.getId(), function( data )
{
callback( data );
});
},
/**
* Processes the step data after it has been retrieved
*
* @param Object data step data (source should return as JSON)
*
* @return void
*/
'private _processData': function( data, ui )
{
// sanity check
if ( !( data.content.html ) )
{
// todo: show more information and give user option to retry
data.content.html = '<h1>Error</h1><p>A problem was encountered ' +
'while attempting to view this step.</p>';
}
// enclose it in a div so that we have a single element we can query,
// making our lives much easier
ui.setContent(
$( '<div class="raterStepDiv" />' )
.attr( 'id', '__step' + ui.getStep().getId() )
.html( data.content.html )
);
// free the content from memory, as it's no longer needed (we don't need
// both the DOM representation and the string representation in memory
// for the life of the script - it's a waste)
delete data.content;
// create the group objects
this._createGroups( ui );
// track changes so we know when to validate and post
ui.setDirtyTrigger();
// let others do any final processing before we consider ourselves
// ready
ui.emit( ui.__self.$( 'EVENT_POST_PROCESS' ) );
},
/**
* Instantiates Group objects for each group in the step content, then
* styles them
*
* TODO: refactor into own builder
*
* @param {StepUi} ui new ui instance
*
* @return {undefined}
*/
'private _createGroups': function( ui )
{
// reference to self for use in closure
var _self = this,
groups = {},
group = null,
group_id = 0,
step = ui.getStep();
// instantiate a group object for each of the groups within this step
var $groups = ( ui.getContent().find( '.stepGroup' ) ).each( function()
{
group = _self._groupBuilder( $( this ), _self._elementStyler );
group_id = group.getGroupId();
groups[ group_id ] = group;
// let the step know what fields it contains
step.addExclusiveFieldNames(
group.getGroup().getExclusiveFieldNames()
);
_self._hookGroup( group, ui );
} );
// XXX: remove public property assignment
ui.groups = groups;
ui.initGroupFieldData();
// we can style all the groups, since the elements that cannot be styled
// (e.g. table groups) have been removed already
_self._elementStyler.apply( $groups, false );
},
/**
* Hook various group events for processing
*
* @param {GroupUi} group group to hook
* @param {StepUi} ui new ui instance
*
* @return {undefined}
*/
'private _hookGroup': function( group, ui )
{
group
.invalidate( function()
{
ui.invalidate();
} )
.on( 'indexAdd', function( index )
{
ui.emit( ui.__self.$( 'EVENT_INDEX_ADD' ), index, this );
} )
.on( 'indexRemove', function( index )
{
ui.emit( ui.__self.$( 'EVENT_INDEX_REMOVE' ), index, this );
} ).on( 'indexReset', function( index )
{
ui.emit( ui.__self.$( 'EVENT_INDEX_RESET' ), index, this );
} )
.on( 'action', function( type, ref, index )
{
// simply forward
ui.emit( ui.__self.$( 'EVENT_ACTION' ), type, ref, index );
} )
.on( 'postAddRow', function( index )
{
ui.emit( 'postAddRow', index );
} );
}
} );