GroupUi show/hide based on liza DOM API
This isn't entirely complete---some groups don't make use of it (namely TableGroupUi). But it's a start.master
commit
31f67b6719
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* List of US states and codes
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of states and their codes
|
||||||
|
* @var {Object}
|
||||||
|
*/
|
||||||
|
var states = {
|
||||||
|
'AL': 'Alabama',
|
||||||
|
'AK': 'Alaska',
|
||||||
|
'AZ': 'Arizona',
|
||||||
|
'AR': 'Arkansas',
|
||||||
|
'CA': 'California',
|
||||||
|
'CO': 'Colorado',
|
||||||
|
'CT': 'Connecticut',
|
||||||
|
'DE': 'Delaware',
|
||||||
|
'FL': 'Florida',
|
||||||
|
'GA': 'Georgia',
|
||||||
|
'HI': 'Hawaii',
|
||||||
|
'ID': 'Idaho',
|
||||||
|
'IL': 'Illinois',
|
||||||
|
'IN': 'Indiana',
|
||||||
|
'IA': 'Iowa',
|
||||||
|
'KS': 'Kansas',
|
||||||
|
'KY': 'Kentucky',
|
||||||
|
'LA': 'Louisiana',
|
||||||
|
'ME': 'Maine',
|
||||||
|
'MD': 'Maryland',
|
||||||
|
'MA': 'Massachusetts',
|
||||||
|
'MI': 'Michigan',
|
||||||
|
'MN': 'Minnesota',
|
||||||
|
'MS': 'Mississippi',
|
||||||
|
'MO': 'Missouri',
|
||||||
|
'MT': 'Montana',
|
||||||
|
'NE': 'Nebraska',
|
||||||
|
'NV': 'Nevada',
|
||||||
|
'NH': 'New Hampshire',
|
||||||
|
'NJ': 'New Jersey',
|
||||||
|
'NM': 'New Mexico',
|
||||||
|
'NY': 'New York',
|
||||||
|
'NC': 'North Carolina',
|
||||||
|
'ND': 'North Dakota',
|
||||||
|
'OH': 'Ohio',
|
||||||
|
'OK': 'Oklahoma',
|
||||||
|
'OR': 'Oregon',
|
||||||
|
'PA': 'Pennsylvania',
|
||||||
|
'RI': 'Rhode Island',
|
||||||
|
'SC': 'South Carolina',
|
||||||
|
'SD': 'South Dakota',
|
||||||
|
'TN': 'Tennessee',
|
||||||
|
'TX': 'Texas',
|
||||||
|
'UT': 'Utah',
|
||||||
|
'VT': 'Vermont',
|
||||||
|
'VA': 'Virginia',
|
||||||
|
'WA': 'Washington',
|
||||||
|
'WV': 'West Virginia',
|
||||||
|
'WI': 'Wisconsin',
|
||||||
|
'WY': 'Wyoming',
|
||||||
|
|
||||||
|
// nothing
|
||||||
|
'' : '',
|
||||||
|
0: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the state associated with the given code
|
||||||
|
*
|
||||||
|
* @param {string} code state abbr
|
||||||
|
*
|
||||||
|
* @return {string} name of state
|
||||||
|
*/
|
||||||
|
exports.getName = function( code )
|
||||||
|
{
|
||||||
|
var val = states[ code ];
|
||||||
|
|
||||||
|
// if the value was not found, return the code we were given (which likely
|
||||||
|
// makes no sense at all, since it's not a valid state)
|
||||||
|
return ( val === undefined )
|
||||||
|
? code
|
||||||
|
: val;
|
||||||
|
}
|
|
@ -22,7 +22,6 @@
|
||||||
* - References to "quote" should be replaced with generic terminology
|
* - References to "quote" should be replaced with generic terminology
|
||||||
* representing a document.
|
* representing a document.
|
||||||
* - Dependencies need to be liberated:
|
* - Dependencies need to be liberated:
|
||||||
* - ElementStyler;
|
|
||||||
* - BucketDataValidator.
|
* - BucketDataValidator.
|
||||||
* - Global references (e.g. jQuery) must be removed.
|
* - Global references (e.g. jQuery) must be removed.
|
||||||
* - Checkbox-specific logic must be extracted.
|
* - Checkbox-specific logic must be extracted.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Field group context
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subset of a larger collection of fields that can be used to restrict
|
||||||
|
* operations for both convenience and (moreso) performance
|
||||||
|
*/
|
||||||
|
module.exports = Interface( 'Context',
|
||||||
|
{
|
||||||
|
'public getFieldByName': [ 'name', 'index', 'filter' ],
|
||||||
|
|
||||||
|
'public split': [ 'on' ]
|
||||||
|
} );
|
|
@ -0,0 +1,350 @@
|
||||||
|
/**
|
||||||
|
* DOM subset context
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
Context = require( './Context' ),
|
||||||
|
|
||||||
|
EventEmitter = require( 'events' ).EventEmitter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subset of the DOM that can be used to restrict operations for both
|
||||||
|
* convenience and (moreso) performance
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'DomContext' )
|
||||||
|
.implement( Context )
|
||||||
|
.extend( EventEmitter,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Parent context, if any
|
||||||
|
* @type {DomContext}
|
||||||
|
*/
|
||||||
|
'private _pcontext': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOM content for this particular context
|
||||||
|
* @type {HTMLElement}
|
||||||
|
*/
|
||||||
|
'private _content': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent to re-attach to
|
||||||
|
* @type {HtmlElement}
|
||||||
|
*/
|
||||||
|
'private _contentParent': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory used to produce DomFields
|
||||||
|
* @type {DomFieldFactory}
|
||||||
|
*/
|
||||||
|
'private _fieldFactory': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache of fields that have been looked up previously
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
'private _fieldCache': {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continuations to be invoked once attached to the DOM
|
||||||
|
* @type {Array.<function()>}
|
||||||
|
*/
|
||||||
|
'private _attachq': [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continuations to be invoked once detached from the DOM
|
||||||
|
* @type {Array.<function()>}
|
||||||
|
*/
|
||||||
|
'private _detachq': [],
|
||||||
|
|
||||||
|
|
||||||
|
__construct: function( content, field_factory, pcontext, cache )
|
||||||
|
{
|
||||||
|
// older browsers do not support HTMLElement, but we still want the type
|
||||||
|
// check for newer ones
|
||||||
|
if ( window.HTMLElement && !( content instanceof HTMLElement ) )
|
||||||
|
{
|
||||||
|
throw TypeError( "Context content must be a valid HTMLElement" );
|
||||||
|
}
|
||||||
|
else if ( !( this.verifyParentContext( pcontext ) ) )
|
||||||
|
{
|
||||||
|
throw TypeError( "Invalid parent DomContext" );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._content = content;
|
||||||
|
this._fieldFactory = field_factory;
|
||||||
|
this._pcontext = pcontext || null;
|
||||||
|
this._fieldCache = cache || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual protected verifyParentContext': function( context )
|
||||||
|
{
|
||||||
|
return Class.isA( module.exports, context );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public split': function( on_id, c )
|
||||||
|
{
|
||||||
|
var _self = this,
|
||||||
|
inst = _self.__inst;
|
||||||
|
|
||||||
|
this._getElementById( on_id, function( element )
|
||||||
|
{
|
||||||
|
// if the element could not be found, just return self
|
||||||
|
c( ( element )
|
||||||
|
? module.exports(
|
||||||
|
element,
|
||||||
|
_self._fieldFactory,
|
||||||
|
inst,
|
||||||
|
_self._fieldCache
|
||||||
|
).on( 'error', function( e )
|
||||||
|
{
|
||||||
|
// "bubble up" errors
|
||||||
|
_self.emit( 'error', e );
|
||||||
|
} )
|
||||||
|
|
||||||
|
: inst
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public getFieldByName': function( name, index, filter )
|
||||||
|
{
|
||||||
|
var result = this._fromCache( name, index );
|
||||||
|
|
||||||
|
if ( filter )
|
||||||
|
{
|
||||||
|
throw Error( "TODO: filter" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _getElementById': function( id, c )
|
||||||
|
{
|
||||||
|
id = ''+id;
|
||||||
|
|
||||||
|
if ( !id )
|
||||||
|
{
|
||||||
|
c( null );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we cannot perform the highly performant getElementById() unless we
|
||||||
|
// are attached to the DOM
|
||||||
|
this.whenAttached( function()
|
||||||
|
{
|
||||||
|
c( document.getElementById( id ) );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _fromCache': function( name, index, lookup )
|
||||||
|
{
|
||||||
|
var data = (
|
||||||
|
this._fieldCache[ name ] = this._fieldCache[ name ] || []
|
||||||
|
);
|
||||||
|
|
||||||
|
// if already present within the cache, simply return it
|
||||||
|
if ( data[ index ] )
|
||||||
|
{
|
||||||
|
return data[ index ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to cache and return
|
||||||
|
var _self = this;
|
||||||
|
return data[ index ] = this._fieldFactory.create(
|
||||||
|
name, index,
|
||||||
|
|
||||||
|
// this is intended to defer request of the root element until this
|
||||||
|
// context is attached to the DOM; this ensures that the requester
|
||||||
|
// can take advantage of features of the attached DOM such as
|
||||||
|
// getElementById() and defers initial DOM operations until the
|
||||||
|
// element is actually available on the DOM
|
||||||
|
function( c )
|
||||||
|
{
|
||||||
|
// invoke the continuation as soon as we're attached to the DOM
|
||||||
|
_self.whenAttached( c );
|
||||||
|
}
|
||||||
|
).on( 'error', function( e )
|
||||||
|
{
|
||||||
|
// forward errors
|
||||||
|
_self.emit( 'error', e );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether this context is currently attached to the DOM
|
||||||
|
*
|
||||||
|
* @return {boolean} true if attached to the DOM, otherwise false
|
||||||
|
*/
|
||||||
|
'virtual public isAttached': function()
|
||||||
|
{
|
||||||
|
// we are attached if (a) our content node has a parent and (b) if our
|
||||||
|
// parent context is also attached
|
||||||
|
return !!this._content.parentElement && this._pcontext.isAttached();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a continunation to be invoked once the context becomes attached
|
||||||
|
* to the DOM
|
||||||
|
*
|
||||||
|
* If already attached, the continuation will be executed immediately
|
||||||
|
* (synchronously).
|
||||||
|
*
|
||||||
|
* @param {function()} c continuation to be invoked
|
||||||
|
*
|
||||||
|
* @return {DomContext} self
|
||||||
|
*/
|
||||||
|
'public whenAttached': function( c )
|
||||||
|
{
|
||||||
|
// invoke immediately if we're already attached
|
||||||
|
if ( this.isAttached() )
|
||||||
|
{
|
||||||
|
c();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue continuation
|
||||||
|
var _self = this;
|
||||||
|
this._attachq.push( function()
|
||||||
|
{
|
||||||
|
// ensure that we're still attached to the DOM by the time this
|
||||||
|
// continuation is actually invoked
|
||||||
|
if ( !( _self.isAttached() ) )
|
||||||
|
{
|
||||||
|
// tough luck; try again later
|
||||||
|
_self.whenAttached( c );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c();
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public whenDetached': function( c )
|
||||||
|
{
|
||||||
|
// invoke immediately if we're not attached
|
||||||
|
if ( this.isAttached() === false )
|
||||||
|
{
|
||||||
|
c();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue the continuation
|
||||||
|
var _self = this;
|
||||||
|
this._detachc.push( function()
|
||||||
|
{
|
||||||
|
// ensure that we're still detached from the DOM
|
||||||
|
if ( _self.isAttached() )
|
||||||
|
{
|
||||||
|
// tough luck; try again later
|
||||||
|
_self.whenDetached( c );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c();
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual public attach': function( to )
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
// if we are already attached to the DOM, then do nothing (note that we
|
||||||
|
// check the parent element of our content node because something could
|
||||||
|
// have detached the node from the DOM without us knowing)
|
||||||
|
if ( this._content.parentElement )
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to the stored parent if they did not provide anything
|
||||||
|
to = ( to || this._contentParent );
|
||||||
|
if ( !( Class.isA( HTMLElement, to ) ) )
|
||||||
|
{
|
||||||
|
throw TypeError( "Cannot attach context to " + to.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-attach ourselves to our parent and dequeue the continuations only
|
||||||
|
// once our parent is attached (will execute immediately if we are
|
||||||
|
// already attached)
|
||||||
|
to.appendChild( this._content );
|
||||||
|
this._pcontext.whenAttached( function()
|
||||||
|
{
|
||||||
|
_self._dequeue( _self._attachq );
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual public detach': function()
|
||||||
|
{
|
||||||
|
// do nothing if we are not attached to the DOM (note that we check the
|
||||||
|
// parent element of the content because something else could have
|
||||||
|
// re-attached our content node to the DOM without us knowing)
|
||||||
|
if ( !( this._content.parentElement ) )
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the parent so that we know where to re-attach ourselves
|
||||||
|
this._contentParent = this._content.parentElement;
|
||||||
|
|
||||||
|
// detach from the DOM and dequeue the conintinuations (we don't care if
|
||||||
|
// our parent is detached since we're still detached regardless)
|
||||||
|
this._contentParent.removeChild( this._content );
|
||||||
|
this._dequeue( this._detachq );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo: rename me to unqueue; dequeue is a data structure
|
||||||
|
*/
|
||||||
|
'private _dequeue': function( q )
|
||||||
|
{
|
||||||
|
// transfer continuation queue onto the JS timeout stack
|
||||||
|
var c, _self = this;
|
||||||
|
while ( c = q.shift() )
|
||||||
|
{
|
||||||
|
// ensures that the continuations will be executed without locking
|
||||||
|
// up the browser; this is important, since these are DOM
|
||||||
|
// manipulations and therefore may be intensive!
|
||||||
|
setTimeout( c, 25 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Dynamic field context
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
Context = require( './Context' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutable Context
|
||||||
|
*
|
||||||
|
* This exists primarily to ease refactoring of old parts of the framework;
|
||||||
|
* it should not be preferred going forward.
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'DynamicContext' )
|
||||||
|
.implement( Context )
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Current context
|
||||||
|
* @type {Context}
|
||||||
|
*/
|
||||||
|
'private _context': null,
|
||||||
|
|
||||||
|
|
||||||
|
__construct: function( initial )
|
||||||
|
{
|
||||||
|
this.assign( initial );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public assign': function( context )
|
||||||
|
{
|
||||||
|
if ( !( Class.isA( Context, context ) ) )
|
||||||
|
{
|
||||||
|
throw TypeError( "Invalid context" );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._context = context;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public proxy getFieldByName': '_context',
|
||||||
|
|
||||||
|
'public proxy split': '_context'
|
||||||
|
} );
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* DOM context representing document root
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
DomContext = require( './DomContext' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intended to serve as the topmost context in a context tree
|
||||||
|
*
|
||||||
|
* Since all other DomContexts besides this one must have a parent, it may
|
||||||
|
* be useful to create other DomContext objects by split()'ing an instance
|
||||||
|
* of this class.
|
||||||
|
*
|
||||||
|
* The root context cannot be detached from the DOM.
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'RootDomContext' )
|
||||||
|
.extend( DomContext,
|
||||||
|
{
|
||||||
|
'override protected verifyParentContext': function( context )
|
||||||
|
{
|
||||||
|
// we have no parent... :(
|
||||||
|
// (this class has Mommy/Daddy issues)
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override public isAttached': function()
|
||||||
|
{
|
||||||
|
// of course we are.
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override public attach': function( to )
|
||||||
|
{
|
||||||
|
throw Error( "Cannot attach DOM root" );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override public detach': function( to )
|
||||||
|
{
|
||||||
|
throw Error( "Cannot detach DOM root" );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -37,13 +37,19 @@ module.exports = Class( 'DomField' )
|
||||||
|
|
||||||
'private _element': null,
|
'private _element': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to query for element
|
||||||
|
* @type {function(function(HTMLElement))}
|
||||||
|
*/
|
||||||
|
'private _query': null,
|
||||||
|
|
||||||
'private _idPrefix': 'q_',
|
'private _idPrefix': 'q_',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently active styles
|
* Cached immediate parent
|
||||||
* @type {Object}
|
* @type {HTMLElement}
|
||||||
*/
|
*/
|
||||||
'private _styles': {},
|
'private _parent': null,
|
||||||
|
|
||||||
|
|
||||||
__construct: function( field, element )
|
__construct: function( field, element )
|
||||||
|
@ -53,8 +59,8 @@ module.exports = Class( 'DomField' )
|
||||||
throw TypeError( "Invalid field provided" );
|
throw TypeError( "Invalid field provided" );
|
||||||
}
|
}
|
||||||
|
|
||||||
this._field = field;
|
this._field = field;
|
||||||
this._element = element;
|
this._query = element;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,77 +68,117 @@ module.exports = Class( 'DomField' )
|
||||||
'public proxy getIndex': '_field',
|
'public proxy getIndex': '_field',
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to retrieve element associated with field
|
||||||
|
*
|
||||||
|
* CALLBACK will be invoked with the element, if found. The DOM is
|
||||||
|
* always queried in case the element associated with this field
|
||||||
|
* changes, but if the element is not found, then it is assumed to be
|
||||||
|
* detached and the last known element is returned.
|
||||||
|
*
|
||||||
|
* @param {function(HTMLElement)} callback element callback
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
'private _getElement': function( callback )
|
'private _getElement': function( callback )
|
||||||
{
|
{
|
||||||
// if the provided root is a function, then it should be lazily laoded
|
// if the provided root is a function, then it should be lazily laoded
|
||||||
if ( this._element === null )
|
if ( this._query === null )
|
||||||
{
|
{
|
||||||
// if the element is null, then we have some serious problems; do
|
// if the element is null, then we have some serious problems; do
|
||||||
// not even invoke the callback
|
// not even invoke the callback
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if ( typeof this._element === 'function' )
|
|
||||||
|
this.queryElement( callback );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate field element on the DOM, or return last known element
|
||||||
|
*
|
||||||
|
* @todo We used to cache the element in memory, period, but we have no
|
||||||
|
* reliable way to clear it from memory in older versions of
|
||||||
|
* browsers. For browsers that support DOM mutator events, we should
|
||||||
|
* use them.
|
||||||
|
*
|
||||||
|
* @param {function(HTMLElement)} callback element callback
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
'protected queryElement': function( callback )
|
||||||
|
{
|
||||||
|
var _self = this,
|
||||||
|
orig_query = this._query;
|
||||||
|
|
||||||
|
// any further requests for this element should be queued rather
|
||||||
|
// than resulting in a thundering herd toward the DOM (imporant: do
|
||||||
|
// this *before* invoking the function, since it may be synchronous)
|
||||||
|
var queue = [];
|
||||||
|
this._query = function( c )
|
||||||
{
|
{
|
||||||
var _self = this,
|
queue.push( c );
|
||||||
f = this._element;
|
};
|
||||||
|
|
||||||
// any further requests for this element should be queued rather
|
// attempt to retrieve our element from the DOM
|
||||||
// than resulting in a thundering herd toward the DOM (imporant: do
|
orig_query( function( element )
|
||||||
// this *before* invoking the function, since it may be synchronous)
|
{
|
||||||
var queue = [];
|
var new_element = element || _self._element;
|
||||||
this._element = function( c )
|
|
||||||
|
if ( !new_element )
|
||||||
{
|
{
|
||||||
queue.push( c );
|
_self._element = null;
|
||||||
};
|
_self.emit( 'error', Error(
|
||||||
|
"Cannot locate DOM element for field " +
|
||||||
|
_self.getName() + "[" + _self.getIndex() + "]"
|
||||||
|
) );
|
||||||
|
|
||||||
// attempt to retrieve our element from the DOM
|
// do not even finish; this shit is for real.
|
||||||
f( function( element )
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( new_element !== _self._element )
|
||||||
{
|
{
|
||||||
if ( !element )
|
_self.updateElement( new_element );
|
||||||
{
|
}
|
||||||
_self._element = null;
|
|
||||||
_self.emit( 'error', Error(
|
|
||||||
"Cannot locate DOM element for field " +
|
|
||||||
_self.getName() + "[" + _self.getIndex() + "]"
|
|
||||||
) );
|
|
||||||
|
|
||||||
// do not even finish; this shit is for real.
|
// restore original query
|
||||||
return;
|
_self._query = orig_query;
|
||||||
}
|
|
||||||
|
|
||||||
_self._element = element;
|
callback( new_element );
|
||||||
callback( element );
|
|
||||||
|
|
||||||
// if we have any queued requests, process them when we're not
|
// if we have any queued requests, process them when we're not
|
||||||
// busy
|
// busy
|
||||||
var c;
|
var c;
|
||||||
while ( c = queue.shift() )
|
while ( c = queue.shift() )
|
||||||
|
{
|
||||||
|
( function( c )
|
||||||
{
|
{
|
||||||
setTimeout( function()
|
setTimeout( function()
|
||||||
{
|
{
|
||||||
// return the element to the queued callback
|
// return the element to the queued callback
|
||||||
c( element );
|
c( element );
|
||||||
}, 25 );
|
}, 25 );
|
||||||
}
|
} )( c );
|
||||||
} );
|
}
|
||||||
|
} );
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we already have the element; immediately return it
|
|
||||||
callback( this._element );
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
'private _hasStyle': function( style )
|
/**
|
||||||
|
* Update cached element
|
||||||
|
*
|
||||||
|
* The parent of NEW_ELEMENT is cached so that it can be reattached to
|
||||||
|
* the DOM after a detach.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} new_element new field element
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
'protected updateElement': function( new_element )
|
||||||
{
|
{
|
||||||
return !!this._styles[ style.getId() ];
|
this._element = new_element;
|
||||||
},
|
this._parent = new_element.parentElement;
|
||||||
|
|
||||||
|
|
||||||
'private _flagStyle': function( style, flag )
|
|
||||||
{
|
|
||||||
this._styles[ style.getId() ] = !!flag;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,23 +186,19 @@ module.exports = Class( 'DomField' )
|
||||||
{
|
{
|
||||||
var _self = this;
|
var _self = this;
|
||||||
|
|
||||||
// if we already have this style applied, then ignore this request
|
|
||||||
if ( this._hasStyle( style ) )
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all remaining arguments should be passed to the style
|
// all remaining arguments should be passed to the style
|
||||||
var sargs = Array.prototype.slice.call( arguments, 1 );
|
var sargs = Array.prototype.slice.call( arguments, 1 );
|
||||||
|
|
||||||
// flag style immediately to ensure we do not queue multiple application
|
|
||||||
// requests
|
|
||||||
this._flagStyle( style, true );
|
|
||||||
|
|
||||||
// wait for our element to become available on the DOM and perform the
|
// wait for our element to become available on the DOM and perform the
|
||||||
// styling
|
// styling
|
||||||
this._getElement( function( root )
|
this._getElement( function( root )
|
||||||
{
|
{
|
||||||
|
// if we already have this style applied, then ignore this request
|
||||||
|
if ( style.isApplied( _self.__inst, root ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
style.applyStyle.apply(
|
style.applyStyle.apply(
|
||||||
style,
|
style,
|
||||||
[ _self.__inst, root, _self.getContainingRow() ].concat( sargs )
|
[ _self.__inst, root, _self.getContainingRow() ].concat( sargs )
|
||||||
|
@ -171,18 +213,14 @@ module.exports = Class( 'DomField' )
|
||||||
{
|
{
|
||||||
var _self = this;
|
var _self = this;
|
||||||
|
|
||||||
// if this style is not applied, then do nothing
|
|
||||||
if ( !( this._hasStyle( style ) ) )
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// immediately flag style to ensure that we do not queue multiple
|
|
||||||
// revocation requests
|
|
||||||
this._flagStyle( style, false );
|
|
||||||
|
|
||||||
this._getElement( function( root )
|
this._getElement( function( root )
|
||||||
{
|
{
|
||||||
|
// if we already have this style applied, then ignore this request
|
||||||
|
if ( !style.isApplied( _self.__inst, root ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
style.revokeStyle( _self.__inst, root, _self.getContainingRow() );
|
style.revokeStyle( _self.__inst, root, _self.getContainingRow() );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -222,22 +260,46 @@ module.exports = Class( 'DomField' )
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// TODO: move me
|
// TODO: move me; too many odd exceptions; standardize
|
||||||
'protected getContainingRow': function()
|
'protected getContainingRow': function()
|
||||||
{
|
{
|
||||||
var dd = this.getParent( this._element, 'dd' ),
|
var node_name = this._element.nodeName.toUpperCase();
|
||||||
|
|
||||||
|
if ( ( node_name === 'DT' ) || ( node_name === 'DD' ) )
|
||||||
|
{
|
||||||
|
return [ this._element ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var dd = this.getParent( 'dd' ),
|
||||||
dt = ( dd ) ? this.getPrecedingSibling( dd, 'dt' ) : null;
|
dt = ( dd ) ? this.getPrecedingSibling( dd, 'dt' ) : null;
|
||||||
|
|
||||||
return ( dt )
|
return ( dt )
|
||||||
? [ dd, dt ]
|
? [ dd, dt ]
|
||||||
: [ this.getParent( this._element ) ];
|
: [ this.getParent() ];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
'protected getParent': function( element, type )
|
'public getParent': function( type )
|
||||||
|
{
|
||||||
|
return this.getElementParent( this._element, type );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'protected getElementParent': function( element, type )
|
||||||
{
|
{
|
||||||
var parent = element.parentElement;
|
var parent = element.parentElement;
|
||||||
|
|
||||||
|
if ( element === this._element )
|
||||||
|
{
|
||||||
|
parent = parent || this._parent;
|
||||||
|
|
||||||
|
// update parent reference if it's since changed
|
||||||
|
if ( this._parent !== parent )
|
||||||
|
{
|
||||||
|
this._parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( parent === null )
|
if ( parent === null )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
@ -247,14 +309,14 @@ module.exports = Class( 'DomField' )
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeName is in caps
|
// nodeName might not be in caps
|
||||||
if ( type.toUpperCase() === parent.nodeName )
|
if ( type.toUpperCase() === parent.nodeName.toUpperCase() )
|
||||||
{
|
{
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, keep looking
|
// otherwise, keep looking
|
||||||
return this.getParent( parent, type );
|
return this.getElementParent( parent, type );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Creates DomField
|
* Creates DomField
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||||
*
|
*
|
||||||
* This file is part of liza.
|
* This file is part of liza.
|
||||||
*
|
*
|
||||||
|
@ -17,11 +17,6 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
|
||||||
* @needsLove
|
|
||||||
* - Dependencies need to be liberated:
|
|
||||||
* - ElementStyler.
|
|
||||||
* @end needsLove
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
var Class = require( 'easejs' ).Class,
|
||||||
|
@ -85,7 +80,7 @@ module.exports = Class( 'DomFieldFactory',
|
||||||
|
|
||||||
function c()
|
function c()
|
||||||
{
|
{
|
||||||
callback( _self._elementStyler.getElementByName(
|
callback( _self._elementStyler.getElementByNameLax(
|
||||||
name, index, null, root
|
name, index, null, root
|
||||||
)[0] );
|
)[0] );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* General UI logic for groups
|
* General UI logic for groups
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||||
*
|
*
|
||||||
* This file is part of liza.
|
* This file is part of liza.
|
||||||
*
|
*
|
||||||
|
@ -144,23 +144,45 @@ module.exports = Class( 'GroupUi' )
|
||||||
*/
|
*/
|
||||||
'private _rawFieldCount': 0,
|
'private _rawFieldCount': 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOM group context
|
||||||
|
* @type {DomContext}
|
||||||
|
*/
|
||||||
|
'protected context': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styler when fields are no longer applicable
|
||||||
|
* @type {FieldStyler}
|
||||||
|
*/
|
||||||
|
'private _naStyler': null,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes GroupUi
|
* Initializes GroupUi
|
||||||
*
|
*
|
||||||
* @param Group group group to style
|
* @todo three of the below parameters might be able to be removed by
|
||||||
* @param jQuery $content the group content
|
* using context instead; the separate context is transitional
|
||||||
* @param ElementStyler styler styler to use to style elements
|
* (refactoring).
|
||||||
* @param jQuery jquery jQuery-compatible object
|
*
|
||||||
|
* @param {Group} group group to style
|
||||||
|
* @param {jQuery} $content the group content
|
||||||
|
* @param {ElementStyler} styler styler to use to style elements
|
||||||
|
* @param {jQuery} jquery jQuery-compatible object
|
||||||
|
* @param {DomContext} context group context
|
||||||
|
* @param {FieldStyler} na_styler styler for fields that are N/A
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'public __construct': function( group, $content, styler, jquery )
|
'public __construct': function(
|
||||||
|
group, $content, styler, jquery, context, na_styler
|
||||||
|
)
|
||||||
{
|
{
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.$content = $content;
|
this.$content = $content;
|
||||||
this.styler = styler;
|
this.styler = styler;
|
||||||
this._jquery = jquery;
|
this._jquery = jquery;
|
||||||
|
this.context = context;
|
||||||
|
this._naStyler = na_styler;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -787,14 +809,8 @@ module.exports = Class( 'GroupUi' )
|
||||||
|
|
||||||
'virtual protected doHideField': function( field, index )
|
'virtual protected doHideField': function( field, index )
|
||||||
{
|
{
|
||||||
var $elements = this.getFieldElements( field, index );
|
this.context.getFieldByName( field, index )
|
||||||
|
.applyStyle( this._naStyler );
|
||||||
$elements.stop( true, true ).slideUp( 500, function()
|
|
||||||
{
|
|
||||||
// be sure to remove the display:none added by jQuery so that we can
|
|
||||||
// perform our own handling of what it means to be "hidden"
|
|
||||||
$elements.addClass( 'hidden' ).attr( 'style', '' );
|
|
||||||
} );
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -815,12 +831,8 @@ module.exports = Class( 'GroupUi' )
|
||||||
|
|
||||||
'virtual protected doShowField': function( field, index )
|
'virtual protected doShowField': function( field, index )
|
||||||
{
|
{
|
||||||
var $elements = this.getFieldElements( field, index );
|
this.context.getFieldByName( field, index )
|
||||||
|
.revokeStyle( this._naStyler );
|
||||||
$elements.find( '.hidden' ).andSelf()
|
|
||||||
.stop( true, true )
|
|
||||||
.removeClass( 'hidden' )
|
|
||||||
.slideDown( 500 );
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Group tabbed UI
|
* Group tabbed UI
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||||
*
|
*
|
||||||
* This file is part of liza.
|
* This file is part of liza.
|
||||||
*
|
*
|
||||||
|
@ -373,23 +373,11 @@ module.exports = Class( 'TabbedGroupUi' )
|
||||||
{
|
{
|
||||||
_self.doHideField( field, index, true );
|
_self.doHideField( field, index, true );
|
||||||
}, 25 );
|
}, 25 );
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var $elements = this.getFieldElements( field, index );
|
this.__super( field, index );
|
||||||
|
|
||||||
$elements.stop( true, true );
|
|
||||||
|
|
||||||
if ( this.isOnVisibleTab( field, index ) )
|
|
||||||
{
|
|
||||||
$elements.slideUp( 500, function()
|
|
||||||
{
|
|
||||||
$( this ).addClass( 'hidden' );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$elements.hide().addClass( 'hidden' );
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -405,26 +393,11 @@ module.exports = Class( 'TabbedGroupUi' )
|
||||||
{
|
{
|
||||||
_self.doShowField( field, index, true );
|
_self.doShowField( field, index, true );
|
||||||
}, 25 );
|
}, 25 );
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var $elements = this.getFieldElements( field, index );
|
this.__super( field, index );
|
||||||
|
|
||||||
// it's important to stop animations *before* removing the hidden class,
|
|
||||||
// since forcing its completion may add it
|
|
||||||
$elements
|
|
||||||
.stop( true, true )
|
|
||||||
.find( '.hidden' )
|
|
||||||
.andSelf()
|
|
||||||
.removeClass( 'hidden' );
|
|
||||||
|
|
||||||
if ( this.isOnVisibleTab( field, index ) )
|
|
||||||
{
|
|
||||||
$elements.slideDown( 500 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$elements.show();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
* - References to "quote" should be replaced with generic terminology
|
* - References to "quote" should be replaced with generic terminology
|
||||||
* representing a document.
|
* representing a document.
|
||||||
* - Dependencies need to be liberated:
|
* - Dependencies need to be liberated:
|
||||||
* - ElementStyler;
|
|
||||||
* - BucketDataValidator.
|
* - BucketDataValidator.
|
||||||
* - Global references (e.g. jQuery) must be removed.
|
* - Global references (e.g. jQuery) must be removed.
|
||||||
* - jQuery must be eliminated.
|
* - jQuery must be eliminated.
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
* @needsLove
|
* @needsLove
|
||||||
* - Global references to jQuery must be removed.
|
* - Global references to jQuery must be removed.
|
||||||
* - Dependencies need to be liberated:
|
* - Dependencies need to be liberated:
|
||||||
* - ElementStyler;
|
|
||||||
* - UI.
|
* - UI.
|
||||||
* - This may not be needed, may be able to be handled differently, and
|
* - This may not be needed, may be able to be handled differently, and
|
||||||
* really should load from data rather than a pre-generated template (?)
|
* really should load from data rather than a pre-generated template (?)
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* Error condition field styler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
FieldStyler = require( './FieldStyler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style field to indicate an error and displays an error message
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'ErrorFieldStyler' )
|
||||||
|
.extend( FieldStyler,
|
||||||
|
{
|
||||||
|
'public getId': function()
|
||||||
|
{
|
||||||
|
return 'error';
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the field has been styled
|
||||||
|
*
|
||||||
|
* Having this predicate on the styler rather than the field ensures
|
||||||
|
* that, even if the two somehow get out of sync (or styles are applied
|
||||||
|
* elsewhere), application/revocation will function sanely.
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to style
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
*
|
||||||
|
* @return {boolean} whether FIELD has been styled by this styler
|
||||||
|
*/
|
||||||
|
'public isApplied': function( field, element )
|
||||||
|
{
|
||||||
|
return /\binvalid_field\b/.test( element.className );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public applyStyle': function( field, element, row, msg )
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
// style the row containing the element
|
||||||
|
for ( var i in row )
|
||||||
|
{
|
||||||
|
this.addClass( row[ i ], 'invalid' );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: legacy; remove
|
||||||
|
this.addClass( element, 'invalid_field' );
|
||||||
|
|
||||||
|
// display the error message
|
||||||
|
this._createMessage( field.getName(), msg, row[ 0 ], row[ 1 ] );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public revokeStyle': function( field, element, row )
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
// un-style the row containing the element
|
||||||
|
// style the row containing the element
|
||||||
|
for ( var i in row )
|
||||||
|
{
|
||||||
|
this.removeClass( row[ i ], 'invalid' );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: legacy; remove
|
||||||
|
this.removeClass( element, 'invalid_field' );
|
||||||
|
|
||||||
|
this._destroyMessage( row[ 0 ], row[ 1 ] );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _createMessage': function( name, message, dd, dt )
|
||||||
|
{
|
||||||
|
// we can only generate the message if the parent row is available
|
||||||
|
if ( !( dd && dt ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = document.createElement( 'div' );
|
||||||
|
msg.className = 'errmsg';
|
||||||
|
msg.innerHTML = message;
|
||||||
|
|
||||||
|
// append to dd
|
||||||
|
dd.appendChild( msg );
|
||||||
|
|
||||||
|
var height = ( msg.offsetTop + msg.offsetHeight );
|
||||||
|
|
||||||
|
// element does not have height until added to DOM
|
||||||
|
// set a default to ensure it appears to user
|
||||||
|
height = ( height === 0 )
|
||||||
|
? 45 + 'px'
|
||||||
|
: ( height + 10 ) + 'px';
|
||||||
|
|
||||||
|
dd.style.height = height;
|
||||||
|
dt.style.height = height;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _destroyMessage': function( dd, dt )
|
||||||
|
{
|
||||||
|
if ( !dd )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.style.height = '';
|
||||||
|
|
||||||
|
// note that dt may not actually exist (in fact, dd may not even be a
|
||||||
|
// dd; we should rename these variables)
|
||||||
|
dt && ( dt.style.height = '' );
|
||||||
|
|
||||||
|
var node;
|
||||||
|
|
||||||
|
// search for the message node, starting with the last element (since
|
||||||
|
// the error message was appended, we're likely to find it on our first
|
||||||
|
// try)
|
||||||
|
for ( node = dd.lastChild;
|
||||||
|
node && node.className !== 'errmsg';
|
||||||
|
node = node.previousSibling
|
||||||
|
);
|
||||||
|
|
||||||
|
// if we found it, then remove it
|
||||||
|
node && dd.removeChild( node );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* Error condition field styler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
Styler = require( './Styler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle error generation and defer styling to supertype
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'ErrorStyler' )
|
||||||
|
.implement( Styler )
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Hash of error messages by field name
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
'private _msgs': {},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize error styler with a hash of error messages by field name
|
||||||
|
*
|
||||||
|
* @param {Object} msgs hash of error messages by field name
|
||||||
|
*/
|
||||||
|
'virtual __construct': function( msgs )
|
||||||
|
{
|
||||||
|
this._msgs = msgs;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public getHooks': function( uistyler )
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
fieldError: function( context, failures, msgs )
|
||||||
|
{
|
||||||
|
msgs = msgs || {};
|
||||||
|
|
||||||
|
for ( var name in failures )
|
||||||
|
{
|
||||||
|
var msgset = ( msgs[ name ] || [] );
|
||||||
|
|
||||||
|
for ( var index in failures[ name ] )
|
||||||
|
{
|
||||||
|
// if no error message was provided, fall back to one of
|
||||||
|
// the defaults
|
||||||
|
var msg = (
|
||||||
|
msgset[ index ]
|
||||||
|
|| _self._msgs[ name ]
|
||||||
|
|| "Field is invalid"
|
||||||
|
);
|
||||||
|
|
||||||
|
_self.onFieldError(
|
||||||
|
context.getFieldByName( name, index ),
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldFixed: function( context, fixed )
|
||||||
|
{
|
||||||
|
for ( var name in fixed )
|
||||||
|
{
|
||||||
|
for ( var index in fixed[ name ] )
|
||||||
|
{
|
||||||
|
_self.onFieldFixed(
|
||||||
|
context.getFieldByName( name, index )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual protected onFieldError': function( field, msg )
|
||||||
|
{
|
||||||
|
// do nothing by default
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual protected onFieldFixed': function( field )
|
||||||
|
{
|
||||||
|
// do nothing by default
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,152 @@
|
||||||
|
/**
|
||||||
|
* Style fields using CSS
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var AbstractClass = require( 'easejs' ).AbstractClass;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style DOM fields
|
||||||
|
*
|
||||||
|
* @todo perhaps this should be called DomFieldStyler
|
||||||
|
*/
|
||||||
|
module.exports = AbstractClass( 'FieldStyler',
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve unique identifier
|
||||||
|
*
|
||||||
|
* @return {string} unique identifier
|
||||||
|
*/
|
||||||
|
'abstract public getId': [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the field has been styled
|
||||||
|
*
|
||||||
|
* Having this predicate on the styler rather than the field ensures
|
||||||
|
* that, even if the two somehow get out of sync (or styles are applied
|
||||||
|
* elsewhere), application/revocation will function sanely.
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to style
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
*
|
||||||
|
* @return {boolean} whether FIELD has been styled by this styler
|
||||||
|
*/
|
||||||
|
'abstract public isApplied': [ 'field', 'element' ],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply style to field
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to style
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
* @param {Array.<HTMLElement>} row DOM elements of containing row
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'abstract public applyStyle': [ 'field', 'element', 'row' ],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove style from field
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to unstyle
|
||||||
|
* @param {HTMLElement} element DOM element to unstyle
|
||||||
|
* @param {Array.<HTMLElement>} row DOM elements of containing row
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'abstract public revokeStyle': [ 'field', 'element', 'row' ],
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add CSS class CLS to element ELEMENT
|
||||||
|
*
|
||||||
|
* This method is needed until support is dropped for browsers that do
|
||||||
|
* not support classList.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
* @param {string} cls class name
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'protected addClass': function( element, cls )
|
||||||
|
{
|
||||||
|
if ( !element )
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are given an array, then recurse
|
||||||
|
if ( Array.isArray( element ) )
|
||||||
|
{
|
||||||
|
for ( var i in element )
|
||||||
|
{
|
||||||
|
this.addClass( element[ i ], cls );
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( typeof element.className === 'string' )
|
||||||
|
{
|
||||||
|
element.className += ' ' + cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add CSS class CLS to element ELEMENT
|
||||||
|
*
|
||||||
|
* This method is needed until support is dropped for browsers that do
|
||||||
|
* not support classList.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
* @param {string} cls class name
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'protected removeClass': function( element, cls )
|
||||||
|
{
|
||||||
|
if ( !element )
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are given an array, then recurse
|
||||||
|
if ( Array.isArray( element ) )
|
||||||
|
{
|
||||||
|
for ( var i in element )
|
||||||
|
{
|
||||||
|
this.removeClass( element[ i ], cls );
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( typeof element.className === 'string' )
|
||||||
|
{
|
||||||
|
// note that we use a space instead of a boundary for the character
|
||||||
|
// preceding the match due to the implementation of addClass()
|
||||||
|
element.className = element.className.replace(
|
||||||
|
new RegExp( ( ' ' + cls + '\\b' ), 'g' ), ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* N/A field styler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
FieldStyler = require( './FieldStyler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style fields that are not applicable (and so do not need to collect data
|
||||||
|
* from the user)
|
||||||
|
*
|
||||||
|
* @todo Detaching should be done by DomField
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'NaFieldStyler' )
|
||||||
|
.extend( FieldStyler,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve unique identifier
|
||||||
|
*
|
||||||
|
* @return {string} unique identifier
|
||||||
|
*/
|
||||||
|
'public getId': function()
|
||||||
|
{
|
||||||
|
return 'na';
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the field has been styled
|
||||||
|
*
|
||||||
|
* Having this predicate on the styler rather than the field ensures
|
||||||
|
* that, even if the two somehow get out of sync (or styles are applied
|
||||||
|
* elsewhere), application/revocation will function sanely.
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to style
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
*
|
||||||
|
* @return {boolean} whether FIELD has been styled by this styler
|
||||||
|
*/
|
||||||
|
'public isApplied': function( field, element )
|
||||||
|
{
|
||||||
|
return /\bhidden\b/.test( element.className );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply style to field
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to style
|
||||||
|
* @param {HTMLElement} element DOM element to style
|
||||||
|
* @param {Array.<HTMLElement>} row DOM elements of containing row
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'public applyStyle': function( field, element, row )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( this.isSubField( field ) )
|
||||||
|
{
|
||||||
|
this.hideField( element, [] );
|
||||||
|
field.getParent().removeChild( element );
|
||||||
|
|
||||||
|
// this is a child of another field; don't consider it a
|
||||||
|
// containing row, since we don't want our operations affecting
|
||||||
|
// it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hideField( element, row );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove style from field
|
||||||
|
*
|
||||||
|
* @param {DomField} field field to unstyle
|
||||||
|
* @param {HTMLElement} element DOM element to unstyle
|
||||||
|
* @param {Array.<HTMLElement>} row DOM elements of containing row
|
||||||
|
*
|
||||||
|
* @return {FieldStyler} self
|
||||||
|
*/
|
||||||
|
'public revokeStyle': function( field, element, row )
|
||||||
|
{
|
||||||
|
if ( this.isSubField( field ) )
|
||||||
|
{
|
||||||
|
this.showField( element, [] );
|
||||||
|
field.getParent().appendChild( element );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showField( element, row );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether element ELEMENT represents a sub-field
|
||||||
|
*
|
||||||
|
* A sub-field is a field within a field; the distinction is important
|
||||||
|
* because we probably don't want operations on a sub-field affecting
|
||||||
|
* its parent.
|
||||||
|
*
|
||||||
|
* @todo: move somewhere else (Field perhaps?)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element DOM element associated with field
|
||||||
|
*
|
||||||
|
* @return {boolean} whether ELEMENT represents a sub-field
|
||||||
|
*/
|
||||||
|
'protected isSubField': function( field )
|
||||||
|
{
|
||||||
|
var parent = field.getParent();
|
||||||
|
|
||||||
|
// ES3-compatible (don't use classList)
|
||||||
|
return !!( parent && /\bwidget\b/.test( parent.className ) );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual protected hideField': function( element, row )
|
||||||
|
{
|
||||||
|
this.addClass( element, 'hidden' );
|
||||||
|
|
||||||
|
// this is a workaround from the old days where jQuery would add
|
||||||
|
// styles to hide elements, which we wanted to override; this can be
|
||||||
|
// removed once jQuery is eradicated from the framework
|
||||||
|
element.style = '';
|
||||||
|
|
||||||
|
for ( var i in row )
|
||||||
|
{
|
||||||
|
this.addClass( row[ i ], 'hidden' );
|
||||||
|
row[ i ].style = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'virtual protected showField': function( element, row )
|
||||||
|
{
|
||||||
|
this.removeClass( element, 'hidden' );
|
||||||
|
this.removeClass( row, 'hidden' );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* Animated N/A field styler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Trait = require( 'easejs' ).Trait,
|
||||||
|
NaFieldStyler = require( './NaFieldStyler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sliding animations for field show/hide
|
||||||
|
*
|
||||||
|
* @todo Use CSS3 once we can drop support for IE<10
|
||||||
|
*/
|
||||||
|
module.exports = Trait.extend( NaFieldStyler,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* jQuery instance
|
||||||
|
* @type {jQuery}
|
||||||
|
*/
|
||||||
|
'private _jquery': null,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare mixin with jQuery instance
|
||||||
|
*
|
||||||
|
* @param {jQuery} jquery jQuery instance
|
||||||
|
*/
|
||||||
|
__mixin: function( jquery )
|
||||||
|
{
|
||||||
|
this._jquery = jquery;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate field display
|
||||||
|
*
|
||||||
|
* When a field becomes applicable, progressively increase its height
|
||||||
|
* ("slide down").
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element field DOM element
|
||||||
|
* @param {Array.<HTMLElement} row parent row elements
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
'override protected showField': function( element, row )
|
||||||
|
{
|
||||||
|
var $row = this._jquery( row );
|
||||||
|
|
||||||
|
$row.stop( true, true );
|
||||||
|
this.__super( element, row );
|
||||||
|
$row
|
||||||
|
.hide()
|
||||||
|
.slideDown( 500 );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate field hiding
|
||||||
|
*
|
||||||
|
* When a field becomes non-applicable, progressively decrease its
|
||||||
|
* height ("slide up").
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element field DOM element
|
||||||
|
* @param {Array.<HTMLElement} row parent row elements
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
'override protected hideField': function( element, row )
|
||||||
|
{
|
||||||
|
var _self = this,
|
||||||
|
all = [ element ].concat( row ),
|
||||||
|
$elements = this._jquery( all );
|
||||||
|
|
||||||
|
$elements.stop( true, true )
|
||||||
|
.slideUp( 500, function()
|
||||||
|
{
|
||||||
|
_self.hideField['super'].call( _self, element, row );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Style errors in sidebar
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
ErrorStyler = require( './ErrorStyler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays errors in the sidebar
|
||||||
|
*
|
||||||
|
* TODO: This is an adapter around the old system; it could use some
|
||||||
|
* refactoring.
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'SidebarErrorStyler' )
|
||||||
|
.extend( ErrorStyler,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Error box in which to display errors
|
||||||
|
* @type {FormErrorBox}
|
||||||
|
*/
|
||||||
|
'private _errbox': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ui instance
|
||||||
|
* @type {Ui}
|
||||||
|
*/
|
||||||
|
'private _ui': null,
|
||||||
|
|
||||||
|
|
||||||
|
'override __construct': function( msgs, error_box, ui )
|
||||||
|
{
|
||||||
|
this._errbox = error_box;
|
||||||
|
this._ui = ui;
|
||||||
|
this.__super( msgs );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override protected onFieldError': function( field, msg )
|
||||||
|
{
|
||||||
|
this._errbox.show( field.getName(), field.getIndex(), msg );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override protected onFieldFixed': function( field )
|
||||||
|
{
|
||||||
|
this._errbox.removeError( field.getName(), field.getIndex() );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Styles errors on steps
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
ErrorStyler = require( './ErrorStyler' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger field styling for errors on the parent step itself
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'StepErrorStyler' )
|
||||||
|
.extend( ErrorStyler,
|
||||||
|
{
|
||||||
|
'private _style': null,
|
||||||
|
|
||||||
|
|
||||||
|
'override __construct': function( msgs, field_style )
|
||||||
|
{
|
||||||
|
this._style = field_style;
|
||||||
|
this.__super( msgs );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override protected onFieldError': function( field, msg )
|
||||||
|
{
|
||||||
|
field.applyStyle( this._style, msg );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'override protected onFieldFixed': function( field )
|
||||||
|
{
|
||||||
|
field.revokeStyle( this._style );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Styler interface
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Can be used for type hinting, but we need an actual API!
|
||||||
|
*/
|
||||||
|
module.exports = Interface( 'Styler',
|
||||||
|
{
|
||||||
|
} );
|
|
@ -0,0 +1,322 @@
|
||||||
|
/**
|
||||||
|
* Test case for NaFieldStyler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var styler = require( '../../../' ).ui.styler,
|
||||||
|
expect = require( 'chai' ).expect,
|
||||||
|
Class = require( 'easejs' ).Class,
|
||||||
|
Sut = styler.NaFieldStyler;
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'ui.styler.NaFieldStyler', function()
|
||||||
|
{
|
||||||
|
function testApplyHidden()
|
||||||
|
{
|
||||||
|
var element = { className: '' },
|
||||||
|
r1 = { className: '' },
|
||||||
|
r2 = { className: '' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().applyStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
[ element, r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.className ).to.match( /\bhidden\b/ );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function testApplyClear()
|
||||||
|
{
|
||||||
|
var element = { style: 'foo' },
|
||||||
|
r1 = { style: 'foo' },
|
||||||
|
r2 = { style: 'foo' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().applyStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
[ element, r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.style ).to.equal( '' );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function testRevokeHidden()
|
||||||
|
{
|
||||||
|
var element = { className: 'foo hidden' },
|
||||||
|
r1 = { className: 'foo hidden' },
|
||||||
|
r2 = { className: 'foo hidden' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().revokeStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
[ element, r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.className ).to.not.match( /\bhidden\b/ );
|
||||||
|
expect( ele.className ).to.match( /foo/ );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function testRevokeStyle()
|
||||||
|
{
|
||||||
|
var element = { style: 'foo' },
|
||||||
|
r1 = { style: 'foo' },
|
||||||
|
r2 = { style: 'foo' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().revokeStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
[ element, r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.style ).to.equal( 'foo' );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#getId', function()
|
||||||
|
{
|
||||||
|
it( 'returns unique identifier', function()
|
||||||
|
{
|
||||||
|
expect( Sut().getId() ).to.equal( 'na' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#applyStyle', function()
|
||||||
|
{
|
||||||
|
it( 'sets hidden class on all elements', testApplyHidden );
|
||||||
|
it( 'clears style on all elements', testApplyClear );
|
||||||
|
|
||||||
|
|
||||||
|
it( 'does not set class on subfield parents', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: '',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
removeChild: function() {},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var r1 = { className: '' },
|
||||||
|
r2 = { className: '' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().applyStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
expect( element.className ).to.match( /\bhidden\b/ );
|
||||||
|
|
||||||
|
[ r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.className ).to.equal( '' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
it( 'does not clears style subfield parents', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
style: 'foo',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
removeChild: function() {},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var r1 = { style: 'foo' },
|
||||||
|
r2 = { style: 'foo' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().applyStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
expect( element.style ).to.equal( '' );
|
||||||
|
|
||||||
|
[ r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.style ).to.equal( 'foo' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
// f@#(& IE
|
||||||
|
it( 'removes subfield from DOM', function( done )
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
style: '',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
removeChild: function( ele )
|
||||||
|
{
|
||||||
|
expect( ele ).to.equal( element );
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sut().applyStyle( getStubField( element ), element, [] );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#revokeStyle', function()
|
||||||
|
{
|
||||||
|
it( 'removes hidden class on all elements', testRevokeHidden );
|
||||||
|
it( 'does not clear style on all elements', testRevokeStyle );
|
||||||
|
|
||||||
|
|
||||||
|
it( 'does not remove hidden class on subfield parents', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: 'foo hidden',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
appendChild: function() {},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var r1 = { className: 'foo hidden' },
|
||||||
|
r2 = { className: 'foo hidden' },
|
||||||
|
row = [ r1, r2 ];
|
||||||
|
|
||||||
|
Sut().revokeStyle( getStubField( element ), element, row );
|
||||||
|
|
||||||
|
expect( element.className ).to.not.match( /\bhidden\b/ );
|
||||||
|
expect( element.className ).to.match( /foo/ );
|
||||||
|
|
||||||
|
[ r1, r2 ].forEach( function( ele )
|
||||||
|
{
|
||||||
|
expect( ele.className ).to.equal( 'foo hidden' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
// we eventually need to care about where it's re-attached
|
||||||
|
it( 're-attaches subfield to DOM', function( done )
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: '',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
appendChild: function( ele )
|
||||||
|
{
|
||||||
|
expect( ele ).to.equal( element );
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sut().revokeStyle( getStubField( element ), element, [] );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#isApplied', function()
|
||||||
|
{
|
||||||
|
it( 'recognizes when applied', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = Sut(),
|
||||||
|
field = getStubField( element );
|
||||||
|
|
||||||
|
sut.applyStyle( field, element, [] );
|
||||||
|
|
||||||
|
expect( sut.isApplied( field, element ) )
|
||||||
|
.to.be.true;
|
||||||
|
|
||||||
|
sut.revokeStyle( field, element, [] );
|
||||||
|
|
||||||
|
expect( sut.isApplied( field, element ) )
|
||||||
|
.to.be.false;
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'protected API', function()
|
||||||
|
{
|
||||||
|
describe( '#isSubField', function()
|
||||||
|
{
|
||||||
|
it( 'recognizes parent widget class as subfield', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: '',
|
||||||
|
parentElement: {
|
||||||
|
className: 'widget',
|
||||||
|
removeChild: function() {},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect( protSut().protIsSubField( getStubField( element ) ) )
|
||||||
|
.to.be.true;
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
it( 'missing parent widget class is non-subfield', function()
|
||||||
|
{
|
||||||
|
var element = {
|
||||||
|
className: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect( protSut().protIsSubField( getStubField( element ) ) )
|
||||||
|
.to.be.false;
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#hideField', function()
|
||||||
|
{
|
||||||
|
it( 'sets hidden class on all elements', testApplyHidden );
|
||||||
|
it( 'clears style on all elements', testApplyClear );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
describe( '#showField', function()
|
||||||
|
{
|
||||||
|
it( 'removes hidden class on all elements', testRevokeHidden );
|
||||||
|
it( 'does not clear style on all elements', testRevokeStyle );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
function getStubField( element )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
getParent: function()
|
||||||
|
{
|
||||||
|
return element.parentElement;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function protSut()
|
||||||
|
{
|
||||||
|
return Class.extend( Sut, {
|
||||||
|
protIsSubField: function( element )
|
||||||
|
{
|
||||||
|
return this.isSubField( element );
|
||||||
|
}
|
||||||
|
} )();
|
||||||
|
}
|
Loading…
Reference in New Issue